CMake

CMake

01 简介

CMake 是一个 跨平台的构建系统生成工具通常被称为 元构建工具(meta-build system),这个说法很贴切。

CMake 允许开发者编写一种 平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到 Write once, run everywhere

它自己并不直接负责怎么编译每个 .cpp,而是:

  • 你用 CMakeLists.txt 描述:有哪些源码、要生成哪些库/可执行文件、依赖哪些第三方库、编译选项是什么……
  • CMake 再根据你的平台与选择的生成器(Generator),生成具体的构建系统文件比如:
    • Linux/macOS:MakefileNinja 构建文件
    • Windows:Visual Studio .sln/.vcxproj
    • MacOS:Xcode 工程等

CMake 写一次构建描述,然后在不同平台生成对应的构建工程/脚本。Make/Ninja/MSBuild 才是实际执行编译的“构建工具”。

最常见的项目结构:

my_project/
├── CMakeLists.txt
├── include/
│   └── hello.h
└── src/
    ├── main.cpp
    └── hello.cpp

02 最小例子

一个最基本的 CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(MyProject)
set(CMAKE_CXX_STANDARD 17)
add_executable(my_app
    src/main.cpp
    src/hello.cpp
)
target_include_directories(my_app PRIVATE include)
target_include_directories(my_app PRIVATE include)

这条 CMake 命令的作用是:为目标 my_app 指定头文件搜索路径(-I),并且该路径仅在 my_app 内部生效。

具体拆解如下:

  • my_app:目标名称(通常由 add_executableadd_library 创建)。
  • PRIVATE:可见性范围。表示 include 目录下的头文件仅供 my_app 自身编译时使用。如果有其他目标链接了 my_app,不会继承该头文件路径。
  • include:头文件所在的目录路径(支持相对路径或绝对路径,此处为当前源码目录下的 include 文件夹)。

可见性对比小结:

  • PRIVATE:仅自己用(最常用,避免污染其他模块)。
  • INTERFACE:自己不用,只给依赖自己的模块用(常见于 header-only 库)。
  • PUBLIC:自己和依赖自己的模块都要用。

然后在项目根目录执行:

cmake -S . -B build
cmake --build build

这两条命令是现代 CMake 构建项目的标准两步走工作流:第一步配置与生成,第二步执行编译

  • cmake -S . -B build(配置与生成阶段)
    • -S .:指定源码目录(Source directory)为当前目录 .(寻找 CMakeLists.txt)。
    • -B build:指定构建目录(Build directory)为 build 文件夹(如果不存在会自动创建)。
    • 作用:检查编译器环境、解析配置逻辑,并在 build/ 目录下生成对应平台的构建文件(如 Makefile、Ninja 规则或 VS 工程)。
  • cmake --build build(编译构建阶段)
    • --build build:让 CMake 调用底层构建工具去编译 build 目录中的工程。
    • 作用:真正触发源码编译并链接生成可执行文件或库。

核心优势:

  1. 跨平台统一:无论底层用的是 makeninja 还是 MSBuild,第二条命令写法完全一致。
  2. 源码树外构建(Out-of-source build):所有编译生成的中间文件都在 build/ 里,清理项目只需删除该文件夹,完全不会污染源码。

--build 后面紧跟的参数是构建输出目录的路径(即前面 -B 生成的文件所在位置)。

除了指定目录,--build 后面还可以追加常用的控制参数:

  • 指定构建目标--target <name>(简写 -t
    • 仅编译某个特定的目标,例如:cmake --build build --target my_app
  • 开启多核并行编译-j <n>--parallel <n>
    • 加快编译速度(如 8 线程):cmake --build build -j 8
  • 指定构建配置(多配置生成器如 VS/Xcode)--config <Debug|Release>
    • 决定编译 Release 还是 Debug 版本:cmake --build build --config Release
  • 清理构建产物--clean-first
    • 编译前先清空旧的目标文件。

cmake --build build 内部已经自动帮你调用了底层的 make(或 Ninja / MSBuild),并且完成了全部的编译和链接工作。

编译完成后通常会得到:

build/
└── my_app

后续操作只需要直接运行生成的可执行程序:

  • 在根目录直接运行

    ./build/my_app        # Linux / macOS
    .\build\Debug\my_app.exe  # Windows (若使用 MSBuild)
    
  • 或者进入 build 目录运行

    cd build
    ./my_app
    

cd build && make 是早期的传统写法;现代 CMake 用 cmake --build build 一条命令就全包了,跨平台且不需要手动切换目录。


CMake 中最常用的几个命令可以先记住:

  • project(MyProject):定义项目
  • add_executable(app main.cpp):生成可执行程序

如果你要生成库:

add_library(mylib
    src/a.cpp
    src/b.cpp
)

然后给程序链接这个库:

target_link_libraries(app PRIVATE mylib)

可以理解为:

mylib
  ↑
  │ 链接
 app

添加头文件路径:

target_include_directories(app PRIVATE include)

相当于告诉编译器:去 include/ 目录找 .h/.hpp 文件。


如果项目稍微正规一些,一般会这样写:

cmake_minimum_required(VERSION 3.16)

project(MyProject
    VERSION 1.0
    LANGUAGES CXX
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(core
    src/hello.cpp
)

target_include_directories(core
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)

add_executable(app
    src/main.cpp
)

target_link_libraries(app
    PRIVATE core
)

依赖关系就是:

hello.cpp
   │
   ▼
 core 库
   │
   ▼
 main.cpp + core
   │
   ▼
  app

你还会经常看到:

find_package(...)

这是用来寻找第三方库的,例如:

find_package(OpenCV REQUIRED)
target_link_libraries(app PRIVATE ${OpenCV_LIBS})

03 参考资料

posted @ 2026-09-08 19:59  光風霽月  阅读(6)  评论(0)    收藏  举报