在 CMake 中,add_custom_target
是一个用于创建自定义构建目标的命令。它主要用于定义一些不生成文件,但需要执行的特定操作(比如运行脚本、执行命令、触发其他构建步骤等)。以下是它的核心用途和特点:
基本语法
add_custom_target(
<目标名称>
[ALL]
[COMMAND <命令1> [args...]]
[WORKING_DIRECTORY <目录>]
[DEPENDS <依赖项...>]
[COMMENT <注释文本>]
[VERBATIM]
[SOURCES <源文件...>]
)
核心作用
-
创建逻辑目标
定义一个在构建时(如执行make <目标名称>
或ninja <目标名称>
)可以手动触发的虚拟目标。它不关联任何输出文件,通常用于执行任务(如清理、打包、测试等)。 -
作为依赖项
可以通过add_dependencies
让其他目标依赖此自定义目标,确保它在特定目标之前执行。
关键参数
-
ALL
将该目标添加到默认构建(即直接运行make
或cmake --build
时会自动执行)。 -
COMMAND
指定要执行的命令(可以是脚本、可执行程序等)。 -
DEPENDS
指定目标依赖的文件或其他目标(依赖项变更时会重新执行)。 -
WORKING_DIRECTORY
设置命令执行的工作目录。 -
COMMENT
构建时显示的日志信息。
典型场景示例
-
运行一个脚本
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 脚本。
- 通过
-
创建清理任务
add_custom_target(clean_all COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/output COMMENT "Cleaning all generated files..." )
-
组合多个命令
add_custom_target(deploy COMMAND make package COMMAND scp package.tar.gz user@server:/path DEPENDS package_target )
-
作为其他目标的依赖
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
的日志(因为目标本身没有实际动作)。
典型用途
-
作为依赖的占位目标
例如,确保某个目标在编译前等待某些条件满足: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
添加实际行为。
- 后续可以通过
-
手动触发的空目标
用于分组或标记构建阶段:add_custom_target(phase1) add_custom_target(phase2)
- 用户可以通过
make phase1
或make phase2
选择构建阶段。
- 用户可以通过
注意事项
-
无命令的目标是有效的
CMake 允许定义无动作的目标,但通常需要后续通过DEPENDS
或add_dependencies
赋予其意义。 -
与
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 )
-
避免混淆
如果目标需要执行命令,必须明确指定COMMAND
参数。仅定义名称的目标通常用于设计复杂的构建流程控制。
对比多参数示例
如果目标是执行具体操作,则需要完整参数:
add_custom_target(
run_script
COMMAND ./script.sh
COMMENT "Running script..."
)
总结:可以只写一个参数,但这样的目标默认无行为,需通过其他机制(如依赖或后续扩展)赋予其实际用途。
在 CMake 中,add_custom_target
的 ALL
参数表示将该目标添加到默认构建流程中。即,当用户直接运行 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
,会依次执行:
generate_headers
生成头文件- 编译
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..."
)
运行 make
或 cmake --build .
后,会:
- 编译
my_app
- 自动执行
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_main
、run_tests
和 generate_docs
。
注意事项
-
依赖顺序
ALL
目标本身不保证执行顺序,需通过DEPENDS
显式定义依赖关系(如上文的DEPENDS my_app
)。 -
避免滥用
如果某个任务不需要每次构建都执行(例如耗时操作),不要加ALL
,否则会拖慢默认构建速度。 -
与
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/ )
关键注意事项
-
生成文件的依赖链
如果其他目标依赖add_custom_command
生成的OUTPUT
文件(如generated.cpp
),CMake 会自动确保该命令在编译依赖它的目标前执行。 -
与
add_custom_target
的区别add_custom_target
定义一个始终执行的虚拟目标(通过make my_target
手动触发)。add_custom_command
仅在需要生成文件或响应构建事件时触发。
-
跨平台兼容性
使用${CMAKE_COMMAND} -E
调用跨平台友好的命令(如文件操作),避免直接使用cp
或mv
等系统命令。
总结:执行时机
- 生成文件模式:当
OUTPUT
文件不存在,或DEPENDS
的文件被修改时自动执行。 - 构建事件模式:在目标构建的指定阶段(如
POST_BUILD
)无条件执行。
合理使用 add_custom_command
可以实现文件生成、后处理、资源嵌入等灵活操作,确保构建流程的自动化。