💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 5.2 配置时运行自定义命令 **NOTE**:*此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-05/recipe-02 中找到。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。* 运行CMake生成构建系统,从而指定原生构建工具必须执行哪些命令,以及按照什么顺序执行。我们已经了解了CMake如何在配置时运行许多子任务,以便找到工作的编译器和必要的依赖项。本示例中,我们将讨论如何使用`execute_process`命令在配置时运行定制化命令。 ## 具体实施 第3章第3节中,我们已经展示了`execute_process`查找Python模块NumPy时的用法。本例中,我们将使用`execute_process`命令来确定,是否存在特定的Python模块(本例中为Python CFFI),如果存在,我们在进行版本确定: 1. 对于这个简单的例子,不需要语言支持: ```cmake cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(recipe-02 LANGUAGES NONE) ``` 2. 我们要求Python解释器执行一个简短的代码片段,因此,需要使用`find_package`来查找解释器: ```cmake find_package(PythonInterp REQUIRED) ``` 3. 然后,调用`execute_process`来运行一个简短的Python代码段;下一节中,我们将更详细地讨论这个命令: ```cmake # this is set as variable to prepare # for abstraction using loops or functions set(_module_name "cffi") execute_process( COMMAND ${PYTHON_EXECUTABLE} "-c" "import ${_module_name}; print(${_module_name}.__version__)" OUTPUT_VARIABLE _stdout ERROR_VARIABLE _stderr OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ) ``` 4. 然后,打印结果: ```cmake if(_stderr MATCHES "ModuleNotFoundError") message(STATUS "Module ${_module_name} not found") else() message(STATUS "Found module ${_module_name} v${_stdout}") endif() ``` 5. 下面是一个配置示例(假设Python CFFI包安装在相应的Python环境中): ```shell $ mkdir -p build $ cd build $ cmake .. -- Found PythonInterp: /home/user/cmake-cookbook/chapter-05/recipe-02/example/venv/bin/python (found version "3.6.5") -- Found module cffi v1.11.5 ``` ## 工作原理 `execute_process`命令将从当前正在执行的CMake进程中派生一个或多个子进程,从而提供了在配置项目时运行任意命令的方法。可以在一次调用`execute_process`时执行多个命令。但请注意,每个命令的输出将通过管道传输到下一个命令中。该命令接受多个参数: * WORKING_DIRECTORY,指定应该在哪个目录中执行命令。 * RESULT_VARIABLE将包含进程运行的结果。这要么是一个整数,表示执行成功,要么是一个带有错误条件的字符串。 * OUTPUT_VARIABLE和ERROR_VARIABLE将包含执行命令的标准输出和标准错误。由于命令的输出是通过管道传输的,因此只有最后一个命令的标准输出才会保存到OUTPUT_VARIABLE中。 * INPUT_FILE指定标准输入重定向的文件名 * OUTPUT_FILE指定标准输出重定向的文件名 * ERROR_FILE指定标准错误输出重定向的文件名 * 设置OUTPUT_QUIET和ERROR_QUIET后,CMake将静默地忽略标准输出和标准错误。 * 设置OUTPUT_STRIP_TRAILING_WHITESPACE,可以删除运行命令的标准输出中的任何尾随空格 * 设置ERROR_STRIP_TRAILING_WHITESPACE,可以删除运行命令的错误输出中的任何尾随空格。 有了这些了解这些参数,回到我们的例子当中: ```cmake set(_module_name "cffi") execute_process( COMMAND ${PYTHON_EXECUTABLE} "-c" "import ${_module_name}; print(${_module_name}.__version__)" OUTPUT_VARIABLE _stdout ERROR_VARIABLE _stderr OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ) if(_stderr MATCHES "ModuleNotFoundError") message(STATUS "Module ${_module_name} not found") else() message(STATUS "Found module ${_module_name} v${_stdout}") endif() ``` 该命令检查`python -c "import cffi; print(cffi.__version__)"`的输出。如果没有找到模块,`_stderr`将包含`ModuleNotFoundError`,我们将在if语句中对其进行检查。本例中,我们将打印`Module cffi not found`。如果导入成功,Python代码将打印模块的版本,该模块通过管道输入`_stdout`,这样就可以打印如下内容: ```cmake message(STATUS "Found module ${_module_name} v${_stdout}") ``` ## 更多信息 本例中,只打印了结果,但实际项目中,可以警告、中止配置,或者设置可以查询的变量,来切换某些配置选项。 代码示例会扩展到多个Python模块(如Cython),以避免代码重复。一种选择是使用`foreach`循环模块名,另一种方法是将代码封装为函数或宏。我们将在第7章中讨论这些封装。 第9章中,我们将使用Python CFFI和Cython。现在的示例,可以作为有用的、可重用的代码片段,来检测这些包是否存在。