CMake中add_dependencies等命令协同失效问题及解法疑问
我想用CMake实现一个简单流程:用脚本生成文件,再基于这些生成文件构建可执行文件。查了文档和资料后按预期配置,但项目始终无法正常构建。
预期执行步骤
- 配置阶段:生成带正确路径的
generate_files.sh脚本 - 构建阶段:先构建
gen自定义目标,再构建test可执行目标
步骤1能正常完成,但步骤2失效。CMake报错test目标的源文件不存在,尽管逻辑上应该先构建gen生成这些源文件。
目录结构
. ├── build ├── CMakeLists.txt ├── config │ └── generate_files.sh.in ├── extern │ ├── CMakeLists.txt │ └── gen ├── scripts └── src ├── CMakeLists.txt └── main.cpp
各文件内容
顶层CMakeLists.txt
cmake_minimum_required(VERSION 3.13) project(test VERSION 0.1.0.0) add_subdirectory(src) add_subdirectory(extern) configure_file("${CMAKE_SOURCE_DIR}/config/generate_files.sh.in" "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh")
src/CMakeLists.txt
add_executable(test main.cpp)
extern/CMakeLists.txt(原失效配置)
set(generated_sources ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated.h ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_2.cpp ) set(generated_directories ${CMAKE_CURRENT_SOURCE_DIR}/gen) add_custom_command( OUTPUT ${generated_sources} COMMAND "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh" ) add_custom_target(gen DEPENDS ${generated_sources} ) add_dependencies(test gen) target_sources(test PRIVATE ${generated_sources} ) target_include_directories(test PRIVATE ${generated_directories} )
config/generate_files.sh.in
#! /bin/bash echo " const int get_trouble_code(); const int get_higher_trouble_code();" > ${CMAKE_SOURCE_DIR}/extern/gen/generated.h echo " #include \"generated.h\" const int get_trouble_code(){return 1;}" > ${CMAKE_SOURCE_DIR}/extern/gen/generated_1.cpp echo " #include \"generated.h\" const int get_higher_trouble_code(){return 1+1;}" > ${CMAKE_SOURCE_DIR}/extern/gen/generated_2.cpp
src/main.cpp
#include "generated.h" int main() { get_trouble_code(); get_higher_trouble_code(); return 0; }
编辑:找到可行方案但有疑问
把生成文件打包成库而非自定义目标后就能正常工作,修改后的extern/CMakeLists.txt如下,求解释原因:
set(generated_sources ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated.h ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_2.cpp ) set(generated_directories ${CMAKE_CURRENT_SOURCE_DIR}/gen) add_custom_command( OUTPUT ${generated_sources} COMMAND "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh" ) # 注释掉原来的自定义目标相关代码 #add_custom_target(gen # DEPENDS ${generated_sources} # ) # #add_dependencies(test gen) # #target_sources(test # PRIVATE # ${generated_sources} #) # 改为生成库 add_library(gen ${generated_sources}) target_link_libraries(test PRIVATE gen) target_include_directories(test PRIVATE ${generated_directories} )
原配置失效的原因
CMake在配置阶段就会检查目标的源文件是否存在(除非明确标记为生成文件)。你原配置中,虽然给test添加了对gen自定义目标的依赖,但直接把生成的源文件通过target_sources加到test时,CMake无法把这些源文件和add_custom_command的输出关联起来——它只会认为这些是普通的、必须预先存在的源文件,所以配置阶段就会报错找不到文件。
add_dependencies(test gen)只是告诉CMake:构建test之前要先构建gen,但这并不能让CMake意识到generated_sources是由gen目标生成的,因为自定义目标和生成文件的关联没有传递到test目标上。
改用库可行的原因
当你用add_library(gen ${generated_sources})时,CMake会自动把add_custom_command的输出(generated_sources)和这个库目标关联起来:
- CMake识别到这些源文件是
add_custom_command的输出,知道它们需要在构建库之前生成 - 当
test链接这个库时,CMake会自动处理依赖链:先运行add_custom_command生成源文件,再构建gen库,最后构建test可执行文件 - 这种方式下,生成文件的依赖关系被封装在库目标内部,CMake能正确追踪整个流程,不会在配置阶段就报错源文件不存在
原配置的修复方案(不用库的话)
如果不想用库,只需修改原extern/CMakeLists.txt,把add_custom_command的输出直接关联到test目标,而不是通过中间自定义目标:
set(generated_sources ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated.h ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_2.cpp ) set(generated_directories ${CMAKE_CURRENT_SOURCE_DIR}/gen) # 直接为test目标添加生成源文件的自定义命令 add_custom_command( OUTPUT ${generated_sources} COMMAND "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh" # 可选:指定DEPENDS,如果脚本依赖其他文件的话 # DEPENDS "${CMAKE_SOURCE_DIR}/config/generate_files.sh.in" ) # 直接把生成文件加到test,CMake会自动关联自定义命令的依赖 target_sources(test PRIVATE ${generated_sources} ) target_include_directories(test PRIVATE ${generated_directories} )
这样CMake就能识别到这些源文件是生成的,会在构建test之前自动运行add_custom_command生成它们,不需要额外的自定义目标和add_dependencies。
内容的提问来源于stack exchange,提问作者learning2code

