如何让CMake目标依赖外部Make构建的源文件?
CMake集成第三方SDK Make构建的依赖更新问题
问题描述
我的CMake项目需要调用第三方SDK的make脚本构建静态库,再复制到二进制目录用于链接。简化代码如下(实际freertos为变量,可指代任意SDK库):
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib COMMAND make -s -f makefile freertos PROFILE=${CMAKE_BUILD_TYPE} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SDK_PATH}/source/kernel/freertos/lib/freertos.${CMAKE_BUILD_TYPE}.lib ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib WORKING_DIRECTORY ${SDK_PATH}) set(LIB_NAME freertos-${EXTENSION}) # 创建依赖自定义命令输出的临时目标 add_custom_target(${LIB_NAME}-temp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib) add_dependencies(${LIB_NAME} ${LIB_NAME}-temp) # 配置导入库的属性 set_target_properties(${LIB_NAME} PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib INTERFACE_INCLUDE_DIRECTORIES "${SDK_PATH}/source" INTERFACE_LINK_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}")
当前实现可正常构建,但存在两个问题:
- SDK源码文件修改后,CMake不会触发自定义命令重新执行
- 不想每次构建都强制运行make脚本,只在必要时执行
同时不想手动添加SDK中数千个源文件到CMake,也不愿深入了解SDK内部Make项目结构,仅需获取构建好的库。
解决方案
方案1:轻量监控关键入口(借助Make自身依赖)
利用Make自带的依赖检查能力,CMake仅监控SDK的核心入口文件和源码目录,内部文件的变化交给Make处理:
修改add_custom_command,添加对SDK Makefile和源码根目录的依赖:
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib COMMAND make -s -f makefile freertos PROFILE=${CMAKE_BUILD_TYPE} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SDK_PATH}/source/kernel/freertos/lib/freertos.${CMAKE_BUILD_TYPE}.lib ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib WORKING_DIRECTORY ${SDK_PATH} # 添加依赖:SDK的Makefile和源码根目录 DEPENDS ${SDK_PATH}/makefile ${SDK_PATH}/source COMMENT "Building FreeRTOS SDK library..." )
原理:
- Makefile修改或源码目录整体变化(新增/删除文件)时,CMake触发重新执行make
- 内部文件修改由Make自动检测(对比目标文件与源文件时间戳)
- 无需枚举所有源文件,开销极小
方案2:Stamp文件精确触发(推荐)
让SDK的Make脚本生成一个时间戳标记文件,CMake仅依赖该标记,内部依赖完全由Make处理:
- 修改SDK的Makefile(有权限情况下),添加stamp生成规则:
# 在freertos库构建规则后添加 freertos: freertos.${PROFILE}.lib touch freertos_build_${PROFILE}.stamp # 确保源码变化时更新stamp freertos_build_${PROFILE}.stamp: freertos.${PROFILE}.lib touch $@
- 修改CMake代码,依赖stamp文件:
set(SDK_STAMP_FILE ${SDK_PATH}/freertos_build_${CMAKE_BUILD_TYPE}.stamp) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib ${SDK_STAMP_FILE} COMMAND make -s -f makefile freertos PROFILE=${CMAKE_BUILD_TYPE} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SDK_PATH}/source/kernel/freertos/lib/freertos.${CMAKE_BUILD_TYPE}.lib ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib WORKING_DIRECTORY ${SDK_PATH} DEPENDS ${SDK_PATH}/makefile COMMENT "Building FreeRTOS SDK library..." ) # 临时目标同时依赖库文件和stamp add_custom_target(${LIB_NAME}-temp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib ${SDK_STAMP_FILE})
原理:
- 源码变化时Make自动重构库并更新stamp时间戳
- CMake通过监控stamp文件,精准触发后续步骤
- 完全无需关心SDK内部文件结构
方案3:自动扫描源码目录(无权限修改Makefile时用)
如果无法修改SDK的Makefile,让CMake自动扫描源码文件,用CONFIGURE_DEPENDS避免手动维护列表:
# 自动扫描SDK源码,CONFIGURE_DEPENDS让CMake每次构建时重新扫描 file(GLOB_RECURSE SDK_SOURCES CONFIGURE_DEPENDS ${SDK_PATH}/source/**/*.c ${SDK_PATH}/source/**/*.h ) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib COMMAND make -s -f makefile freertos PROFILE=${CMAKE_BUILD_TYPE} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SDK_PATH}/source/kernel/freertos/lib/freertos.${CMAKE_BUILD_TYPE}.lib ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib WORKING_DIRECTORY ${SDK_PATH} DEPENDS ${SDK_PATH}/makefile ${SDK_SOURCES} COMMENT "Building FreeRTOS SDK library..." )
注意:
CONFIGURE_DEPENDS会在每次构建时重新扫描目录,对大型SDK有轻微性能开销,但远低于手动维护文件的成本- 能精准检测任何源码文件的修改
内容的提问来源于stack exchange,提问作者NetoBF
相关产品推荐
相关产品推荐

