不知道怎么学 CMake ?这份精炼的学习路线拯救你!
一、为什么学习 CMake?
CMake、CTest、CPack和CDash已经成为源码构建的主要工具集,在使用和流行性方面超过了许多类似工具,例如备受推崇的GNU自动工具(Automake)和最新的基于Python的SCons构建系统。
上面的折线图展示 CMake、Automake 和 SCons 在 Google Trends 上的搜索热度随时间推移的演变。从图表可以看出:
- CMake 的搜索热度逐渐增加,且在过去几年中明显高于其他两个工具。这证实了它在现代 C++ 构建工具中的主导地位。
- Automake 的搜索热度显示出逐渐下降的趋势,体现出使用范围逐渐缩小的现实。
- SCons 的搜索热度相对较低,而且没有明显的增长或下降趋势,说明其使用人群相对稳定,但没有显著扩张。
在当今复杂多变的软件开发环境中,高效可靠的构建流程至关重要。CMake 及其配套工具 CTest、CPack 和 CDash,已经成为事实上的标准,它们组成了一套强大的生态系统,用于管理软件项目的整个生命周期,包括配置、构建、测试和部署。
相比于其他构建系统,例如曾经风靡一时的 GNU Autotools (如 Automake) 以及新兴的 Python 构建工具 SCons,CMake 系列工具在实用性和流行度上都占据了显著优势。这主要归功于 CMake 的特点:
- 跨平台支持: CMake 的核心理念之一就是跨平台,它支持 Windows、macOS、Linux 以及其他众多操作系统,并能生成特定平台的原生构建系统(例如 Makefile、Visual Studio 项目文件、Xcode 项目文件等),从而大大降低了跨平台构建的复杂度。
- 灵活的构建配置: CMake 提供了一种简洁、声明式的语言,用于定义项目的构建规则和依赖关系。可以轻松地配置构建过程,例如选择编译器、优化级别、库依赖,而无需深入了解底层构建系统的细节。
- 模块化和可扩展性: CMake 鼓励模块化的项目组织方式,并提供了丰富的模块来支持常见的构建任务,如处理第三方库、生成文档等。此外,CMake 还支持自定义模块和命令,允许根据自己的需求进行扩展。
- 集成测试: CTest 是 CMake 集成的测试工具,可以方便地执行各种测试,并生成详细的测试报告。这能够及早发现和修复缺陷,从而提高软件质量。
- 打包和部署: CPack 创建可分发的软件包,例如安装包、二进制压缩包等,并支持多种打包格式。这简化了软件的发布和部署流程。
- 持续集成: CDash 是一个基于 Web 的平台,用于收集和显示构建和测试结果,方便进行持续集成和质量监控。
CMake 项目始于 1999 年,其初衷是为了解决当时构建系统碎片化的问题。最初由 Kitware 公司开发,旨在为他们的开源项目提供一个通用的、跨平台的构建解决方案。随着时间的推移,CMake 逐渐发展成为一个功能强大、用途广泛的构建工具。
CMake 的核心目标可以概括为:提供一组工具,可以在不同平台上以一致的方式配置、构建、测试和部署项目。 换句话说,CMake 希望开发者专注于代码的逻辑,而不是纠结于不同平台构建系统的差异,从而提高软件开发的效率和质量。
二、如何学习 CMake?
学习CMake并非一蹴而就,而是一个循序渐进的过程。可以从了解CMake的基本概念开始,包括它的作用、核心组件以及基本命令。学习如何编写第一个CMakeLists.txt文件,并了解构建流程。解构模块化、依赖管理、条件编译等更复杂的知识。学习一些高级技巧,例如自定义命令、打包等。
本系列强调实践,通过实际项目来应用所学知识。本系列的 CMake 路线图:
第一阶段:基础入门
- 了解 CMake 的基本概念:什么是 CMake?为什么需要 CMake?以及如何下载安装 CMake。
- 编写第一个 CMakeLists.txt:学习如何使用
cmake
命令生成构建文件,学习如何使用生成的构建文件进行编译。 - 理解 CMake 变量和命令:学习如何设置和使用 CMake 变量,学习常用的 CMake 命令。
- 掌握基本构建流程:了解配置 (configure)、构建 (build) 和安装 (install) 的概念。了解 CMake 的构建目录和源目录的区别。理解构建类型(Debug, Release)。
第二阶段:中级进阶。
- 模块化和代码组织:学习如何使用
add_subdirectory
命令将项目分解为多个模块。学习如何管理模块之间的依赖关系。以及了解如何创建和使用静态/动态库。 - 查找依赖项:深入理解
find_package
命令,以及如何查找外部库。学习如何处理找不到依赖项的情况。了解pkg-config
等工具。 - 条件编译和平台特定代码:学习如何使用
if
语句和条件变量来控制构建过程。了解 CMake 提供的平台检测变量。学习如何编写平台特定的代码。 - 生成器表达式和目标属性:了解并使用生成器表达式
$<...>
,实现更灵活的构建配置。掌握常用的目标属性,例如OUTPUT_NAME
、VERSION
、CXX_STANDARD
。 - 单元测试:学习如何使用 CTest 来进行单元测试。了解如何集成测试框架。
第三阶段:高级技巧。
- 自定义命令和脚本:学习如何使用
add_custom_command
和add_custom_target
添加自定义构建步骤。了解如何使用 CMake 脚本语言编写自定义逻辑。 - 打包和安装:学习如何使用 CPack 创建安装包。了解不同的打包格式 (例如 ZIP, DEB, RPM)。
- 了解如何集成代码覆盖率分析工具,并生成报告。了解一些 CMake 最佳实践。学习如何设计一个好的 CMake 项目结构。
- CMake 在不断发展,保持学习的心态,关注随时了解 CMake 的新特性。
三、预备知识
学习 CMake 虽然是从头开始,但也不能完全是“小白”!
虽然我们说学习 CMake 是从零开始,但它也不是完全不需要任何基础的。如果你想学得轻松、学得明白,最好还是先熟悉一些开发圈里的“基本操作”,这样学习起来会更顺畅。最好具备下面这些技能:
- 对命令行操作要有点感觉: 需要知道如何在终端或者命令提示符里输入命令,比如切换目录、执行文件等。这是开发过程中经常要用到的,也是 CMake 工作的基础。如果你对命令行很陌生,建议先花点时间熟悉一下。
- 对开发环境不陌生: 要知道开发软件(比如IDE、文本编辑器)长什么样,怎么用。毕竟你最终是要用 CMake 来构建你的软件,得先知道软件开发流程是怎样的,对吧?
- 至少熟悉一门编程语言: 你得对 C++、C 或者 Fortran 这类编程语言比较熟悉。而且,你知道你用的是哪个编译器(比如 GCC、Clang、MSVC)编译的。这样你才能更好地理解 CMake 是怎么帮你把代码变成可执行程序的。
- 对 Python 有点了解: 虽然 CMake 主要不是用 Python 写的,但是它有时候需要用到 Python 来做一些辅助操作,比如处理一些构建脚本。如果你了解 Python,会更容易理解 CMake 的一些高级用法。当然,不会也不要紧,但是了解一下肯定没坏处。
特别是命令行,建议熟悉以下常用命令:
-
cd <目录路径>
:切换目录。例如:cd my_project
进入my_project
目录,cd ..
返回上一级目录。 -
pwd
(Linux/macOS)或echo %cd%
(Windows):显示当前工作目录的路径。 -
ls
(Linux/macOS)或dir
(Windows):列出当前目录下的文件和子目录。 -
mkdir <目录名>
:创建新目录。例如:mkdir build
创建一个名为build
的目录。 -
rm <文件名>
(Linux/macOS)或del <文件名>
(Windows):删除文件。 -
rm -rf <目录名>
(Linux/macOS): 强制删除目录及其内容。(注意:使用此命令时要小心) -
cp <源文件> <目标文件>
:复制文件。 -
mv <源文件> <目标文件>
: 移动文件,也可以用于重命名文件。 -
touch <文件名>
(Linux/macOS):创建空文件。 -
type <文件名>
(Windows) 或cat <文件名>
(Linux/macOS):显示文件内容。 -
./<可执行文件名>
(Linux/macOS)或<可执行文件名>.exe
(Windows):执行当前目录下的可执行文件。 -
make
或ninja
:使用构建工具编译代码 (CMake 生成构建文件后使用)。 -
echo <字符串>
: 将字符串输出到终端。 -
clear
(Linux/macOS)或cls
(Windows):清空终端屏幕。 -
git --version
:查看 Git 版本号 (如果使用 Git)。 -
<命令> --help
: 查看命令帮助文档。
简单来说,如果你已经掌握了一些编程的基本技能,并且对软件开发流程有一定了解,那学习 CMake 就会轻松很多。 如果你对这些还比较陌生,也不要害怕,可以先补充一些相关的知识,再来挑战 CMake 也会事半功倍!
四、CMake的工作时序
在CMake管理的项目中,工作流可以被理解为一个由多个阶段组成的过程链,每个阶段都有其特定的功能和目的。
- 配置阶段(CMake time或confighure time):在这一阶段,CMake 解析和处理项目根目录以及所有子目录下的 CMakeLists.txt 文件。这里,CMake 收集所有构建指令和依赖关系,生成一个内部的构建模型。
- 生成阶段(Generation time):配置完成后,CMake 利用收集到的信息生成适合特定构建工具(如Make、Ninja、Visual Studio等)的构建脚本或项目文件。此阶段的输出是特定于平台的构建文件。
- 编译阶段(Build time):使用生成的构建脚本或项目文件,调用本地编译器进行代码编译。所有的源代码被编译成库和可执行文件。注意,在这个过程中,可能会有子项目的CMake文件再次被调用(递归配置),以确保依赖正确处理。
- 测试阶段(CTest time或 test time):一旦构建完成,CTest工具被用于执行项目的测试脚本,验证生成的可执行文件和库是否正常工作。
- 报告阶段(CDash time或report time):测试结果通过CDash上传到一个中央仪表板,供开发团队查看和分析,以便跟踪项目的质量和进度。
- 安装阶段(Install time):项目构建完成后,CMake提供的
cmake --install
命令用于将编译好的文件(如可执行文件、库、配置文件等)从构建目录复制到指定的安装目录。 - 打包阶段(CPack time或packaging time):CPack工具在此阶段被用于将项目打包成各种格式的安装包,比如DEB、RPM、NSIS、ZIP等,以便于项目发布和分发。
- 包安装阶段(Package install time):一旦包被创建,用户可以将其安装到他们的系统中。此阶段涉及系统范围的文件安装和配置。
每个阶段都依赖于前一阶段的成功完成,但也允许一定程度的灵活性。
五、总结
CMake 作为现代构建工具,核心价值在于跨平台、灵活配置和高效管理。它统一了不同平台的构建流程,简化了复杂的依赖关系,并提供了强大的测试、打包和部署能力。学习 CMake 能够提升开发效率、确保软件质量并简化发布流程。从基础概念入手,逐步掌握高级技巧,并结合实践不断探索,你就能充分利用 CMake 的优势。
参考:
- 【cmake社区帮助文档】(https://cmake.org/cmake/help/latest/)
- 【CMake Tutorial (官方)】(https://cmake.org/cmake/help/latest/guide/tutorial/index.html)
- 《CMake Cookbook》- Radovan Bast & Roberto Di Remigio
- 《精通CMake》- Ken Martin & Bill Hoffman