修改 CMakeLists.txt 后需手动重运行 cmake ..,因其构建系统不会自动重配置;最小配置只需三行:cmake_minimum_required、project 和 add_executable;应优先用 target_include_directories 而非 include_directories,并通过 target_compile_features 显式声明 C++ 标准特性。

为什么 CMakeLists.txt 改了却没生效
常见现象是修改了 CMakeLists.txt,但执行 make 或构建后,新增的源文件不编译、链接选项没加进去、甚至 cmake .. 都报错——根本原因是 CMake 的构建系统默认**不会自动重运行 cmake 命令**。它只在首次配置时解析 CMakeLists.txt,之后生成的 Makefile 或 VS 工程是静态快照。
- 每次修改
CMakeLists.txt后,必须进入build/目录重新运行cmake ..(或cmake -G "Unix Makefiles" ..) - 若只是改了源码(如
main.cpp),直接make即可;但只要动了构建逻辑,就得重配置 - 某些 IDE(如 CLion)会自动触发重配置,但 VS Code + CMake Tools 插件默认不会,需手动点击 “Build” 旁的刷新按钮或运行
CMake: Clean Rebuild
最简可用的 CMakeLists.txt 长什么样
不必一上来就写满 find_package、target_compile_features,一个能编译单个 main.cpp 的最小配置只需三行:
cmake_minimum_required(VERSION 3.10) project(hello LANGUAGES CXX) add_executable(hello main.cpp)
-
cmake_minimum_required必须放在第一行,版本选3.10足够覆盖 GCC 7+/Clang 6+/MSVC 2017+ -
project()中显式声明LANGUAGES CXX,否则 CMake 可能默认只启用 C,导致 C++17 特性(如std::optional)被禁用 -
add_executable()的第一个参数是目标名,不一定要和源文件同名,但后续链接或 install 时都用这个名
include_directories 和 target_include_directories 到底该用哪个
旧教程常写 include_directories(./include),但它会让所有后续 add_executable / add_library 都隐式包含该路径,污染全局作用域,极易引发头文件冲突或误包含。
- 始终优先用
target_include_directories(,例如:PRIVATE ) add_executable(app main.cpp) target_include_directories(app PRIVATE ./include)
-
PRIVATE表示仅该 target 编译时可见;用PUBLIC时,依赖它的其他 target 也会继承此路径(适合库头文件导出) - 绝对避免在 project 根目录用
include_directories,除非你明确需要跨整个项目的“兜底包含”(极少见)
如何让 CMake 正确识别 C++17 或更高标准
只写 set(CMAKE_CXX_STANDARD 17) 不够:它只影响新创建的 target,且不保证编译器实际启用对应 flag(比如 GCC 还要加 -std=gnu++17)。
立即学习“C++免费学习笔记(深入)”;
- 正确做法是结合
set_property或直接设 target 属性:set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 拒绝降级 set(CMAKE_CXX_EXTENSIONS OFF) # 用标准 std=... 而非 gnu=...
add_executable(app main.cpp) set_property(TARGET app PROPERTY CXX_STANDARD 17) set_property(TARGET app PROPERTY CXX_STANDARD_REQUIRED ON)
- 更推荐的方式是用
target_compile_features()显式声明所需特性:target_compile_features(app PRIVATE cxx_std_17 cxx_optional)
,CMake 会自动推导并设置最低标准 - 注意:
CMAKE_CXX_STANDARD是全局变量,影响所有后续 target;而set_property或target_compile_features是 per-target 的,更安全
CMake 的坑不在语法多难,而在“隐式行为”——比如全局 include、缓存变量残留、target 属性未显式继承。真正卡住人的,往往是改了一行 CMakeLists.txt 后,忘了删 build/CMakeCache.txt 或没重跑 cmake ..。











