CMake本身不编译代码,只生成构建文件;真正编译由make、ninja或msbuild执行。入门门槛低,但精通难点在于路径管理、目标属性和生成器表达式。

直接说结论:CMake 本身不编译代码,只生成构建文件;真正编译靠 make、ninja 或 msbuild。入门门槛低,但“精通”卡点在路径管理、目标属性和生成器表达式上。
cmake .. 和 cmake -S . -B build 有啥区别?
本质是同一操作,但后者更现代、更安全。前者隐式把当前目录当构建目录(CMAKE_BINARY_DIR == CMAKE_CURRENT_SOURCE_DIR),容易污染源码;后者强制分离源码与构建产物。
-
cmake ..:在build/目录里执行时,会向上找上级目录的CMakeLists.txt,但缓存(CMakeCache.txt)写在当前目录,一旦误删或混用易出错 -
cmake -S . -B build:明确指定源码目录(-S)和构建目录(-B),CMake 自动创建build/并把所有中间文件锁死在里面 - Windows 用户尤其要注意:Visual Studio 生成器下,
cmake ..可能因路径含空格或中文触发CMake Error: The source directory ".../my project" does not exist.
add_executable() 里传相对路径还是绝对路径?
必须传相对于当前 CMakeLists.txt 所在目录的相对路径。CMake 不支持 add_executable(app /abs/path/main.cpp) 这种写法,会报 file not found 或静默忽略。
- 正确写法:
add_executable(app main.cpp src/utils.cpp),前提是这些文件真正在当前 CMakeLists.txt 同级或子目录下 - 跨目录引用要配合
add_subdirectory():比如lib/下有自己独立的CMakeLists.txt,就得先写add_subdirectory(lib),再用target_link_libraries(app PRIVATE mylib) - 常见坑:
add_executable(app ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp)看似合理,但会导致 Ninja 构建失败 —— 因为 Ninja 要求所有源文件路径必须可被构建系统追踪,而变量展开后的绝对路径破坏了这一机制
为什么 set(CMAKE_CXX_STANDARD 17) 没生效?
这条命令只设置默认标准,但不会自动应用到已有 target。它只对后续用 add_executable() 或 add_library() 创建的目标起作用,且会被 target 级别的属性覆盖。
- 安全写法是:在
add_executable()之后立刻加set_property(TARGET app PROPERTY CXX_STANDARD 17) - 或者统一用
target_compile_features(app PRIVATE cxx_std_17),它还能做编译器能力检查 - 注意
CMAKE_CXX_STANDARD_REQUIRED和CMAKE_CXX_EXTENSIONS:前者设为ON强制拒绝不支持该标准的编译器;后者设为OFF可禁用 GNU 扩展(如__attribute__),提升跨平台兼容性 - 如果用了
find_package(Threads REQUIRED),线程库链接可能依赖 C++ 标准版本,此时标准没对齐会导致undefined reference to pthread_create
找不到第三方库?别急着改 CMAKE_PREFIX_PATH
多数情况下不是路径问题,而是 find_package() 的调用时机或模式不对。CMake 查找逻辑是先找 config 模式(*Config.cmake),再 fallback 到 module 模式(Find*.cmake),两者行为差异极大。
- OpenCV、Qt 等现代库优先走 config 模式,要求你提供
OpenCV_DIR或Qt5_DIR指向其lib/cmake/opencv4/这类目录,而不是库文件所在路径 - 像 zlib、libpng 这类传统库走 module 模式,依赖
CMAKE_LIBRARY_PATH和CMAKE_INCLUDE_PATH,但更推荐用find_package(zlib REQUIRED)+target_link_libraries(app PRIVATE ZLIB::ZLIB),避免手动拼include_directories() - 最容易被忽略的一点:
find_package()必须在add_executable()之前调用,否则target_link_libraries()会报target "app" not found
真正难的从来不是语法,而是理解 CMake 如何把 CMakeLists.txt 映射成 Makefile/Ninja 规则 —— 比如 INTERFACE 属性怎么影响头文件传播,$<build_interface></build_interface> 生成器表达式在 install 时如何切换路径。这些细节不踩几次坑,光看文档根本记不住。


















