CMake 入门教程之一
大纲
前言
本文将介绍 CMake 是什么,并使用 CMake 编译第一个 C/C++ 项目。
主流的构建工具
| 构建工具 | 主要用途 | 适用平台 | 特点 | 常见使用场景 |
|---|---|---|---|---|
| Make | 经典构建系统 | Unix/Linux/macOS,Windows(MinGW / Cygwin) | GNU 工具链标配,依赖 Makefile | Linux 系统编译、跨平台项目 |
| CMake | 跨平台构建配置生成器 | 跨平台 | 不直接编译,生成 Makefile / Ninja / VS 工程 | 跨平台 C/C++ 项目管理 |
| Ninja | 高性能并行构建 | 跨平台 | 构建速度快,文件依赖解析简单 | CMake、Chromium、LLVM 等大型项目 |
| NMake | 命令行 Make 工具(微软版) | Windows | 随 MSVC 提供,适合命令行构建简单项目 | MSVC 环境下的轻量构建 |
| MSBuild | 编译和管理 Visual Studio 项目(.sln/.vcxproj) | Windows | 与 VS 深度集成,支持 MSVC 编译器 | Visual Studio 传统项目构建 |
| Meson | 现代化构建系统 | 跨平台 | 默认用 Ninja 构建,语法简单 | GNOME、GStreamer 等项目 |
| Bazel | Google 出品构建系统 | 跨平台 | 支持大型代码库、分布式构建 | TensorFlow、Google 内部项目 |
| SCons | 基于 Python 实现的构建系统 | 跨平台 | 配置用 Python 脚本编写,依赖少 | 嵌入式或定制化构建流程 |
CMake 基本概念
什么是 CMake
CMake 是用于构建、测试和软件打包的开源跨平台工具。
为什么要学习 CMake
- 企业项目:不管是构建 Linux 程序还是自动化构建 VS 程序,业内大量公司都在使用 CMake。
- 开源项目:QT、OpenCV、GoogleTest、KDE、OGRE、Android NDK、鸿蒙 ETS NDK 等知名开源项目都使用了 CMake。
- 职业发展:CMake 适用于跨平台自动化构建和部署、持续集成、测试驱动开发、自动化单元测试等场景。
为什么要选用 CMake
为什么需要一个好的构建系统
- 想避免硬编码路径
- 需要在多台计算机上构建一个包
- 想使用 CI(持续集成)
- 需要支持不同的操作系统
- 想支持多个编译器
- 想使用 IDE,但不是所有情况
- 想描述程序的逻辑结构,而不是标志和命令
- 想使用库
- 想使用其他工具来帮助编写代码,比如
ProtoBuf - 想使用单元测试
为什么需要持续集成
- 每次集成都通过自动化的制造(包括提交、发布、自动化测试)来验证,准确地发现集成错误
- 快速错误,每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易
- 各种不同的更新主干,如果不经常集成,会导致集成的成本变大
- 让产品可以快速地通过,同时保持关键测试合格
- 自动化测试,只要有一个测试用例不通过就不能集成
- 集成并不能删除发现的错误,但可以让它们很容易被发现和改正
为什么是 CMake
- CMake 的特性
- 自动搜索可能需要的程序、库和头文件的能力
- 独立的构建目录,可以安全清理
- 支持创建复杂的自定义命令,例如
qt moc uic - 配置时选择可选组件的能力
- 从简单的文本文件(CMakeLists.txt)自动生成工作区和项目的能力
- 在静态和共享构建之间轻松切换的能力
- 在大多数平台上自动生成文件依赖项,并支持并行构建
- 每个 IDE 都支持 CMake( CMake 支持几乎所有 IDE)
- 使用 CMake 的软件包比任何其他系统都多
- CMake 的特性
CMake 的工作原理

编译第一个 CMake 项目
- 项目的目录结构
1 | first_cmake |
first_cmake.cpp源文件
1 |
|
CMakeLists.txt配置文件
1 | # 指定 CMake 版本 |
- 执行 CMake 编译
1 | # 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件) |
1 | - 参数说明 |
- 上述两个 CMake 命令的功能可以分为几部分:
- 调用生成的构建系统:
- 如果之前生成的是 Makefile,它将调用
make。 - 如果生成的是 Ninja 构建系统,它将调用
ninja。
- 如果之前生成的是 Makefile,它将调用
- 编译项目:
- 它将编译项目中的源代码,并生成可执行文件、库等目标。
- 自动检测编译工具:
cmake --build会根据生成的构建系统(Makefile、Ninja 等)来自动使用正确的工具进行构建,而不需要手动调用make或ninja。
- 调用生成的构建系统:
提示
通过 cmake --build build,可以避免使用特定的生成器工具(例如 make 或 ninja),并保持跨平台的一致性。
使用指定的 C/C++ 编译器
在使用 CMake 编译 C/C++ 项目时,CMake 会自动根据不同的操作系统类型来调用对应的 C/C++ 编译器,比如:
Linux 操作系统:使用gcc、g++、clang编译器。Windows 操作系统:使用 Microsoft Visual C++ 编译器(简称 MSVC),提供了cl.exe编译和link.exe链接等命令行工具。
在 Windows 操作系统上,如果不是在 Developer Command Prompt for Visual Studio 开发者命令提示符窗口内执行生成构建文件的 CMake 命令,而是在 Git Bash 内执行命令,那么就会出现以下的错误信息:
1 | # 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件) |
1 | -- Building for: NMake Makefiles |
这通常是因为在 Git Bash 内运行 CMake 时,CMake 找不到 nmake 工具,比如 nmake 工具不可用或没有正确安装。解决该问题的方法是,在 Git Bash 中让 CMake 使用特定的 C/C++ 编译器。
什么是 nmake
nmake 是 Microsoft 的一个构建工具,其作用类似 make,它通常与 Visual Studio 一起安装,尤其在使用 Microsoft Visual C++ 编译器(简称 MSVC)时,CMake 可能需要它来生成配置文件。
解决方案一
在 Windows 系统上 安装 MinGW,然后在 Git Bash 中让 CMake 使用 MinGW 自带的 gcc 和 g++ 编译器。
- 首先删除之前生成的
build目录
1 | # 删除构建目录 |
- 在 Git Bash 中,要让 CMake 使用 MinGW 提供的
gcc和g++编译器,可以通过指定使用 MinGW Makefiles 配置生成器来完成
1 | # 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件) |
- 一旦配置完成,就可以使用以下命令编译 C/C++ 项目,相当于执行了
mingw32-make -C build命令
1 | # 编译项目,生成可执行文件 |
1 | - 参数说明 |
解决方案二
在 Windows 系统上 安装 MinGW,然后在 Git Bash 中让 CMake 使用 MinGW 自带的 gcc 和 g++ 编译器。
- 首先删除之前生成的
build目录
1 | # 删除构建目录 |
- 在 Git Bash 中,要让 CMake 使用 MinGW 提供的编译器,还可以在
CMakeLists.txt配置文件中,显式指定 C/C++ 编译器
1 | # 设置 MinGW 提供的 GCC 作为 C 编译器 |
然后编译项目代码,必须确保上面设置的编译器路径和 MinGW 的安装路径一致
1 | # 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件) |
成功编译代码后,最后会显示 gcc 和 g++ 编译器都来自 MinGW,而不是 Visual Studio 的 cl.exe。
特别注意
笔者使用该方案来让 CMake 使用特定的编译器,但最终没有成功,原因暂时未知。
使用 NMake 构建并编译
NMake 是 MSVC 自带的轻量命令行构建工具,在 Windows 环境下,若希望让 CMake 使用 NMake 构建并编译项目,可以在 Developer Command Prompt for Visual Studio 开发者命令提示符窗口内执行以下命令:
- 让 CMake 使用 NMake Makefiles 配置生成器来生成构建文件
1 | # 配置项目,生成构建文件(例如 Makefile 或 Ninja 文件) |
- 一旦配置完成,就可以使用以下命令编译 C/C++ 项目,相当于在
build目录下执行nmake命令
1 | # 编译项目,生成可执行文件 |
1 | - 参数说明 |
提示
- 若想知道当前版本的 CMake 支持哪些生成器(Generators),可以使用帮助命令
cmake --help来查看详细的帮助手册。 - CMake 常用的生成器有:NMake Makefiles、MinGW Makefiles、MSYS Makefiles、Ninja 等。
使用常见的编译选项
--target <target>: 指定要构建的特定目标(例如,生成特定的可执行文件或库)。如果不指定,它会构建所有默认目标。
1 | cmake --build build --target my_target |
--config <configuration>: 用于多配置生成器(例如 Visual Studio 或 Xcode),可以指定构建的配置类型,如Debug或Release。
1 | cmake --build build --config Release |
--clean-first: 先清理项目再编译,相当于make clean && make。
1 | cmake --build build --clean-first |
