Cmake的File命令收集源代码方法总结

1.概述:

在CMake中,可以使用file命令配合通配符来发现一组C源代码文件,然后将其添加到目标中。但是,CMake官方文档通常建议明确列出源文件,而不是使用通配符,因为如果添加了新文件,CMake可能不会自动重新生成构建系统(除非采取额外措施)。然而,在某些情况下,使用通配符收集源文件也是可行的,特别是当源文件经常变化,且你希望自动包含新文件时。以下是一些使用file命令发现C源代码文件的常见方法:

1.1 使用GLOB或GLOB_RECURSE

GLOB: 在指定目录中匹配文件,不递归子目录。
GLOB_RECURSE: 递归匹配指定目录及其子目录中的文件。

1.2.源文件更新

注意:使用通配符时,如果添加或删除源文件,可能需要重新运行CMake才能更新构建系统。为了确保在添加新文件时构建系统能够更新,可以将源文件列表的生成与一个变量相关联,并在每次构建前检查是否更新。但是,这并不能完全自动检测,因为CMake只在CMakeLists.txt改变时重新运行。一种常见的做法是使用CONFIGURE_DEPENDS选项(在较新版本的CMake中可用),它会在每次构建时检查文件系统是否变化,但这样可能会降低构建速度。

2.方法汇总:

下面是一个简单例子,给读者一个整体上使用方法:
假设我们有如下目录结构:
project/
src/
main.c
foo.c
bar.c
subdir/
baz.c

我们想要收集所有.c文件。
使用GLOB(非递归):
file(GLOB SRC_FILES "src/.c")
使用GLOB_RECURSE(递归):
file(GLOB_RECURSE SRC_FILES "src/
.c")
然后,我们可以将SRC_FILES变量用于创建可执行文件或库,例如:
add_executable(myapp ${SRC_FILES})
但是,请注意,GLOB_RECURSE会递归所有子目录,可能会匹配到我们不希望包含的源文件(比如测试文件或示例代码)。因此,需要谨慎使用。另外,由于通配符匹配可能不是所有情况下都可靠,特别是当文件被重命名或删除时,构建系统可能不会自动更新。因此,CMake官方推荐明确列出源文件。但是,如果你决定使用通配符,并且使用CMake 3.12或更高版本,可以考虑使用CONFIGURE_DEPENDS选项,例如:
file(GLOB SRC_FILES CONFIGURE_DEPENDS "src/*.c")
这会在每次构建时检查目录中的文件是否变化,并更新变量SRC_FILES。但是,这可能会增加构建时间,因为每次构建都需要检查文件系统。

2.1 常用示例

2.1.1 基础 GLOB 用法

*匹配当前目录所有 .c 文件*
file(GLOB C_SOURCES "*.c")
# 匹配当前目录所有 .c 和 .h 文件
file(GLOB ALL_SOURCES "*.c" "*.h")
# 创建可执行文件
add_executable(my_app ${C_SOURCES})

2.1.2 递归搜索 GLOB_RECURSE

# 递归搜索所有子目录中的 .c 文件
file(GLOB_RECURSE C_SOURCES_RECURSE "*.c")
# 递归搜索多个目录
file(GLOB_RECURSE SOURCES 
    "src/*.c"
    "lib/*.c"
    "utils/*.c"
)
add_executable(my_app ${SOURCES})

2.1.3 排除特定文件或目录

# 先收集所有文件
file(GLOB_RECURSE ALL_SOURCES "*.c")

# 使用 list 过滤掉不需要的文件
list(FILTER ALL_SOURCES EXCLUDE REGEX ".*/test/.*")
list(FILTER ALL_SOURCES EXCLUDE REGEX ".*/third_party/.*")
list(FILTER ALL_SOURCES EXCLUDE REGEX ".*_mock\\.c$")
add_executable(my_app ${ALL_SOURCES})

2.1.4 相对路径处理

# 获取相对于当前源目录的路径
file(GLOB_RECURSE ABS_SOURCES "*.c")

# 转换为相对于 CMAKE_CURRENT_SOURCE_DIR 的路径
set(REL_SOURCES)
foreach(src ${ABS_SOURCES})
    file(RELATIVE_PATH rel_src ${CMAKE_CURRENT_SOURCE_DIR} ${src})
    list(APPEND REL_SOURCES ${rel_src})
endforeach()
message("相对路径源文件: ${REL_SOURCES}")

2.1.5 组合使用示例:

# 定义源文件目录
set(SRC_DIRS src lib include)

# 收集所有源文件
set(ALL_SOURCES)
foreach(dir ${SRC_DIRS})
    file(GLOB_RECURSE DIR_SOURCES 
        "${dir}/*.c"
        "${dir}/*.h"
    )
    list(APPEND ALL_SOURCES ${DIR_SOURCES})
endforeach()

# 去重(可能在不同目录中有相同文件)
list(REMOVE_DUPLICATES ALL_SOURCES)
# 创建目标
add_executable(my_program ${ALL_SOURCES})

2.1.6 使用 CONFIGURE_DEPENDS(CMake 3.12+)

# 使用 CONFIGURE_DEPENDS 可以在构建时自动检测新文件
file(GLOB C_SOURCES CONFIGURE_DEPENDS "*.c")
file(GLOB HEADER_FILES CONFIGURE_DEPENDS "*.h")

add_executable(my_app ${C_SOURCES})
target_include_directories(my_app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

2.1.7 完整项目示例

cmake_minimum_required(VERSION 3.10)
project(MyCProject C)

# 设置 C 标准
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# 定义源文件类型
set(C_SOURCE_EXTENSIONS "*.c")
set(C_HEADER_EXTENSIONS "*.h")

# 收集应用源代码
file(GLOB_RECURSE APP_SOURCES
    "app/${C_SOURCE_EXTENSIONS}"
)

# 收集库源代码
file(GLOB_RECURSE LIB_SOURCES
    "libs/${C_SOURCE_EXTENSIONS}"
)

# 排除测试文件
set(ALL_SOURCES ${APP_SOURCES} ${LIB_SOURCES})
list(FILTER ALL_SOURCES EXCLUDE REGEX ".*_test\\.c$")
list(FILTER ALL_SOURCES EXCLUDE REGEX ".*/test/.*\\.c$")

# 收集头文件用于包含目录
file(GLOB_RECURSE HEADER_FILES "${C_HEADER_EXTENSIONS}")

# 提取头文件目录
set(INCLUDE_DIRS)
foreach(header ${HEADER_FILES})
    get_filename_component(dir ${header} DIRECTORY)
    list(APPEND INCLUDE_DIRS ${dir})
endforeach()
list(REMOVE_DUPLICATES INCLUDE_DIRS)

# 创建可执行文件
add_executable(${PROJECT_NAME} ${ALL_SOURCES})

# 设置包含目录
target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIRS})

3.替代方案:显式列出文件
# 更好的做法:显式列出文件
set(MY_SOURCES
    src/main.c
    src/utils.c
    src/config.c
    lib/math.c
    lib/string.c
)

# 或者从文件中读取列表
file(STRINGS "sources.txt" SOURCE_LIST)
add_executable(my_app ${SOURCE_LIST})

3.使用File命令收集源代码的总结

  1. 生产项目:显式列出所有源文件

  2. 原型/快速开发:使用 file(GLOB) 但配合 CONFIGURE_DEPENDS

  3. 大型项目:混合使用 - 核心代码显式列出,生成的代码使用 GLOB

  4. 始终过滤:排除测试文件、第三方代码等

  5. 考虑性能:递归搜索可能在大项目中较慢

posted @ 2026-01-08 15:44  退休人生  阅读(68)  评论(0)    收藏  举报