如何解决CMake构建阶段Python脚本生成源文件首次编译失败问题
CMake构建阶段自动生成源文件并编译的问题解决
问题原因
你之前用add_custom_command(TARGET ...)绑定到目标执行脚本的方式,不管是POST_BUILD还是PRE_LINK阶段,执行时机都太晚了:
- sub_folder下的库目标在CMake配置阶段就通过
file(GLOB)扫描了源文件,首次构建时脚本还没运行,生成的文件不存在,自然不会被加入编译列表 - 第二次构建时文件已经存在,但如果没重新运行CMake,CMake缓存里还是旧的扫描结果,只有重新配置或者构建系统检测到目录变化才会重新扫描(不同构建系统行为有差异),这才导致第二次构建能成功。
而execute_process能正常工作,是因为它在配置阶段就运行脚本生成了文件,后续的file(GLOB)能直接扫描到这些文件,构建时自然会编译。
解决方案
方案1:配置阶段生成文件(最简单可靠)
如果你的Python脚本不需要依赖构建阶段的产物,直接用execute_process在配置阶段生成文件即可,这样sub_folder的file(GLOB)能直接扫到所有生成的文件。
在ROOT/build/CMakeLists.txt中添加:
# 运行Python脚本生成文件 execute_process( COMMAND ${CMAKE_COMMAND} -E env ${PYTHON3} ${CMAKE_CURRENT_SOURCE_DIR}/my_python_script.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE SCRIPT_RUN_RESULT ) # 检查脚本执行是否成功 if(NOT SCRIPT_RUN_RESULT EQUAL 0) message(FATAL_ERROR "自动生成源文件的Python脚本执行失败") endif()
方案2:构建阶段生成文件(需适配动态文件名)
如果必须在构建阶段生成文件(比如脚本需要依赖构建过程中生成的中间产物),要解决两个核心问题:确保脚本在库目标编译前运行,让CMake能追踪到动态生成的文件。
步骤如下:
- 修改Python脚本:生成源文件后,在
sub_folder/auto_gen_dir目录下输出一个包含所有生成源文件路径的文本文件(比如generated_sources.txt),每行一个文件路径。 - 在ROOT/build/CMakeLists.txt中定义生成目标:
# 定义生成文件的输出列表文件路径 set(GENERATED_FILE_LIST ${CMAKE_CURRENT_SOURCE_DIR}/../sub_folder/auto_gen_dir/generated_sources.txt) set(GENERATED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../sub_folder/auto_gen_dir) # 定义自定义命令:运行脚本生成文件,并输出文件列表 add_custom_command( OUTPUT ${GENERATED_FILE_LIST} COMMAND ${CMAKE_COMMAND} -E env ${PYTHON3} ${CMAKE_CURRENT_SOURCE_DIR}/my_python_script.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "正在自动生成源文件..." VERBATIM ) # 创建自定义目标,确保脚本在构建阶段执行 add_custom_target(generate_auto_sources ALL DEPENDS ${GENERATED_FILE_LIST})
- 在sub_folder/build/CMakeLists.txt中绑定依赖并添加源文件:
set(GENERATED_FILE_LIST ${CMAKE_CURRENT_SOURCE_DIR}/../auto_gen_dir/generated_sources.txt) # 读取生成的源文件列表 file(READ ${GENERATED_FILE_LIST} GENERATED_SOURCES_CONTENT) string(REPLACE "\n" ";" GENERATED_SOURCES ${GENERATED_SOURCES_CONTENT}) # 过滤空行 list(FILTER GENERATED_SOURCES EXCLUDE REGEX "^$") # 创建库目标 add_library(amdb_core STATIC) # 添加生成的源文件 target_sources(amdb_core PRIVATE ${GENERATED_SOURCES}) # 让库目标依赖生成脚本的目标,确保先生成文件再编译 add_dependencies(amdb_core generate_auto_sources)
这种方式避免了依赖file(GLOB)的配置阶段扫描,直接通过脚本输出的文件列表添加源文件,同时通过add_dependencies确保生成步骤先于编译执行。
总结
- 优先选择方案1,配置阶段生成文件,实现简单且不易出错
- 若必须在构建阶段生成,采用方案2,通过脚本输出文件列表的方式让CMake追踪动态生成的文件,避免依赖
file(GLOB)的局限性
内容的提问来源于stack exchange,提问作者Sriram
相关产品推荐
相关产品推荐

