CMake项目中如何在文件夹文件变更时自动执行自定义脚本?
解决CMake GLOB+自定义命令无法自动响应文件增删的问题
问题根源
用file(GLOB CONFIGURE_DEPENDS)确实能让CMake在手动重新配置时更新文件列表,但构建阶段(比如make)不会自动触发CMake重新配置。这就导致:
- 增删图片后,Makefile里的自定义命令依赖还是旧的文件列表,不会触发脚本重新运行
- 即使CMake打印
GLOB mismatch!,也只是提醒你需要重新配置,不会自动执行
解决方案:两种可靠实现方式
方式一:让脚本自己判断是否需要更新(推荐)
核心思路:让自定义命令同时依赖图片文件和目录本身(目录的修改时间在增删文件时会变化),然后在脚本里对比文件列表的哈希或者目录时间,只在需要的时候重新生成generated.h。
修改CMake配置
set(IMAGE_DIR ${CMAKE_SOURCE_DIR}/images) set(OUT_FILE ${CMAKE_BINARY_DIR}/generated.h) # 保留CONFIGURE_DEPENDS确保配置时更新文件列表 file(GLOB IN_FILES CONFIGURE_DEPENDS ${IMAGE_DIR}/*.png) add_custom_command( OUTPUT ${OUT_FILE} # 把目录、输出文件、当前图片列表传给脚本 COMMAND ${CMAKE_SOURCE_DIR}/command-to-generate-build-artifact.sh ${IMAGE_DIR} ${OUT_FILE} ${IN_FILES} # 同时依赖图片文件和目录本身,目录变化时触发命令 DEPENDS ${IN_FILES} ${IMAGE_DIR} COMMENT "Generating image header..." ) add_custom_target(generate_image_data ALL DEPENDS ${OUT_FILE}) add_dependencies(project generate_image_data)
修改生成脚本(command-to-generate-build-artifact.sh)
在脚本开头加入判断逻辑,只有当文件列表变化或者输出文件不存在时,才重新生成:
#!/bin/bash IMAGE_DIR=$1 OUT_FILE=$2 shift 2 # 获取当前传入的图片列表 CURRENT_FILES="$@" # 用MD5哈希记录当前文件列表的状态 HASH_FILE="${OUT_FILE}.hash" CURRENT_HASH=$(echo "$CURRENT_FILES" | md5sum | cut -d' ' -f1) # 判断是否需要更新 if [ ! -f "$OUT_FILE" ] || [ ! -f "$HASH_FILE" ] || [ "$CURRENT_HASH" != "$(cat "$HASH_FILE" 2>/dev/null)" ]; then # 这里放你原来的生成逻辑,比如生成包含文件名的宏 echo "#pragma once" > "$OUT_FILE" echo "#define IMAGE_COUNT $#" >> "$OUT_FILE" echo "const char* IMAGE_FILES[] = {" >> "$OUT_FILE" for file in $CURRENT_FILES; do echo " \"$(basename "$file")\"," >> "$OUT_FILE" done echo "};" >> "$OUT_FILE" # 更新哈希文件 echo "$CURRENT_HASH" > "$HASH_FILE" else # 没有变化,触摸输出文件保持时间戳最新,避免后续不必要的构建 touch "$OUT_FILE" fi
方式二:让构建系统自动触发CMake重新配置
如果不想修改脚本,可以让Makefile在检测到目录变化时自动重新运行CMake配置,更新依赖列表。
修改CMake配置
set(IMAGE_DIR ${CMAKE_SOURCE_DIR}/images) set(OUT_FILE ${CMAKE_BINARY_DIR}/generated.h) # 用一个标记文件追踪目录变化 set(DIR_MARKER ${CMAKE_BINARY_DIR}/.images_dir_marker) # 生成标记文件,依赖目录本身 add_custom_command( OUTPUT ${DIR_MARKER} COMMAND ${CMAKE_COMMAND} -E touch ${DIR_MARKER} DEPENDS ${IMAGE_DIR} COMMENT "Checking image directory changes..." ) # 扫描图片文件 file(GLOB IN_FILES CONFIGURE_DEPENDS ${IMAGE_DIR}/*.png) # 自定义命令同时依赖标记文件,目录变化时触发重新运行CMake add_custom_command( OUTPUT ${OUT_FILE} COMMAND ${CMAKE_SOURCE_DIR}/command-to-generate-build-artifact.sh ${OUT_FILE} ${IN_FILES} DEPENDS ${IN_FILES} ${DIR_MARKER} COMMENT "Generating image header..." ) add_custom_target(generate_image_data ALL DEPENDS ${OUT_FILE}) add_dependencies(project generate_image_data) # 添加一个目标,检测目录变化后自动重新配置CMake configure_file( ${CMAKE_SOURCE_DIR}/cmake/CheckDirChange.cmake.in ${CMAKE_BINARY_DIR}/CheckDirChange.cmake @ONLY ) add_custom_target(check_dir_change COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/CheckDirChange.cmake DEPENDS ${DIR_MARKER} ) add_dependencies(generate_image_data check_dir_change)
创建CheckDirChange.cmake.in文件
在项目根目录下新建cmake文件夹,创建该文件:
set(IMAGE_DIR @IMAGE_DIR@) set(DIR_MARKER @DIR_MARKER@) # 获取目录和标记文件的修改时间 file(TIMESTAMP ${IMAGE_DIR} DIR_TIMESTAMP) if(EXISTS ${DIR_MARKER}) file(TIMESTAMP ${DIR_MARKER} MARKER_TIMESTAMP) else() set(MARKER_TIMESTAMP "") endif() # 目录变化时重新运行CMake配置 if(NOT DIR_TIMESTAMP STREQUAL MARKER_TIMESTAMP) execute_process( COMMAND ${CMAKE_COMMAND} -E touch ${DIR_MARKER} COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) endif()
验证效果
- 新增/删除/重命名
images下的png文件,直接运行make(或VS Code里的构建),脚本会自动重新生成generated.h - 修改图片内容,脚本也会因为文件时间戳变化触发更新
内容的提问来源于stack exchange,提问作者Bri Bri
相关产品推荐
相关产品推荐

