强曰为道

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

第 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 对比总表

特性CMakeMakefileNinjaMesonGradleBazel
跨平台⚠️
语言支持C/C++/CUDA/Fortran/Swift 等任意任意C/C++/Fortran/RustJava/Kotlin/C++任意
依赖管理FetchContent/vcpkg/Conan手动手动Wrap内置 Maven 仓库内置 Bzlmod
IDE 集成✅ 非常好⚠️ 一般✅ 非常好⚠️ 一般
学习曲线中等极低中等
构建速度较快极快极快
社区规模⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
采用项目Qt, LLVM, OpenCV, KDELinux KernelChrome, LLVM(构建)GNOME, systemdAndroidGoogle 内部

1.4.2 CMake vs Makefile

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

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

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

1.4.3 CMake vs Gradle

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

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

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

1.4.4 CMake vs Meson

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

1.4.5 CMake vs Bazel

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

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/CDashCMake 原生测试支持
大型项目的依赖管理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.1target_compile_features、C++ 标准设置
3.7cmake_parse_arguments 内置、Android 支持
3.11FetchContent 模块
3.13target_link_directoriesadd_link_options
3.16target_precompile_headers、Unity Build
3.19CMake Presets(CMakePresets.json
3.20cmake_path 命令、预设版本 2
3.21FILE_SET 支持、C++23 标准
3.24FIND_PACKAGE_ARGS in FetchContent
3.25SYSTEM 属性在 FetchContent 中
3.28C++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 章 — 安装与环境配置 →