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

CMake指令:add_custom_command和add_custom_target详解

目录

1.add_custom_command

1.1.简介

1.2.常见使用场景

1.3.注意事项

2.add_custom_target

2.1.简介

2.2.常见使用场景

2.3.注意事项

3.add_custom_command和add_custom_target区别

3.1. 区别

3.2.示例说明

4.总结

相关链接


1.add_custom_command

1.1.简介

        add_custom_command() 用于定义构建阶段(如 make/ninja 运行时)执行的自定义命令,通常用于生成文件、预处理资源、或在编译前后执行额外操作(如复制文件、运行脚本等)。它的核心是与构建系统(如 Makefile、Ninja)集成,根据依赖关系自动触发命令执行。

        add_custom_command() 有两种主要用法:生成文件(通过 OUTPUT 定义输出文件)和为目标添加命令(通过 TARGET 关联目标)。

        1.生成文件(最常用)

add_custom_command(OUTPUT <output_file1> [output_file2...]  # 命令生成的文件(必填)COMMAND <cmd1> [args1...]               # 要执行的命令(必填)[MAIN_DEPENDENCY <file>]                # 主要依赖文件(如输入源文件)[DEPENDS <dep1> <dep2>...]              # 其他依赖(文件或目标,依赖变化则重新执行)[WORKING_DIRECTORY <dir>]               # 命令执行的工作目录[COMMENT <message>]                     # 执行时显示的信息(方便调试)[VERBATIM]                              # 确保命令参数被正确转义(跨平台安全)
)

        2.为目标添加前置 / 后置命令

add_custom_command(TARGET <target_name>                    # 关联的目标(如可执行文件、库)[PRE_BUILD]                             # 目标构建前执行[PRE_LINK]                              # 目标链接前执行(仅部分构建系统支持)[POST_BUILD]                            # 目标构建后执行(最常用)COMMAND <cmd1> [args1...]               # 要执行的命令(必填)[WORKING_DIRECTORY <dir>][COMMENT <message>][VERBATIM]
)

1.2.常见使用场景

1.生成源文件(构建时动态生成代码)

当需要在构建阶段通过脚本(如 Python、Perl)生成源代码或头文件时,add_custom_command() 是核心工具。生成的文件需被其他目标依赖,以触发命令执行。

示例:用 Python 脚本生成头文件

# 定义命令:用 generate_header.py 生成 version.h
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/version.h  # 输出文件(构建目录下)COMMAND python3 ${CMAKE_SOURCE_DIR}/scripts/generate_header.py --version 1.0.0 --output ${CMAKE_BINARY_DIR}/version.hMAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/scripts/generate_header.py  # 脚本本身是主要依赖DEPENDS ${CMAKE_SOURCE_DIR}/version.txt  # 版本信息文件变化时重新生成WORKING_DIRECTORY ${CMAKE_BINARY_DIR}    # 在构建目录执行COMMENT "生成 version.h..."             # 构建时显示的提示VERBATIM                                # 跨平台参数转义(如路径含空格时)
)# 创建一个目标(如可执行文件),依赖生成的 version.h
add_executable(myapp src/main.cpp ${CMAKE_BINARY_DIR}/version.h)
# 确保 myapp 知道 version.h 的生成目录
target_include_directories(myapp PRIVATE ${CMAKE_BINARY_DIR})
  • 当 generate_header.py 或 version.txt 变化时,构建系统会自动重新执行命令生成 version.h
  • 若没有目标依赖 version.h,该命令可能不会执行(需结合 add_custom_target 强制执行,见下文)。

2.为目标添加后置命令(如复制文件、运行测试)

通过 TARGET + POST_BUILD 可在目标(如可执行文件)编译完成后执行命令,例如复制二进制到指定目录、运行验证脚本等。

示例:编译后复制二进制到 bin 目录

add_executable(myapp src/main.cpp)# 构建 myapp 后,复制到 ${CMAKE_BINARY_DIR}/bin 目录
add_custom_command(TARGET myappPOST_BUILD  # 编译链接完成后执行COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin  # 创建目录COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:myapp> ${CMAKE_BINARY_DIR}/bin/  # 复制文件COMMENT "复制 myapp 到 bin 目录..."VERBATIM
)
  • $<TARGET_FILE:myapp> 是生成器表达式,自动获取 myapp 的二进制文件路径(跨平台兼容)。
  • ${CMAKE_COMMAND} -E 是 CMake 自带的命令行工具,支持 copy/make_directory 等跨平台操作(避免直接用 cp/mkdir,增强兼容性)。

示例:解压文件

set(wrap_BLAS_LAPACK_sources${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.hpp${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.cpp${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.hpp${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.cpp
)add_custom_command(OUTPUT${wrap_BLAS_LAPACK_sources}COMMAND${CMAKE_COMMAND} -E tar xzf ${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gzCOMMAND${CMAKE_COMMAND} -E touch ${wrap_BLAS_LAPACK_sources}WORKING_DIRECTORY${CMAKE_CURRENT_BINARY_DIR}DEPENDS${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gzCOMMENT"Unpacking C++ wrappers for BLAS/LAPACK"VERBATIM
)

这段代码做了以下几件事:

  1. 定义输出文件列表(解压后预期的文件)
  2. 使用CMake自带的tar命令解压文件
  3. 使用touch命令更新文件时间戳(防止使用旧文件)
  4. 指定工作目录为构建目录
  5. 声明依赖关系(依赖于压缩包文件)
  6. 添加构建时的提示信息
  7. 使用VERBATIM确保命令跨平台兼容

3.结合 add_custom_target 强制执行命令

如果自定义命令生成的文件没有被其他目标依赖(如生成日志、文档),需用 add_custom_target 创建一个目标,并让该目标依赖 add_custom_command 的输出,以确保命令执行。

示例:生成文档(无其他目标依赖)

# 定义生成文档的命令
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/docs/index.html  # 生成的文档COMMAND doxygen ${CMAKE_SOURCE_DIR}/Doxyfile  # 用 doxygen 生成文档DEPENDS ${CMAKE_SOURCE_DIR}/Doxyfile ${CMAKE_SOURCE_DIR}/src  # 依赖配置和源码WORKING_DIRECTORY ${CMAKE_BINARY_DIR}COMMENT "生成文档..."
)# 创建自定义目标,依赖生成的文档
add_custom_target(generate_docs ALL  # ALL 表示默认构建时执行(可选)DEPENDS ${CMAKE_BINARY_DIR}/docs/index.html  # 依赖上述命令的输出
)
  • ALL 选项让 generate_docs 成为默认构建目标的一部分(运行 make 时自动执行);若不加,需手动运行 make generate_docs

1.3.注意事项

1.依赖关系是核心

构建系统通过 OUTPUTDEPENDS 判断是否需要执行命令。只有当输出文件不存在,或依赖文件(DEPENDS/MAIN_DEPENDENCY)比输出文件新时,命令才会执行。

2.跨平台兼容性

避免直接使用平台特定命令(如 Linux 的 cp、Windows 的 copy),优先用 ${CMAKE_COMMAND} -E 提供的跨平台操作:

  • cmake -E copy <src> <dest>:复制文件
  • cmake -E make_directory <dir>:创建目录
  • cmake -E remove <file>:删除文件

3.与 add_custom_target 的区别

  • add_custom_command 定义 “命令”,需通过依赖触发执行;
  • add_custom_target 定义 “目标”(类似 make 中的伪目标),可直接通过 make <target> 执行,常用来触发 add_custom_command

4.前置命令(PRE_BUILD)的限制

PRE_BUILD 在目标开始构建前执行,但并非所有构建系统都支持(如 Makefile 不支持,Ninja 支持)。若需跨平台的前置操作,建议通过依赖文件的方式间接实现(如让目标依赖一个生成文件的命令)。

2.add_custom_target

2.1.简介

        在 CMake 中,add_custom_target() 用于定义构建阶段的自定义目标(类似 Makefile 中的 “伪目标”),这些目标不对应实际的源文件编译,而是执行用户指定的命令(如生成文档、清理文件、运行测试等)。它是构建系统的 “一等公民”,可直接通过 make <target> 或 ninja <target> 调用,也能被其他目标依赖。

        基本语法:

add_custom_target(<target_name>            # 目标名称(必填)[ALL]                    # 可选:是否加入默认构建目标(运行 make 时自动执行)[COMMAND <cmd1> [args1...]]  # 要执行的命令(可多个,按顺序执行)[DEPENDS <dep1> <dep2>...]  # 依赖项(文件或其他目标,依赖变化时重新执行)[WORKING_DIRECTORY <dir>]   # 命令执行的工作目录[COMMENT <message>]        # 构建时显示的提示信息[VERBATIM]                 # 确保命令参数被正确转义(跨平台安全)[BYPRODUCTS <file1> <file2>...]  # 命令生成的副产品文件(CMake 3.9+)
)

2.2.常见使用场景

1.创建独立任务(不参与默认构建)

最常见的用途是定义可手动触发的任务(如生成文档、清理临时文件)。

示例:生成 API 文档

add_custom_target(docs                     # 目标名称:make docs 可触发COMMAND doxygen Doxyfile  # 执行 doxygen 生成文档WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}  # 在源码目录执行DEPENDS Doxyfile src/    # 依赖配置文件和源码目录COMMENT "生成 API 文档..."
)
  • 用户可通过 make docs 或 ninja docs 手动触发文档生成;
  • 若 Doxyfile 或 src/ 目录内容变化,下次执行时会重新生成。

2.加入默认构建(通过 ALL 选项)

若目标需在默认构建时自动执行(如生成必需的资源文件),可添加 ALL 选项。

示例:编译前生成配置文件

add_custom_target(generate_config ALL      # 加入默认构建,make 时自动执行COMMAND python3 ${CMAKE_SOURCE_DIR}/scripts/generate_config.py  # 生成配置OUTPUT ${CMAKE_BINARY_DIR}/config.h  # 生成的配置文件DEPENDS ${CMAKE_SOURCE_DIR}/scripts/generate_config.py  # 依赖脚本COMMENT "生成配置文件..."
)# 让可执行文件依赖此目标,确保生成配置文件后再编译
add_executable(myapp src/main.cpp)
add_dependencies(myapp generate_config)  # 依赖 generate_config 目标
  • 运行 make 时,generate_config 会先执行,确保 config.h 存在;
  • add_dependencies() 用于建立目标间的依赖关系。

3.依赖其他目标(联动执行)

自定义目标可依赖其他目标(如可执行文件、库),实现 “构建后操作”。

示例:构建后运行测试

add_executable(mytest test/main.cpp)  # 测试程序add_custom_target(run_tests                    # 目标名称:make run_tests 触发COMMAND ${CMAKE_BINARY_DIR}/mytest  # 运行测试程序DEPENDS mytest               # 依赖 mytest 目标(确保先编译)COMMENT "运行单元测试..."
)
  • 执行 make run_tests 时,CMake 会先确保 mytest 已编译,再运行测试;
  • 常用于自动化测试流程(如 CI/CD 环境)。

4.清理临时文件(结合 clean 目标)

可定义自定义清理目标,配合 make clean 使用。

示例:清理生成的文档

add_custom_target(clean_docs                   # 目标名称:make clean_docs 触发COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/docs  # 删除 docs 目录COMMENT "清理文档..."
)# 将 clean_docs 添加到默认的 clean 目标中(可选)
add_custom_target(clean_allCOMMAND ${CMAKE_MAKE_PROGRAM} clean  # 执行默认的 cleanCOMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/docs  # 额外清理文档COMMENT "完全清理..."
)
  • 执行 make clean_docs 可单独清理文档;
  • 执行 make clean_all 可同时执行默认清理和自定义清理。

2.3.注意事项

1.目标名称必须唯一

同一目录下的自定义目标名称不能重复,否则会覆盖或报错。

2.ALL 选项的慎用

添加 ALL 会使目标成为默认构建的一部分,可能延长构建时间(如生成文档、运行测试),建议仅对必需的操作使用 ALL

3.与 add_custom_command 的配合

若自定义目标需生成文件,通常先用 add_custom_command 定义命令,再用 add_custom_target 触发:

# 定义生成文件的命令
add_custom_command(OUTPUT data.txtCOMMAND echo "data" > data.txt
)# 定义目标,依赖生成的文件
add_custom_target(generate_dataDEPENDS data.txt  # 触发命令执行
)

4.跨平台命令

避免直接使用平台特定命令(如 rm/del),优先用 ${CMAKE_COMMAND} -E 提供的跨平台操作:

# 跨平台删除目录
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/temp

3.add_custom_command和add_custom_target区别

3.1. 区别

        在 CMake 中,add_custom_command 和 add_custom_target 都是用于自定义构建流程的命令,但它们的核心定位、触发方式和使用场景有显著区别。

  • add_custom_command 定义 “具体要执行的命令”(如生成文件、复制资源),但它本身不创建可直接调用的目标,需依赖其他条件触发;
  • add_custom_target 定义 “一个可执行的目标”(类似 Makefile 中的 “伪目标”,如 make clean),可直接通过构建命令(如 make mytarget)调用,也可被其他目标依赖。
维度add_custom_commandadd_custom_target
本质定义 “命令”(描述 “做什么”)定义 “目标”(描述 “一个可执行的任务”)
是否生成目标不生成独立目标,无对应的构建条目(如 make XXX 无法直接调用)生成独立目标,可通过 make <target> 直接执行
触发方式需通过 “依赖” 触发:
1. 若生成文件(OUTPUT),则被其他目标依赖该文件时触发;
2. 若关联目标(TARGET),则随目标构建(如编译、链接)触发。
需通过 “显式调用” 或 “被其他目标依赖” 触发:
1. 用户手动执行 make <target>
2. 其他目标通过 DEPENDS 依赖它时,随依赖目标触发。
典型用途生成文件(如动态生成代码)、为已有目标添加前后置命令(如编译后复制二进制)创建独立任务(如生成文档、清理日志)、强制触发 add_custom_command 的命令

3.2.示例说明

1.触发方式的核心差异

这是两者最关键的区别:

  • add_custom_command 的命令不会主动执行,必须满足特定依赖条件才会触发;
  • add_custom_target 的目标可以主动执行(用户显式调用),也可被依赖触发。

示例 1:add_custom_command 的触发依赖

假设用 add_custom_command 定义一个生成文件的命令:

# 定义命令:生成 output.txt
add_custom_command(OUTPUT output.txtCOMMAND echo "生成文件" > output.txtCOMMENT "执行生成命令..."
)# 只有当其他目标依赖 output.txt 时,上述命令才会执行
add_executable(myapp main.cpp output.txt)  # myapp 依赖 output.txt → 触发命令
  • 若没有 myapp 依赖 output.txt,这个命令永远不会执行;
  • 即使手动运行 make,也不会触发(因为没有目标关联它)。

示例 2:add_custom_target 的主动执行

用 add_custom_target 定义一个目标:

# 定义目标:打印一条消息
add_custom_target(print_msgCOMMAND echo "这是一个自定义目标"COMMENT "执行目标命令..."
)
  • 无需依赖其他目标,用户可直接通过 make print_msg 触发该命令;
  • 若想让它在默认构建中执行,可添加 ALL 选项:add_custom_target(print_msg ALL ...),这样运行 make 时会自动执行。

2.关联性:两者常结合使用

add_custom_command 生成的命令若没有被其他目标依赖,可能永远不会执行。此时可通过 add_custom_target 创建一个目标,让该目标依赖 add_custom_command 的输出,从而强制命令执行。

示例:生成文档(无其他目标依赖时)

# 1. 定义生成文档的命令(生成 docs/index.html)
add_custom_command(OUTPUT docs/index.htmlCOMMAND doxygen Doxyfile  # 用 doxygen 生成文档DEPENDS Doxyfile src/     # 依赖配置文件和源码COMMENT "生成文档..."
)# 2. 定义目标 generate_docs,依赖上述命令的输出
add_custom_target(generate_docsDEPENDS docs/index.html  # 依赖生成的文件 → 触发上述命令
)
  • 此时,用户运行 make generate_docs 会触发 add_custom_command 的命令,生成文档;
  • 若没有 add_custom_targetadd_custom_command 的命令因无依赖而不会执行。

4.总结

add_custom_command() 是构建阶段自定义流程的核心工具,主要用于:

  • 动态生成源文件 / 资源(通过 OUTPUT);
  • 目标构建前后执行辅助操作(通过 TARGET)。
    其关键是正确设置依赖关系(DEPENDS/OUTPUT),并结合 add_custom_target 确保命令被触发,同时注意跨平台兼容性。

add_custom_target() 是 CMake 中扩展构建流程的核心工具,主要用于:

  • 创建独立任务(如文档生成、测试运行);
  • 定义默认构建时执行的操作(通过 ALL 选项);
  • 实现目标间的依赖关系(如构建后自动执行其他命令)。

核心要点:

  • add_custom_command:专注于 “定义命令逻辑”,是 “被动触发” 的(依赖文件或目标),用于生成文件或给已有目标加前后置操作。
  • add_custom_target:专注于 “创建可执行任务”,是 “主动可调用” 的(通过 make <target>),常用于封装命令、创建独立任务(如生成文档、清理文件),或强制触发 add_custom_command

相关链接

  • CMake 官网 CMake - Upgrade Your Software Build System
  • CMake 官方文档:CMake Tutorial — CMake 4.1.0-rc1 Documentation
  • CMake 源码:https://github.com/Kitware/CMake
  • CMake 源码:https://gitlab.kitware.com/cmake/cmakeku
  • 中文版基础介绍:  CMake 入门实战 | HaHack
  • wiki:  Home · Wiki · CMake / Community · GitLab
  • Modern CMake 简体中文版:  Introduction · Modern CMake
http://www.dtcms.com/a/273035.html

相关文章:

  • Vue响应式原理五:响应式-自动收集依赖
  • OKHttp 核心知识点详解
  • 页面html,当鼠标点击图标,移开图标,颜色方块消失
  • 【牛客刷题】跳台阶(三种解法深度分析)
  • doker以及网站案例
  • 快速上手ASP .NET Core 8与MongoDB整合
  • 200W 以内的伺服电机 典型应用场景
  • C语言顺序表:从零开始,解锁数据结构之门!
  • YOLO系列pt导出不同onnx方法
  • Renren框架DistributeLock排他锁实现详解
  • 企业内网系统:从传统开发到智能赋能的进化之路
  • 安达发|医疗器械行业APS自动排单:智能化生产管理的未来之路
  • useRef跨渲染周期存储
  • 数据结构 --- 队列
  • 10.Docker安装mysql
  • chatgpt是怎么诞生的,详解GPT1到GPT4的演化之路及相关背景知识
  • dexie 前端数据库封装
  • 使用快捷键迅速校准多个通道 | IPEmotion
  • 软件技术:柯里化
  • 《PyQt6-3D应用开发技术文档》
  • 仿豆包智能输入框实现
  • python基础25_某大网校(下)处理json数据以及保存题库
  • 安全访问云端内部应用:用frp的stcp功能解决SSH转发的痛点
  • Linux驱动开发(platform 设备驱动)
  • 老题新解|矩阵转置
  • AI驱动的业务系统智能化转型:从非结构化到结构化的智能转换
  • 【STM32 学习笔记】FLASH闪存
  • pytorch学习-12循环神经网络(基础篇)
  • 机器视觉之激光码检测系统
  • 【世纪龙科技】学测-汽车信息化综合实训考核平台(机电方向)