add_executable()直接列源文件仅适用于单文件或极小项目;现代CMake推荐用target_sources()解耦目标定义与源文件管理,并配合CONFIGURE_DEPENDS和source_group()提升可维护性。

add_executable() 里直接列源文件最简单,但只适合单文件或极小项目
刚上手时最容易想到的就是在 add_executable() 里一股脑写全所有 .cpp 文件,比如:add_executable(myapp main.cpp utils.cpp network.cpp)。这确实能跑通,但一旦源文件超过 5 个,每次增删都要手动改这行,很快就会漏掉、拼错路径、或者把头文件误加进去导致编译失败。
更麻烦的是,这种写法无法区分哪些是实现文件(.cpp),哪些是头文件(.h),而 IDE(比如 VS)依赖正确分类才能显示在“Header Files”和“Source Files”分组下——你手动加的 .h 文件会被当成编译单元,可能触发重复定义错误。
- 只推荐用于原型验证或单文件练习
- 绝对不要在团队项目或持续迭代的代码中长期使用
- 如果硬要用,确保列表里只有
.cpp、.c等可编译后缀,别混入.h
target_sources() 是现代 CMake 添加源文件的标准方式
target_sources() 的核心价值不是“多写一行”,而是把“目标定义”和“源文件归属”解耦。它允许你在 add_executable() 之后,按需、分批、带作用域地添加文件,尤其适合子目录管理、条件编译、或生成源文件(如 protobuf 编译产出的 pb.cc)。
关键点在于作用域参数:PRIVATE 最常用,表示这些源只参与当前目标编译;PUBLIC 会把头文件暴露给依赖者(慎用,一般用 target_include_directories() 更清晰);INTERFACE 基本不用在 .cpp 上。
- 基础写法:
add_executable(myapp)后接target_sources(myapp PRIVATE main.cpp utils.cpp) - 头文件建议也加进来(仅为了 IDE 分组):
target_sources(myapp PUBLIC main.cpp utils.cpp utils.h) - 跨子目录时路径必须写全:
target_sources(myapp PRIVATE src/main.cpp libs/net/conn.cpp)
file(GLOB_RECURSE) 自动收集文件,但 CONFIGURE_DEPENDS 不可省略
想让 CMake 像 VS 那样“自动感知新增文件”,就得用 file(GLOB_RECURSE)。但它默认是静态快照——第一次 configure 后新增的 .cpp 文件,不重新运行 cmake 就不会被加入构建。这就是为什么 CONFIGURE_DEPENDS 必须显式启用:
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "src/*.cpp" "src/*.h")target_sources(myapp PRIVATE ${sources})
- 不加
CONFIGURE_DEPENDS:新增文件后必须手动删 build 目录再 cmake,否则无效 -
GLOB_RECURSE会钻进build/目录找文件,如果 build 目录里有临时生成的.cpp(比如 moc 或 protobuf 输出),会被误加——务必把 build 目录排除在外,或改用file(GLOB)限定一级目录 - Android NDK 等受限环境可能不支持
CONFIGURE_DEPENDS,此时只能退回到手动维护变量
source_group() 让 VS 或 CLion 显示文件夹结构
即使源文件物理路径是 src/network/client.cpp 和 src/utils/log.h,默认生成的 VS 解决方案里它们仍平铺在根目录。要还原文件夹视图,必须配合 source_group():
source_group("Source Files\Network" FILES src/network/client.cpp)source_group("Header Files\Utils" FILES src/utils/log.h)
- 路径分隔符用双反斜杠
\(Windows)或正斜杠/(跨平台),不能混用 - 分组名是纯显示用字符串,和磁盘路径无关;同一分组可跨多个物理路径
- 这个命令必须在
add_executable()或add_library()之后、target_sources()之前调用才生效
.h 文件该用 PUBLIC 还是 INTERFACE,CONFIGURE_DEPENDS 在 CI 环境是否可靠,source_group() 的层级怎么和团队约定对齐,这些细节不踩几次坑很难记住。


















