如何在CMake中为每个编译命令前执行自定义命令
在CMake中为每个源文件编译前绑定自定义命令
完全可行,下面提供两种实现方案,满足你“仅当目标文件需重建时执行自定义命令”且“通过生成表达式传递源文件路径”的需求:
方案一:针对单个源文件绑定独立自定义命令
这种方式灵活性更高,可针对不同源文件配置不同的预处理逻辑,依赖关系清晰。
# 定义源文件列表 set(SOURCES source1.c source2.c main.c) # 创建目标可执行文件 add_executable(my_app ${SOURCES}) # 遍历每个源文件,绑定预处理命令 foreach(src IN LISTS SOURCES) # 获取当前源文件对应的目标文件路径 get_source_file_property(obj_path ${src} OBJECT_OUTPUTS) # 生成唯一的标记文件,用于跟踪预处理命令是否需要执行 set(marker_file "${CMAKE_CURRENT_BINARY_DIR}/${src}.precompile.marker") # 定义自定义预处理命令 add_custom_command( OUTPUT ${marker_file} # 这里替换为你的实际自定义命令,用生成表达式传递源文件路径 COMMAND my_preprocess_tool "$<SHELL_PATH:${src}>" # 依赖源文件,只有源文件变化时才重新执行预处理 DEPENDS ${src} COMMENT "正在预处理源文件: ${src}" VERBATIM ) # 创建临时目标,关联标记文件的依赖 add_custom_target(precompile_${src} DEPENDS ${marker_file}) # 让主目标依赖临时目标,确保编译源文件前先执行预处理 add_dependencies(my_app precompile_${src}) endforeach()
原理说明
- 每个源文件对应一个唯一的标记文件,自定义命令仅在源文件变化时重新生成标记文件
- 目标文件(如
source1.o)间接依赖标记文件,因此只有当标记文件更新时,才会触发后续的编译步骤 $<SHELL_PATH:>生成表达式用于处理含空格的源文件路径,确保在shell中正确解析
方案二:全局修改编译命令(批量应用)
如果所有源文件的预处理逻辑一致,可通过修改CMake内置的编译命令变量,一次性为所有源文件添加预处理步骤,更简洁高效。
# 保存原始的C语言编译命令 set(ORIGINAL_C_COMPILE_OBJECT ${CMAKE_C_COMPILE_OBJECT}) # 修改编译命令,在正式编译前插入自定义预处理步骤 # <SOURCE>是CMake编译命令中的占位符,代表当前要编译的源文件路径 set(CMAKE_C_COMPILE_OBJECT "my_preprocess_tool \"$<SHELL_PATH:<SOURCE>>\" && ${ORIGINAL_C_COMPILE_OBJECT}" ) # 定义源文件并创建目标 set(SOURCES source1.c source2.c main.c) add_executable(my_app ${SOURCES})
原理说明
CMAKE_C_COMPILE_OBJECT是CMake用于生成C语言编译命令的模板变量- 我们在原始编译命令前插入自定义命令,用
<SOURCE>占位符获取当前源文件路径,配合$<SHELL_PATH:>处理路径兼容性 - 只有当源文件需要重新编译时,整个命令串(预处理+编译)才会执行,满足“按需执行”的要求
注意事项
- 替换示例中的
my_preprocess_tool为你实际的自定义命令或脚本路径 - 如果是C++源文件,需修改
CMAKE_CXX_COMPILE_OBJECT变量 - 生成表达式
$<SHELL_PATH:>适用于大多数平台,若需跨平台兼容,建议保留该表达式处理路径中的特殊字符
内容的提问来源于stack exchange,提问作者fskoras
相关产品推荐
相关产品推荐

