CMake中如何为已创建的custom_target()添加依赖项?
需要创建名为sdk_extraction的顶层目标,使其每次构建/测试都会执行,该目标依赖一批从项目文件复制或轻微转换生成的文件。已将待导出文件列表存为全局属性列表,尝试两种实现方式:
方式1:先创建目标再添加依赖(报错)
先通过add_custom_target()创建sdk_target,再遍历文件生成custom_command,用add_dependencies()给目标添加依赖,报错“依赖目标不存在”。
代码示例:
function(finalize_sdk_export destination) file(REAL_PATH ${destination} destination ) add_custom_target(sdk_target ALL COMMENT "Exporting SDK" ) get_property(sdk_source GLOBAL PROPERTY SDK_SOURCE ) string(REPLACE " " ";" sdk_source "${sdk_source}") foreach (item ${sdk_source}) string(REPLACE ${destination} ${destination}/sdk target "${item}") add_custom_command( OUTPUT ${target} DEPENDS ${item} COMMAND ${CMAKE_COMMAND} -E copy ${item} ${target} VERBATIM ) add_dependencies(sdk_target ${target}) endforeach() endfunction()
报错信息:
CMake Error at <redacted>/build_scripts/CMakeLists.txt:209 (add_dependencies): The dependency target "<redacted>/sdk/CMakeLists.txt" of target "sdk_target" does not exist. Call Stack (most recent call first): <redacted>/CMakeLists.txt:78 (finalize_sdk_export)
方式2:先收集输出文件再创建目标(正常工作,但担心性能)
先遍历生成所有custom_command并收集输出文件列表,创建custom_target时通过DEPENDS参数指定所有依赖,可正常运行,但担心文件数量达数百/数千个时出现问题。
代码示例:
function(finalize_sdk_export destination) file(REAL_PATH ${destination} destination ) get_property(sdk_source GLOBAL PROPERTY SDK_SOURCE ) string(REPLACE " " ";" sdk_source "${sdk_source}") foreach (item ${sdk_source}) string(REPLACE ${destination} ${destination}/sdk target "${item}") add_custom_command( OUTPUT ${target} DEPENDS ${item} COMMAND ${CMAKE_COMMAND} -E copy ${item} ${target} VERBATIM ) list(APPEND sdk_files ${target}) endforeach() add_custom_target(sdk_target ALL COMMENT "Exporting SDK" DEPENDS ${sdk_files} ) endfunction()
注:已放弃CMake内置的install/export功能,因为需要严格控制目录结构和CMake文件样式。
方式1报错原因
add_dependencies()仅接受CMake目标作为参数——即通过add_executable、add_library或add_custom_target创建的实体,而你传入的${target}是生成文件的路径,并非CMake目标,因此会提示“依赖目标不存在”。add_custom_command生成的输出文件,只能通过自定义目标的DEPENDS参数关联,不能用add_dependencies。
方式2是标准且可靠的实现
方式2是CMake中关联自定义命令输出与自定义目标的标准用法,完全无需担心文件数量问题。CMake内部对大量依赖项的处理做了优化,哪怕是数千个文件,只要路径有效、无重复,就能稳定运行。
可选优化点
若想提升代码效率或可维护性,可考虑:
- 若源文件符合特定路径/命名规则,用
file(GLOB_RECURSE ... CONFIGURE_DEPENDS)(CMake 3.12+支持)批量收集文件,减少手动维护全局属性的工作量,CONFIGURE_DEPENDS可自动检测新增文件。 - 对于需要转换的文件,将转换逻辑封装成独立脚本或CMake函数,在
add_custom_command中调用,让代码结构更清晰。
最终推荐代码
直接采用方式2的实现即可,若要将目标名改为需求中的sdk_extraction,仅需修改add_custom_target的第一个参数:
function(finalize_sdk_export destination) file(REAL_PATH ${destination} destination ) get_property(sdk_source GLOBAL PROPERTY SDK_SOURCE ) string(REPLACE " " ";" sdk_source "${sdk_source}") foreach (item ${sdk_source}) string(REPLACE ${destination} ${destination}/sdk target "${item}") add_custom_command( OUTPUT ${target} DEPENDS ${item} COMMAND ${CMAKE_COMMAND} -E copy ${item} ${target} VERBATIM ) list(APPEND sdk_files ${target}) endforeach() add_custom_target(sdk_extraction ALL COMMENT "Exporting SDK" DEPENDS ${sdk_files} ) endfunction()
内容的提问来源于stack exchange,提问作者Russ Schultz

