CMake中add_custom_target和add_custom_command用法详解

在 CMake 中,add_custom_target 是一个用于创建自定义构建目标的命令。它主要用于定义一些不生成文件,但需要执行的特定操作(比如运行脚本、执行命令、触发其他构建步骤等)。以下是它的核心用途和特点:


基本语法

add_custom_target(
    <目标名称>
    [ALL]
    [COMMAND <命令1> [args...]]
    [WORKING_DIRECTORY <目录>]
    [DEPENDS <依赖项...>]
    [COMMENT <注释文本>]
    [VERBATIM]
    [SOURCES <源文件...>]
)

核心作用

  1. 创建逻辑目标
    定义一个在构建时(如执行 make <目标名称>ninja <目标名称>)可以手动触发的虚拟目标。它不关联任何输出文件,通常用于执行任务(如清理、打包、测试等)。

  2. 作为依赖项
    可以通过 add_dependencies 让其他目标依赖此自定义目标,确保它在特定目标之前执行。


关键参数

  • ALL
    将该目标添加到默认构建(即直接运行 makecmake --build 时会自动执行)。

  • COMMAND
    指定要执行的命令(可以是脚本、可执行程序等)。

  • DEPENDS
    指定目标依赖的文件或其他目标(依赖项变更时会重新执行)。

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

  • COMMENT
    构建时显示的日志信息。


典型场景示例

  1. 运行一个脚本

    add_custom_target(
        run_my_script
        COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/setup.py
        COMMENT "Running setup script..."
    )
    
    • 通过 make run_my_script 执行 Python 脚本。
  2. 创建清理任务

    add_custom_target(clean_all
        COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/output
        COMMENT "Cleaning all generated files..."
    )
    
  3. 组合多个命令

    add_custom_target(deploy
        COMMAND make package
        COMMAND scp package.tar.gz user@server:/path
        DEPENDS package_target
    )
    
  4. 作为其他目标的依赖

    add_executable(my_app main.cpp)
    add_custom_target(generate_resources COMMAND ...)
    add_dependencies(my_app generate_resources)  # 确保生成资源后再编译
    

add_custom_command 的区别

  • add_custom_target 定义一个始终执行的虚拟目标。
  • add_custom_command 则关联到具体的文件生成(例如生成头文件),通常由其他目标间接触发。

注意事项

  • 如果目标需要生成文件,通常应使用 add_custom_command + add_custom_target 组合。
  • 跨平台时注意命令的兼容性(如使用 CMAKE_COMMAND -E 调用跨平台工具)。

通过灵活使用 add_custom_target,可以在 CMake 中实现复杂的构建流程控制。


在 CMake 中,add_custom_target 可以只指定目标名称(即只写一个参数),但这样的目标将不会执行任何命令(COMMAND),仅作为一个逻辑上的空目标存在。它的常见用途是作为其他目标的依赖项(通过 add_dependencies)或手动触发的构建目标。


单参数示例

add_custom_target(my_dummy_target)
  • 执行 make my_dummy_target 时,不会运行任何命令,但会输出类似 Built target my_dummy_target 的日志(因为目标本身没有实际动作)。

典型用途

  1. 作为依赖的占位目标
    例如,确保某个目标在编译前等待某些条件满足:

    add_custom_target(pre_build_check)
    add_executable(my_app main.cpp)
    add_dependencies(my_app pre_build_check)  # 强制 my_app 依赖 pre_build_check
    
    • 后续可以通过 add_custom_command 或其他方式为 pre_build_check 添加实际行为。
  2. 手动触发的空目标
    用于分组或标记构建阶段:

    add_custom_target(phase1)
    add_custom_target(phase2)
    
    • 用户可以通过 make phase1make phase2 选择构建阶段。

注意事项

  1. 无命令的目标是有效的
    CMake 允许定义无动作的目标,但通常需要后续通过 DEPENDSadd_dependencies 赋予其意义。

  2. add_dependencies 配合
    单参数目标常用于依赖管理,例如:

    add_custom_target(generate_resources)  # 空目标
    add_executable(app main.cpp)
    add_dependencies(app generate_resources)  # 确保 generate_resources 先执行
    
    # 后续可能通过 add_custom_command 为 generate_resources 添加实际生成步骤
    add_custom_command(
        TARGET generate_resources
        COMMAND touch resources.txt
    )
    
  3. 避免混淆
    如果目标需要执行命令,必须明确指定 COMMAND 参数。仅定义名称的目标通常用于设计复杂的构建流程控制。


对比多参数示例

如果目标是执行具体操作,则需要完整参数:

add_custom_target(
    run_script
    COMMAND ./script.sh
    COMMENT "Running script..."
)

总结:可以只写一个参数,但这样的目标默认无行为,需通过其他机制(如依赖或后续扩展)赋予其实际用途。


在 CMake 中,add_custom_targetALL 参数表示将该目标添加到默认构建流程中。即,当用户直接运行 make(或 cmake --build)而不指定具体目标时,该自定义目标会自动执行。


ALL 的作用

  • 强制包含在默认构建中
    不加 ALL 的自定义目标需要手动触发(例如 make my_target),而加了 ALL 的目标会和 main 目标一样,成为默认构建的一部分。
  • 简化构建流程
    适用于需要每次构建时自动执行的任务(如代码生成、后处理、资源打包等)。

典型场景示例

1. 自动生成代码或资源

假设每次构建前需要生成一些代码或资源文件(例如通过脚本生成头文件),可以将生成步骤设为 ALL 目标,确保它们总是先于主程序编译。

# 生成头文件的脚本
add_custom_target(
    generate_headers ALL  # 关键:ALL 参数
    COMMAND ${PYTHON_EXECUTABLE} generate.py
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scripts
    COMMENT "Generating headers..."
)

# 主程序依赖生成的头文件
add_executable(my_app main.cpp generated_header.h)

此时直接运行 make,会依次执行:

  1. generate_headers 生成头文件
  2. 编译 my_app

如果省略 ALL,用户需要手动运行 make generate_headers && make


2. 构建后自动打包

构建完成后自动将可执行文件打包成压缩包,并包含在默认构建中:

add_executable(my_app main.cpp)

# 打包任务(依赖 my_app)
add_custom_target(
    package_all ALL  # 关键:ALL 参数
    COMMAND ${CMAKE_COMMAND} -E tar cfv ${CMAKE_BINARY_DIR}/my_app.tar.gz my_app
    DEPENDS my_app
    COMMENT "Packaging the app..."
)

运行 makecmake --build . 后,会:

  1. 编译 my_app
  2. 自动执行 package_all 打包。

3. 统一构建入口

在复杂项目中,将所有关键步骤(编译、测试、文档生成)统一到默认构建中:

# 默认构建会按顺序执行以下目标
add_custom_target(build_all ALL
    DEPENDS compile_main run_tests generate_docs
)

add_custom_target(compile_main ...)
add_custom_target(run_tests ...)
add_custom_target(generate_docs ...)

直接运行 make 会触发 compile_mainrun_testsgenerate_docs


注意事项

  1. 依赖顺序
    ALL 目标本身不保证执行顺序,需通过 DEPENDS 显式定义依赖关系(如上文的 DEPENDS my_app)。

  2. 避免滥用
    如果某个任务不需要每次构建都执行(例如耗时操作),不要加 ALL,否则会拖慢默认构建速度。

  3. add_custom_command 的区别
    add_custom_target + ALL 适合无输出文件的逻辑任务,而 add_custom_command 更适合生成文件的任务(例如生成代码)。


对比无 ALL 的情况

# 无 ALL 的目标需要手动触发
add_custom_target(
    manual_task
    COMMAND echo "This is a manual task"
)

# 运行 make 不会触发 manual_task
# 必须显式执行 make manual_task

总结

使用 ALL 的场景:

  • 需要每次构建时自动执行的任务(如代码生成、资源处理)。
  • 统一构建入口,简化用户操作(如编译、测试、打包一步完成)。
  • 强制某些任务作为构建流程的一部分(例如清理临时文件)。

add_custom_command 是 CMake 中用于在构建过程中添加自定义命令的工具。它通常用于生成文件或在构建特定目标前后执行操作。其行为和执行时机取决于具体使用场景。


主要用法

add_custom_command 有两种典型用法:

1. 生成文件(Generating Files)

用于生成一个或多个文件,通常与 add_custom_target 或目标依赖结合使用。

add_custom_command(
  OUTPUT output_file         # 生成的输出文件
  COMMAND command [args...]  # 执行的命令
  DEPENDS depend_files...    # 依赖的文件或目标
  COMMENT "message"          # 可选,构建时的提示信息
)
  • 执行时机:当 output_file 不存在,或其 DEPENDS 中的文件被修改时,命令会自动执行。
  • 示例:生成源代码文件:
    add_custom_command(
      OUTPUT generated.cpp
      COMMAND generator_tool input.txt generated.cpp
      DEPENDS input.txt
    )
    add_executable(my_app generated.cpp)  # 依赖 generated.cpp
    
2. 构建事件(Build Events)

附加到某个目标(如可执行文件或库)的构建阶段,在构建前/后执行命令。

add_custom_command(
  TARGET target             # 目标名称(如可执行文件)
  PRE_BUILD | PRE_LINK | POST_BUILD
  COMMAND command [args...] # 执行的命令
  COMMENT "message"         # 可选,构建时的提示信息
)
  • 执行时机
    • PRE_BUILD:在编译目标源文件之前执行(某些生成器如 Visual Studio 支持)。
    • PRE_LINK:在编译源文件后、链接目标前执行。
    • POST_BUILD:在目标构建完成后执行。
  • 示例:构建完成后复制文件:
    add_executable(my_app main.cpp)
    add_custom_command(
      TARGET my_app
      POST_BUILD
      COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:my_app> /path/to/destination/
    )
    

关键注意事项

  1. 生成文件的依赖链
    如果其他目标依赖 add_custom_command 生成的 OUTPUT 文件(如 generated.cpp),CMake 会自动确保该命令在编译依赖它的目标前执行。

  2. add_custom_target 的区别

    • add_custom_target 定义一个始终执行的虚拟目标(通过 make my_target 手动触发)。
    • add_custom_command 仅在需要生成文件或响应构建事件时触发。
  3. 跨平台兼容性
    使用 ${CMAKE_COMMAND} -E 调用跨平台友好的命令(如文件操作),避免直接使用 cpmv 等系统命令。


总结:执行时机

  • 生成文件模式:当 OUTPUT 文件不存在,或 DEPENDS 的文件被修改时自动执行。
  • 构建事件模式:在目标构建的指定阶段(如 POST_BUILD)无条件执行。

合理使用 add_custom_command 可以实现文件生成、后处理、资源嵌入等灵活操作,确保构建流程的自动化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值