CMake不是编译器或构建工具,而是跨平台构建系统生成器;它读取CMakeLists.txt生成Makefile、VS工程等,再由make/msbuild/ninja执行实际编译。

直接说结论:CMake 不是编译器,也不是构建工具本身,它只负责读 CMakeLists.txt 生成 Makefile、Visual Studio 工程或 Ninja 配置——你真正编译代码的,还是 make、msbuild 或 ninja。搞不清这点,后面所有报错都容易走偏。
cmake .. 和 cmake --build . 到底在做什么
这是新手最常卡住的两步,本质是完全分离的两个阶段:
-
cmake ..是「配置阶段」:CMake 去读上级目录的CMakeLists.txt,检测编译器(g++还是MSVC)、检查依赖路径、解析add_executable和target_link_libraries,最后生成构建系统文件(比如build/Makefile或build/ALL_BUILD.vcxproj) -
cmake --build .是「构建阶段」:CMake 调用底层真正的构建工具(make/ninja/msbuild),去执行编译、链接动作 - 如果
cmake ..报错,说明CMakeLists.txt写错了,或者编译器没装好;如果cmake --build .报错,才是源码或链接问题(比如找不到std::filesystem) - 不要在源码目录里直接运行
cmake .——这叫「内部构建」,会把CMakeCache.txt、CMakeFiles/等中间文件塞进源码树,Git 很难干净地 ignore
add_executable 和 target_include_directories 的作用域陷阱
这两个命令的 scope(作用域)不是全局的,而是绑定到具体 target 的,写错位置就失效:
-
add_executable(myapp src/main.cpp)创建了一个名为myapp的 target;之后所有以target_*开头的命令,必须明确指定这个 target 名,比如target_include_directories(myapp PRIVATE include) -
PRIVATE表示:头文件路径只对myapp自己有效,下游 target(比如另一个可执行文件)不会继承;PUBLIC表示:本 target 暴露的头文件路径,也会被链接它的 target 自动继承;INTERFACE表示:本 target 不编译源码,只提供头文件和链接信息(典型用于 header-only 库) - 常见错误:
include_directories(include)是旧式全局写法,已被弃用;它会让所有后续add_executable都带上这个路径,无法控制粒度,极易污染其他 target - 如果你的
main.cpp里写了#include "foo.h",但foo.h在include/mylib/foo.h,那必须写target_include_directories(myapp PRIVATE ${CMAKE_SOURCE_DIR}/include),否则编译器根本找不到头文件
CMAKE_CXX_STANDARD 为什么有时不生效
设了 CMAKE_CXX_STANDARD 17 却还是报 std::optional 找不到?关键看它在哪设、有没有配对开关:
- 必须在
project()之后设置,否则会被 project 默认值覆盖;推荐紧跟着project()写 - 仅设
CMAKE_CXX_STANDARD不够,必须同时打开CMAKE_CXX_STANDARD_REQUIRED ON,否则 CMake 可能降级为 C++14 编译(尤其在旧编译器上) - 还要关掉扩展:
CMAKE_CXX_EXTENSIONS OFF,否则 GCC/Clang 默认用gnu++17,而 MSVC 不认这个宏,跨平台时行为不一致 - 更稳妥的做法是改用
target_compile_features,比如target_compile_features(myapp PRIVATE cxx_std_17 cxx_optional),它会做实际可用性检测,而不是只加个编译选项 - 注意:这些变量只影响后续定义的 target;如果
add_executable写在set(CMAKE_CXX_STANDARD ...)前面,那这个 executable 就不会被设标准
为什么 find_package(OpenCV) 找不到库
find_package 不是万能的,它依赖路径、命名和版本三重匹配,失败原因往往很隐蔽:
- 先确认 OpenCV 确实装了:
pkg-config --modversion opencv4(Linux/macOS)或查安装目录(Windows 上常见在C:/opencv/build/install) -
find_package(OpenCV REQUIRED)默认找的是OpenCVConfig.cmake或opencv-config.cmake,不是.pc文件;如果只有 pkg-config,得用find_package(PkgConfig)+pkg_check_modules - 如果 OpenCV 是自己编译的,确保编译时加了
-DBUILD_opencv_world=ON并且make install过,否则install/lib/cmake/opencv4/下没有 config 文件 - 可以手动指定路径:
find_package(OpenCV REQUIRED PATHS "C:/opencv/build/install"),但别写死绝对路径,用${CMAKE_SOURCE_DIR}/deps/opencv更可移植 - 找到之后,必须显式链接:
target_link_libraries(myapp PRIVATE ${OpenCV_LIBS}),光find_package不会自动链接
最容易被忽略的一点:CMake 的缓存机制。一旦 cmake .. 成功过,它就把检测结果(比如编译器路径、库位置)记在 build/CMakeCache.txt 里。改了环境变量、换了编译器、重装了库,不删 cache 就可能继续用旧记录——遇到诡异问题,第一反应该删掉整个 build/ 目录重来。


















