强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

CMake 从入门到精通:完整教程 / 第 1 章:CMake 简介与背景

第 1 章:CMake 简介与背景

1.1 什么是 CMake

CMake(Cross-platform Make)是一个开源的、跨平台的构建系统生成器。它本身并不是构建工具,而是生成特定平台的构建文件(如 Unix Makefile、Ninja 文件、Visual Studio 项目文件等)的元构建系统(meta-build system)。

┌─────────────┐     ┌─────────────┐     ┌─────────────────┐     ┌──────────┐
│ CMakeLists  │ ──> │    CMake    │ ──> │ 构建文件         │ ──> │ 可执行文件│
│    .txt     │     │  (配置阶段)  │     │ (Makefile/Ninja) │     │ / 库文件  │
└─────────────┘     └─────────────┘     └─────────────────┘     └──────────┘
     用户编写          cmake ..             cmake --build .          最终产物

1.2 历史与发展

发展时间线

时间 事件
2000 年 Kitware 公司为 ITK(Insight Segmentation and Registration Toolkit)项目开发了 CMake
2001 年 CMake 1.0 发布
2006 年 KDE 4 采用 CMake 作为构建系统,推动了 CMake 的广泛普及
2011 年 CMake 2.8 引入了 target_link_libraries 等现代特性
2014 年 CMake 3.0 发布,引入生成器表达式(Generator Expressions)
2018 年 CMake 3.12 引入 FetchContent 模块
2020 年 CMake 3.19 引入 CMake Presets(预设文件)
2024 年 CMake 3.30 持续改进 C++20 模块支持

项目名称由来

CMake = Cross-platform + Make

最初是为 C 语言设计的,但现在已经支持 C++、CUDA、Objective-C、Fortran、Swift 等多种语言。

1.3 设计理念

CMake 的设计遵循以下核心理念:

1.3.1 声明式而非命令式

CMake 鼓励使用声明式的方式来描述构建目标和依赖关系:

# 声明式:描述"是什么"
add_executable(myapp main.cpp utils.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)
target_include_directories(myapp PRIVATE include)

对比 Makefile 的命令式风格:

# 命令式:描述"怎么做"
myapp: main.o utils.o
	g++ -o myapp main.o utils.o -lfmt -Iinclude

main.o: main.cpp
	g++ -c main.cpp -Iinclude

utils.o: utils.cpp
	g++ -c utils.cpp -Iinclude

1.3.2 目标(Target)为中心

现代 CMake 以 Target 为核心概念。每个目标携带自己的属性(源文件、编译选项、链接依赖、包含目录等),这些属性会自动传递:

# 创建一个库目标
add_library(mylib src/mylib.cpp)
target_include_directories(mylib PUBLIC include/)
target_compile_features(mylib PUBLIC cxx_std_17)

# 创建一个可执行文件目标
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# myapp 会自动获得 mylib 的 include 目录和 C++17 标准

1.3.3 跨平台抽象

CMake 在不同平台上提供统一的接口:

# 这段代码在 Linux、macOS、Windows 上都能正确工作
if(WIN32)
    target_compile_definitions(myapp PRIVATE PLATFORM_WINDOWS)
elseif(APPLE)
    target_compile_definitions(myapp PRIVATE PLATFORM_MACOS)
elseif(UNIX)
    target_compile_definitions(myapp PRIVATE PLATFORM_LINUX)
endif()

1.4 CMake vs 其他构建系统

1.4.1 对比总表

特性 CMake Makefile Ninja Meson Gradle Bazel
跨平台 ⚠️
语言支持 C/C++/CUDA/Fortran/Swift 等 任意 任意 C/C++/Fortran/Rust Java/Kotlin/C++ 任意
依赖管理 FetchContent/vcpkg/Conan 手动 手动 Wrap 内置 Maven 仓库 内置 Bzlmod
IDE 集成 ✅ 非常好 ⚠️ 一般 ✅ 非常好 ⚠️ 一般
学习曲线 中等 极低 中等
构建速度 较快 极快 极快
社区规模 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
采用项目 Qt, LLVM, OpenCV, KDE Linux Kernel Chrome, LLVM(构建) GNOME, systemd Android Google 内部

1.4.2 CMake vs Makefile

方面 CMake Makefile
跨平台 自动生成各平台构建文件 仅限 Unix/MSYS
依赖追踪 自动头文件依赖扫描 需手动或 gcc -MM
可维护性 声明式,易于维护 大型项目维护困难
灵活性 较高,但有抽象层 极高,完全控制
构建速度 配置慢,构建快 构建较慢

Makefile 更适合:内核模块、嵌入式脚本构建等小型或极度定制化场景。

CMake 更适合:跨平台项目、团队协作、需要 IDE 支持的项目。

1.4.3 CMake vs Gradle

方面 CMake Gradle
生态 C/C++/Fortran 原生 Java/Kotlin 原生,C++ 为插件
构建模型 Target 为中心 Task 为中心
依赖管理 通过 vcpkg/Conan Maven 仓库原生支持
增量构建 通过 Ninja 实现 原生支持
配置语言 CMake 脚本语言 Groovy/Kotlin DSL
构建速度 较慢(JVM 启动开销)

Gradle 更适合:Android 开发、Java 为主的项目中包含 C++ 组件。

CMake 更适合:纯 C/C++ 项目、游戏引擎、科学计算库。

1.4.4 CMake vs Meson

方面 CMake Meson
语法 CMake 脚本 Python-like
后端 多种(Make/Ninja/VS) 主要 Ninja
依赖管理 FetchContent/vcpkg/Conan Wrap 系统
社区成熟度 非常成熟 快速成长
配置速度 较快 非常快

1.4.5 CMake vs Bazel

方面 CMake Bazel
适用规模 小到大型 中到超大型
可重现性 一般 极强(沙箱构建)
远程缓存 需额外配置 原生支持
学习成本 中等
部署复杂度 高(需要服务端)

1.5 CMake 的工作流程

CMake 的构建分为两个阶段:

阶段一:配置(Configure)

mkdir build && cd build
cmake ..

此阶段 CMake 执行以下工作:

  1. 读取 CMakeLists.txt 文件
  2. 检测编译器和平台特性
  3. 处理 find_package() 查找依赖
  4. 生成缓存文件 CMakeCache.txt
  5. 生成构建系统文件(如 Makefile 或 build.ninja

阶段二:构建(Build)

cmake --build .
# 或直接
make
# 或
ninja

此阶段使用生成的构建文件来编译和链接源代码。

完整流程图

CMakeLists.txt ──┐
                 │  cmake configure
                 ▼
           ┌───────────┐
           │ CMakeCache │   存储用户选项和检测结果
           │    .txt    │
           └─────┬─────┘
                 │
                 ▼
           ┌───────────┐
           │ 构建文件    │   Makefile / build.ninja / .sln
           └─────┬─────┘
                 │  cmake --build
                 ▼
           ┌───────────┐
           │ 目标产物    │   可执行文件 / 库文件
           └───────────┘

1.6 适用场景

✅ 推荐使用 CMake 的场景

场景 原因
跨平台 C/C++ 项目 CMake 的核心优势
需要 IDE 支持 自动生成 VS/Xcode/CLion 项目
使用 CTest/CDash CMake 原生测试支持
大型项目的依赖管理 FetchContent + vcpkg/Conan
开源库发布 find_package 支持使得下游使用方便
CI/CD 流水线 广泛支持,配置灵活

⚠️ 可能不太适合的场景

场景 建议
纯 Java/Kotlin 项目 使用 Gradle
纯 Python 项目 使用 setuptools/poetry
Android NDK 开发 可以用 CMake,但 Gradle 是更自然的选择
极小项目(单文件) 直接 g++ main.cpp 更简单
需要完全可重现构建 考虑 Bazel

1.7 CMake 的版本演进要点

了解版本特性有助于选择合适的最低版本:

CMake 版本 重要特性
3.0 生成器表达式、cmake_minimum_required 提升
3.1 target_compile_features、C++ 标准设置
3.7 cmake_parse_arguments 内置、Android 支持
3.11 FetchContent 模块
3.13 target_link_directoriesadd_link_options
3.16 target_precompile_headers、Unity Build
3.19 CMake Presets(CMakePresets.json
3.20 cmake_path 命令、预设版本 2
3.21 FILE_SET 支持、C++23 标准
3.24 FIND_PACKAGE_ARGS in FetchContent
3.25 SYSTEM 属性在 FetchContent 中
3.28 C++20 模块支持改进

1.8 业务场景:选择构建系统

场景:新启动的跨平台 C++ 项目

假设你是一个 5 人团队,要开发一个跨 Linux、macOS、Windows 的网络服务框架:

需求分析:
├── 需要跨平台?           → 是
├── 使用 C++17?           → 是
├── 依赖第三方库(OpenSSL, Protobuf)? → 是
├── 团队成员使用不同 IDE?  → 是(VSCode、CLion、Visual Studio)
├── 需要 CI/CD 集成?      → 是
└── 评估结果:              → CMake 是最佳选择 ✅

选择 CMake 的理由

  1. 所有主流 IDE 原生支持
  2. find_package 生态覆盖主要 C++ 库
  3. vcpkg/Conan 提供丰富的包管理
  4. CI 平台(GitHub Actions、GitLab CI)广泛支持

1.9 扩展阅读


下一章:第 2 章 — 安装与环境配置 →