当前位置: 首页 > news >正文

CMake execute_process用法详解

execute_process 是 CMake 中的一个命令,用于在 CMake 配置阶段(即运行 cmake 命令时)执行外部进程。它与 add_custom_commandadd_custom_target 不同,后者是在构建阶段(如 makeninja)执行命令。execute_process 通常用于获取系统信息、生成代码或处理依赖项。


基本语法

execute_process(COMMAND <cmd1> [<args1>][COMMAND <cmd2> [<args2>] ...][WORKING_DIRECTORY <dir>][TIMEOUT <seconds>][RESULT_VARIABLE <var>][OUTPUT_VARIABLE <var>][ERROR_VARIABLE <var>][INPUT_FILE <file>][OUTPUT_FILE <file>][ERROR_FILE <file>][OUTPUT_QUIET][ERROR_QUIET][COMMAND_ECHO <where>][ENCODING <encoding>][ECHO_OUTPUT_VARIABLE][ECHO_ERROR_VARIABLE][OUTPUT_STRIP_TRAILING_WHITESPACE][ERROR_STRIP_TRAILING_WHITESPACE]
)

参数详解

1. COMMAND (必需)

指定要执行的命令及其参数。可以链式调用多个命令,按顺序执行(类似 Shell 的管道 |)。

execute_process(COMMAND echo "Hello"COMMAND sed "s/Hello/Hi/"
)
# 输出 "Hi"
2. WORKING_DIRECTORY

设置命令执行的工作目录。

execute_process(COMMAND pwdWORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/subdir
)
3. TIMEOUT

设置超时时间(秒),超时后终止进程。

execute_process(COMMAND sleep 10TIMEOUT 5  # 5 秒后终止
)
4. RESULT_VARIABLE

保存命令执行的返回值(退出码)。通常 0 表示成功,非 0 表示错误。

execute_process(COMMAND git rev-parse --verify HEADRESULT_VARIABLE git_result
)
if(NOT git_result EQUAL 0)message(FATAL_ERROR "Git failed!")
endif()
5. OUTPUT_VARIABLEERROR_VARIABLE

捕获命令的标准输出(stdout)和标准错误(stderr)。

execute_process(COMMAND ls non_existent_fileOUTPUT_VARIABLE outERROR_VARIABLE err
)
message("Output: ${out}")  # 空
message("Error: ${err}")   # 显示错误信息
6. OUTPUT_STRIP_TRAILING_WHITESPACE

删除输出末尾的空白字符(如换行符)。

execute_process(COMMAND echo "Hello World   "OUTPUT_VARIABLE outOUTPUT_STRIP_TRAILING_WHITESPACE
)
message("Output: [${out}]")  # 输出 "Hello World"
7. INPUT_FILE, OUTPUT_FILE, ERROR_FILE

将输入、输出、错误重定向到文件。

execute_process(COMMAND my_toolINPUT_FILE input.txtOUTPUT_FILE output.txtERROR_FILE error.log
)

常见用法示例

1. 执行命令并获取输出
execute_process(COMMAND uname -sOUTPUT_VARIABLE OS_NAMEOUTPUT_STRIP_TRAILING_WHITESPACE
)
message("OS: ${OS_NAME}")  # 如 "Linux", "Darwin"
2. 检查命令是否成功
execute_process(COMMAND which python3RESULT_VARIABLE resultOUTPUT_QUIET
)
if(result EQUAL 0)message("Python3 found")
else()message(FATAL_ERROR "Python3 not found")
endif()
3. 链式命令(管道)
execute_process(COMMAND cat ${CMAKE_SOURCE_DIR}/data.txtCOMMAND grep "important"COMMAND wc -lOUTPUT_VARIABLE line_count
)
4. 处理错误输出
execute_process(COMMAND gcc --versionERROR_VARIABLE gcc_errorOUTPUT_VARIABLE gcc_output
)
if(gcc_error)message("Error: ${gcc_error}")
else()message("Output: ${gcc_output}")
endif()

注意事项

  1. 执行时机execute_process 在 CMake 配置阶段立即执行,若命令耗时较长,可能拖慢配置过程。
  2. 跨平台兼容性:命令在不同平台下可能需要调整(如 cmd.exe vs bash)。
  3. 输出处理:若输出内容较大,建议重定向到文件而非变量。
  4. 错误处理:始终检查 RESULT_VARIABLE 以确保命令成功。

add_custom_command 的区别

  • execute_process: 在配置阶段运行,直接影响 CMake 变量和后续流程。
  • add_custom_command: 在构建阶段运行,生成文件或触发其他构建步骤。

根据需求选择合适的命令!


你提到的执行时机问题非常重要,也是很多 CMake 初学者容易混淆的地方。我们通过一个具体的例子和对比来彻底理解 execute_process 的执行时机。


CMake 的两个阶段

CMake 的工作流程分为两个阶段:

  1. 配置阶段 (Configure Time)

    • 运行 cmake 命令时发生(如 cmake -B build
    • 此时会执行所有 execute_process 命令
    • 生成构建系统文件(如 Makefilebuild.ninja
  2. 构建阶段 (Build Time)

    • 运行构建命令时发生(如 makeninja
    • 此时会执行 add_custom_commandadd_custom_target 中定义的命令

例子对比

假设我们有两个 CMake 脚本:

案例 1:使用 execute_process(配置阶段执行)
# CMakeLists.txt
message("=== 配置阶段开始 ===")execute_process(COMMAND echo "正在生成 version.h (配置阶段)"COMMAND bash -c "echo '#define VERSION \"1.0.0\"' > version.h"
)add_executable(my_app main.cpp version.h)
message("=== 配置阶段结束 ===")

运行结果:

$ cmake -B build
=== 配置阶段开始 ===
正在生成 version.h (配置阶段)  # 立即执行!
=== 配置阶段结束 ===
Generating done.$ ls build/version.h  # 文件已生成
version.h$ make                # 构建阶段不会重新生成 version.h
[100%] Built target my_app
案例 2:使用 add_custom_command(构建阶段执行)
# CMakeLists.txt
message("=== 配置阶段开始 ===")add_custom_command(OUTPUT version.hCOMMAND echo "正在生成 version.h (构建阶段)"COMMAND bash -c "echo '#define VERSION \"1.0.0\"' > version.h"
)add_executable(my_app main.cpp version.h)
message("=== 配置阶段结束 ===")

运行结果:

$ cmake -B build
=== 配置阶段开始 ===
=== 配置阶段结束 ===  # 此时没有生成 version.h
Generating done.$ ls build/version.h  # 文件不存在
ls: cannot access 'build/version.h': No such file or directory$ make                # 构建阶段生成 version.h
[ 50%] Generating version.h
正在生成 version.h (构建阶段)
[100%] Built target my_app

关键差异总结

特性execute_processadd_custom_command
执行时机配置阶段 (cmake 运行时)构建阶段 (makeninja 运行时)
执行次数每次运行 cmake 时执行一次每次构建时执行(如果输出需要更新)
典型用途获取系统信息、预生成配置文件编译时生成代码、处理构建依赖
能否影响构建规则不能直接定义构建系统的依赖关系可以定义构建依赖关系

什么时候用 execute_process

  1. 需要立即获取信息
    例如:检测编译器特性、查询 Git 提交哈希、检查系统库是否存在。

    # 在配置阶段获取 Git 提交 ID
    execute_process(COMMAND git log -1 --format=%hOUTPUT_VARIABLE GIT_COMMIT_HASHOUTPUT_STRIP_TRAILING_WHITESPACE
    )
    configure_file(config.h.in config.h)  # 将 GIT_COMMIT_HASH 写入头文件
    
  2. 生成构建所需的初始文件
    例如:生成默认配置文件(如果文件不存在)。

    if(NOT EXISTS "${CMAKE_SOURCE_DIR}/config.json")execute_process(COMMAND cp "${CMAKE_SOURCE_DIR}/config.default.json" "${CMAKE_SOURCE_DIR}/config.json")
    endif()
    

什么时候用 add_custom_command

  1. 需要在构建时生成文件
    例如:用 Protobuf 生成代码、在编译前预处理资源文件。

    add_custom_command(OUTPUT generated_code.cppCOMMAND protoc --cpp_out=. my.protoDEPENDS my.proto
    )
    add_executable(app main.cpp generated_code.cpp)
    
  2. 构建时需要动态更新内容
    例如:每次构建时更新版本时间戳。

    add_custom_command(OUTPUT timestamp.hCOMMAND bash -c "date +'#define TIMESTAMP \"%Y-%m-%d %H:%M:%S\"' > timestamp.h"
    )
    add_executable(app main.cpp timestamp.h)
    

易错场景分析

错误:期望 execute_process 在每次构建时运行
# ❌ 错误:这个文件只会在运行 cmake 时生成一次
execute_process(COMMAND echo "#define BUILD_COUNT 1" > build_count.h
)# ✅ 正确:使用 add_custom_command 在每次构建时更新
add_custom_command(OUTPUT build_count.hCOMMAND bash -c "echo '#define BUILD_COUNT $(($(cat build_count.h 2>/dev/null | cut -d' ' -f3) + 1))' > build_count.h"
)
错误:在 execute_process 中生成未跟踪的文件
execute_process(COMMAND touch some_file.txt  # 生成的文件不会被 CMake 自动跟踪
)# ✅ 正确:显式声明生成的文件
add_custom_command(OUTPUT some_file.txtCOMMAND touch some_file.txt
)
add_executable(app main.cpp some_file.txt)  # 建立依赖关系

总结

  • execute_process配置阶段的“一次性操作”,适合做初始化工作。
  • 如果你需要 在构建时动态生成内容定义构建依赖关系,应该使用 add_custom_command
  • 可以通过以下命令直观观察执行时机:
    # 查看配置阶段的输出
    cmake -B build# 查看构建阶段的输出
    cmake --build build --verbose
    

相关文章:

  • HyperDefect-YOLO:基于超图计算的工业缺陷检测算法解析
  • cdq 系列 题解
  • arkTs:使用Refresh实现下拉刷新功能(含状态提示与动画控制)
  • 并发设计模式之双缓冲系统
  • 基于SpringBoot的心情疗愈平台-项目分享
  • oracle rac时区问题导致远程查询时间不准
  • 报告系统状态的连续日期 mysql + pandas(连续值判断)
  • 【MySQL】数据库安装
  • Python Cookbook-6.6 在代理中托管特殊方法
  • Flowith AI,解锁下一代「知识交易市场」
  • C语言 函数(上)
  • CAD文件如何导入BigemapPro
  • Java:多线程
  • 极刻AI搜v1.0 问一次问题 AI工具一起答
  • Kubernetes相关的名词解释Container(16)
  • Linux:进程:进程控制
  • AI软件栈:LLVM分析(六)
  • Shell脚本-变量的分类
  • 计算机组成与体系结构:内存接口(Memory Interface)
  • Linux学习笔记|入门指令
  • 大幅加仓美的、茅台,买入小米,银华基金李晓星:看好港股与A股消费股
  • 被指违反代理协议遭南航暂停售票资格,去哪儿网:今起恢复
  • 一中国公民在日本滑雪场意外死亡,我领馆发布提醒
  • 我国成功发射试验二十七号卫星01星~06星
  • 稳健开局!今年粮食产量瞄准1.4万亿斤
  • 奥利弗·沙赫特博士:集群是产业集聚地,更是“超级连接器”