CMake遍历子目录指定模式文件时变量仅保留最后一个文件的问题
问题分析与解决方案
问题现象
调用ListFilesInSubmodule两次分别收集.c和.h文件后,PLUGIN_STRUCTURES仅保留每次调用的最后一个文件,最终仅留存第二次调用的最后一个.h文件。
问题根源
- 原代码中,循环内调用
set(PLUGIN_STRUCTURES ${PLUGIN_STRUCTURES} "${_file}" PARENT_SCOPE)时,函数作用域内未定义PLUGIN_STRUCTURES,导致每次都将父作用域变量覆盖为单个文件。 - 修改后的代码里,每次函数调用都会初始化空的
_Temp变量,收集当前模式的文件后直接覆盖父作用域的PLUGIN_STRUCTURES,第一次调用的结果被第二次完全覆盖。
解决方案
方案1:修复原函数的作用域问题
让函数接收输出变量名作为参数,先读取父作用域的已有值,追加新文件后再写回父作用域:
function(ListFilesInSubmodule directory_path pattern output_var) # 读取父作用域中输出变量的当前值 set(_current_files "") if(DEFINED ${output_var}) set(_current_files "${${output_var}}") endif() if(WIN32) execute_process(COMMAND powershell -c "Get-ChildItem -Path '${directory_path}' -Recurse -Filter '${pattern}' -File | ForEach-Object { Write-Host $_.FullName }" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE _Files OUTPUT_STRIP_TRAILING_WHITESPACE ) else() execute_process(COMMAND bash -c "find '${directory_path}' -type f -name '${pattern}'" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE _Files OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() if(NOT _Files) message(STATUS "No files found matching pattern '${pattern}' in '${directory_path}'.") else() string(REPLACE "\n" ";" _FilesList "${_Files}") foreach(_file IN LISTS _FilesList) if(EXISTS "${CMAKE_SOURCE_DIR}/${_file}") list(APPEND _current_files "${_file}") message(STATUS "File is found '${_file}'.") else() message(WARNING "File is possibly not found '${_file}'.") endif() endforeach() endif() # 将更新后的列表写回父作用域 set(${output_var} "${_current_files}" PARENT_SCOPE) endfunction()
调用方式:
ListFilesInSubmodule("libs/subhook" "*.c" PLUGIN_STRUCTURES) ListFilesInSubmodule("libs/subhook" "*.h" PLUGIN_STRUCTURES) message(STATUS "PLUGIN_STRUCTURES : ${PLUGIN_STRUCTURES}")
方案2:改用CMake内置file(GLOB_RECURSE)(推荐)
CMake自带跨平台的递归文件查找命令,无需手动处理shell逻辑,更可靠:
file(GLOB_RECURSE PLUGIN_STRUCTURES "${CMAKE_SOURCE_DIR}/libs/subhook/*.c" "${CMAKE_SOURCE_DIR}/libs/subhook/*.h" ) # 可选:过滤不存在的文件(GLOB_RECURSE理论不会返回无效路径,仅作容错) foreach(_file IN LISTS PLUGIN_STRUCTURES) if(NOT EXISTS "${_file}") message(WARNING "File not found: ${_file}") list(REMOVE_ITEM PLUGIN_STRUCTURES "${_file}") endif() endforeach() message(STATUS "PLUGIN_STRUCTURES : ${PLUGIN_STRUCTURES}")
关键说明
- 方案1通过参数化输出变量,实现灵活的变量操作,同时避免作用域导致的覆盖问题。
- 方案2符合CMake最佳实践,若需自动感知文件新增(无需手动重新配置),可使用
file(GLOB_RECURSE ... CONFIGURE_DEPENDS)(CMake 3.12+支持)。
内容的提问来源于stack exchange,提问作者Zorono
相关产品推荐
相关产品推荐

