如何在CMake中无全局变量链式调用不同目录的custom_targets?
解决CMake跨子目录自定义目标的文件依赖问题
首先得明确核心问题:你遇到的是跨子目录的生成文件路径传递和自定义目标依赖不传递文件这两个CMake特性导致的问题。自定义目标的依赖只会保证目标执行,但不会把生成的文件路径自动传递给下游,而且跨子目录时相对路径根本找不到生成的文件(尤其是out-of-source构建时)。
不用全局变量的话,推荐两种更符合CMake最佳实践的方案:
方案一:通过Target属性传递生成文件路径
这种方法更贴合CMake的"以目标为中心"的设计思路,不需要依赖全局或父作用域变量。
1. 修改gen1/CMakeLists.txt
给gen1-wrapper目标添加一个自定义INTERFACE属性,用来存储生成文件的绝对路径:
set(SPECIAL_TEXT foo) # 务必使用绝对路径指定输出文件,避免路径混乱 add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen1 COMMAND cmake -E echo ${SPECIAL_TEXT} > ${CMAKE_CURRENT_BINARY_DIR}/gen1 ) add_custom_target( gen1-wrapper DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gen1 ) # 添加自定义INTERFACE属性,把生成文件路径附加到目标上 set_target_properties(gen1-wrapper PROPERTIES INTERFACE_GEN1_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gen1 )
2. 调整顶层CMakeLists.txt的子目录顺序
因为gen2需要引用gen1的目标,所以必须先加载gen1的子目录:
add_subdirectory(gen1) add_subdirectory(gen2)
3. 修改gen2/CMakeLists.txt
从gen1-wrapper目标中获取生成文件路径,同时在自定义命令中同时依赖目标和文件:
# 从目标属性中提取gen1的生成文件路径 get_target_property(GEN1_OUTPUT_FILE gen1-wrapper INTERFACE_GEN1_OUTPUT_FILE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen2 # 同时依赖目标(确保gen1执行)和文件(让CMake追踪文件依赖) DEPENDS gen1-wrapper ${GEN1_OUTPUT_FILE} COMMAND cmake -E copy ${GEN1_OUTPUT_FILE} ${CMAKE_CURRENT_BINARY_DIR}/gen2 ) add_custom_target( all-generated ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gen2 )
方案二:通过父作用域传递变量
如果觉得Target属性太复杂,也可以用父作用域传递变量,同样避免全局变量(只在父目录和子目录间传递,不是全局)。
1. 修改gen1/CMakeLists.txt
把生成文件的绝对路径传递到父作用域:
set(SPECIAL_TEXT foo) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen1 COMMAND cmake -E echo ${SPECIAL_TEXT} > ${CMAKE_CURRENT_BINARY_DIR}/gen1 ) add_custom_target( gen1-wrapper DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gen1 ) # 传递变量到父目录 set(GEN1_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gen1 PARENT_SCOPE)
2. 调整顶层CMakeLists.txt的子目录顺序
同样要先加载gen1,确保父目录能拿到变量:
add_subdirectory(gen1) add_subdirectory(gen2)
3. 修改gen2/CMakeLists.txt
直接使用父目录传递过来的变量:
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen2 DEPENDS gen1-wrapper ${GEN1_OUTPUT_FILE} COMMAND cmake -E copy ${GEN1_OUTPUT_FILE} ${CMAKE_CURRENT_BINARY_DIR}/gen2 ) add_custom_target( all-generated ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gen2 )
关键注意点
- 绝对路径是必须的:out-of-source构建时,gen1和gen2的构建目录(比如
build/gen1和build/gen2)是分开的,相对路径根本找不到对方的生成文件。 - 必须同时依赖目标和文件:自定义目标的依赖不会自动传递生成文件,所以下游的自定义命令要同时依赖上游目标(确保执行)和上游的输出文件(让CMake正确追踪文件依赖关系)。
内容的提问来源于stack exchange,提问作者random
相关产品推荐
相关产品推荐

