aux_source_directory 仅在配置阶段扫描一次,不感知新增文件,导致链接错误;应配合 target_sources() 增量注册,并避免直接用于 add_executable,file(GLOB) 同样不可靠且更危险。

aux_source_directory 会漏掉新添加的 .cpp 文件
aux_source_directory 只在 CMake 配置阶段(即运行 cmake 命令时)扫描一次目录,它不会自动感知后续手动新增的源文件。如果你在 build/ 目录外编辑并保存了一个新 .cpp 文件,再直接 make 或 ninja,CMake 不会重新运行、也不会把该文件加进编译列表——结果就是链接时报 undefined reference,但连编译警告都没有。
常见误用场景:团队协作中有人加了新文件,却忘了告诉别人要重跑 cmake;或者 CI 脚本里只执行 make,没触发 CMake 重新配置。
- 它只匹配当前目录(不递归子目录),除非你显式对每个子目录都调一次
- 匹配规则是 shell glob,不是正则:
*.cpp可以,*.cc|*.cpp不行 - 不会包含头文件(
.h/.hpp),这点没问题,但有人误以为能自动处理头依赖 - 生成的变量是普通 list,不能直接传给
target_sources()(后者要求命名 target)
正确写法:用 list(APPEND) + target_sources() 替代 add_executable() 的全量参数
现代 CMake(3.10+)推荐显式管理源文件,但若真想“半自动”,应避免把 aux_source_directory 结果直接塞进 add_executable(myapp main.cpp ${SRC_LIST}) —— 这种写法会让 CMake 无法跟踪源文件变更,IDE(如 CLion)也无法索引新文件。
更可靠的做法是:先用 aux_source_directory 收集,再用 target_sources() 增量注册到已有 target:
立即学习“C++免费学习笔记(深入)”;
add_executable(myapp main.cpp)
aux_source_directory(src SRC_LIST)
target_sources(myapp PRIVATE ${SRC_LIST})
这样 CMake 能识别这些文件属于 myapp,支持 IDE 刷新、依赖自动推导、甚至 Ninja 的增量构建感知。
-
PRIVATE表示这些源只用于本 target,不影响接口 - 如果源文件需要被其他 target include(比如含模板实现),改用
PUBLIC并配合target_include_directories() - 注意
${SRC_LIST}是空字符串时不会报错,但 target 会少文件——建议加message(WARNING "No sources found in src/")辅助调试
为什么不用 file(GLOB)?它比 aux_source_directory 更危险
file(GLOB) 看似更灵活(支持 ** 递归、可过滤子目录),但它和 aux_source_directory 有同一个致命问题:CMake 不监视 glob 匹配结果的变化。更糟的是,file(GLOB) 的结果是静态快照,连配置阶段新增的文件都捕获不到(除非手动删 CMakeCache.txt 强制重扫)。
官方文档明确说:“We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.”
- 哪怕你写了
file(GLOB_RECURSE SRC "*.cpp"),新增src/util/new.cpp后,不重跑cmake就不会编译它 -
file(GLOB)不区分大小写(Windows/macOS 默认行为),可能意外匹配到.CPP或.CpP,而编译器不一定认 - 某些旧版 CMake(** 支持不一致,跨平台 CI 容易出问题
真正省心的做法:用现代 CMake 的源文件自动发现(CMake 3.20+)
CMake 3.20 引入了 file(GLOB_RECURSE ... CONFIGURE_DEPENDS),这个标记会让 CMake 在构建前检查 glob 匹配的文件是否变动,并自动触发重新配置。但它仅在部分生成器(Ninja、Makefiles)上有效,且 Windows MSVC 下不可靠。
更稳的路径是放弃“自动发现”,改用脚本辅助管理——例如写个 Python 小脚本扫描 src/ 下所有 .cpp,输出标准格式的 set(SRC_LIST ...) 到临时 sources.cmake,然后在 CMakeLists.txt 里 include(sources.cmake)。这样既保持 CMakeLists 清洁,又让 IDE 和构建系统都能感知变化。
不过多数项目走到这一步,其实更适合回归显式列表——因为文件增删本就是开发者的主动行为,值得在版本控制里留下明确痕迹。一个清晰的 add_executable(...) 参数列表,比任何自动机制都更易审查、迁移和调试。
最常被忽略的一点:即使用了 CONFIGURE_DEPENDS,CMake 也只检查文件是否存在/被删除,不检查文件内容是否从 .txt 改成了 .cpp——这种边界情况,只有显式声明才真正可靠。


















