开篇:让引擎先转起来
前两节课,我们认识了 CMake 这位靠谱的施工队长,也把它成功请进了电脑。但从今天开始,我们要停止纸上谈兵,动手搭建第一间样板房。
对零基础的朋友来说,第一次写 CMake 就像学开车时启动引擎——你暂时不需要搞懂引擎内部的精密原理,只需要踩下油门,听到轰鸣声,建立“我能让它跑起来”的信心。这节课,我们的目标只有一个:用最小的代码量,跑通第一个跨平台的 C++ 项目。
最小可运行示例:Hello, CMake!
在计算机界,任何新技术的”仪式感”都从 Hello World 开始。我们先创建一个新文件夹,名字就叫 hello_cmake。
第一步:创建目录结构
在你的终端(Windows 用 PowerShell/CMD,macOS/Linux 用 Terminal)里执行:
mkdir hello_cmake
cd hello_cmake
第二步:编写 main.cpp
创建一个最普通的 C++ 源文件:
#include <iostream>
int main() {
std::cout << "Hello, CMake World!" << std::endl;
return 0;
}
第三步:编写 CMakeLists.txt
这是整个项目的灵魂文件。在 hello_cmake 根目录下,新建一个名为 CMakeLists.txt 的文件(注意大小写和完整拼写,一个字母都不能错),写入以下内容:
cmake_minimum_required(VERSION 3.10)
project(HelloCMake)
add_executable(hello main.cpp)
这三行代码是 Modern CMake 项目的最小公约数,我们来逐行拆解:
cmake_minimum_required(VERSION 3.10):告诉 CMake,”这个项目至少需要 3.10 版本才能看懂”。如果系统上的 CMake 版本太老,它会立刻报错退出,而不是产生诡异的错误。project(HelloCMake):给整个项目起个名字。这个名字会作为构建产物的前缀,也会出现在 IDE 的解决方案名称里。add_executable(hello main.cpp):声明我们要生成一个名为hello(Windows 下会自动变成hello.exe)的可执行文件,它由main.cpp编译而来。
目录结构规范:源码与构建必须”分居”
很多初学者会犯的一个错误,是把所有文件平铺在桌面上,或者直接在源码文件夹里”就地编译”。CMake 社区有一个铁律:源代码目录(Source Tree)和构建目录(Build Tree)必须物理分离。
推荐目录结构
即使是我们这个只有两个文件的小项目,也要养成好习惯:
hello_cmake/
├── CMakeLists.txt # 项目配置(永远留在源码区)
├── src/
│ └── main.cpp # 源代码
└── build/ # 构建产物(随时可删除,不加入版本控制)
如果代码文件不多,你也可以暂时把 main.cpp 和 CMakeLists.txt 放在同一层,但build 目录一定要独立出去。
为什么要分离?
- 方便清理:编译产生的 .o、.obj、可执行文件、CMake 缓存混在一起,Out-of-source 构建只需要
rm -rf build/就能回到纯净状态。 - 多配置并行:你可以同时创建
build-debug和build-release,分别存放不同优化级别的产物,互不干扰。 - 版本控制友好:只需在
.gitignore里忽略build/目录,永远不会把几 MB 的临时文件提交到 Git 仓库。
构建流程详解:配置 → 生成 → 构建
CMake 的工作不是”一键编译”那么简单,它 internally 分为三个明确的阶段。理解这三个阶段,能让你在报错时快速定位问题到底出在哪一环。
阶段一:Configure(配置)
CMake 读取你的 CMakeLists.txt,执行其中的脚本命令,检测编译器路径、系统特性、依赖库是否存在。这个阶段会在内存中构建出一个项目模型(比如有哪些目标、需要哪些头文件路径)。
如果配置成功,你会在 build 目录下看到 CMakeCache.txt,这就是配置的”快照”。
阶段二:Generate(生成)
根据配置阶段得到的信息,CMake 开始生成底层构建系统所需的文件。如果你用默认的 Makefile,它会生成 Makefile;如果你用 Ninja,它会生成 build.ninja;如果你在 Windows 上用 Visual Studio 生成器,它会生成 .sln 和 .vcxproj 文件。
你可以把 Configure 和 Generate 理解为画蓝图和出施工图纸的区别。
阶段三:Build(构建)
CMake 把”施工图纸”交给真正的编译工具链(如 make、ninja、MSBuild),由它们调用编译器和链接器,最终产出可执行文件或库。
在命令行里,cmake .. 默认会连续执行 Configure 和 Generate;而 cmake --build . 则对应第三阶段。
命令行基础操作:现在敲下这些命令
打开终端,确保你在 hello_cmake 目录下,然后严格执行以下步骤。这是你和 CMake 的第一次握手。
步骤 1:进入构建目录
mkdir build
cd build
如果你忘了创建 build 目录,后文的命令会报错,提醒你”目录不存在”。
步骤 2:配置并生成(Configure + Generate)
cmake ..
这里的 .. 表示”上一级目录”,也就是告诉 CMake:去上一层找 CMakeLists.txt。如果一切顺利,你会看到类似这样的输出:
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/you/hello_cmake/build
最后这一行非常关键,它证明了 CMake 已经成功把”施工图纸”写进了 build 目录。
步骤 3:构建(Build)
cmake --build .
--build 是 CMake 提供的通用构建指令,后面跟着的 . 表示”在当前目录寻找构建图纸”。它底层会自动调用 make、ninja 或 MSBuild,你不需要关心具体用的是哪个。
编译成功后,你会在 build 目录下看到生成的可执行文件:
- Linux/macOS:
hello - Windows:
Debughello.exe(如果是 MSVC 生成器)或hello.exe(如果是 MinGW)
步骤 4:运行程序
在 Linux/macOS 下:
./hello
在 Windows PowerShell 下:
.Debughello.exe
看到 Hello, CMake World! 了吗?恭喜你,你的第一个 CMake 项目成功运行了!
关于 ctest 的提前预告
虽然我们这个小项目还没有测试代码,但你可以提前记住这条命令:
ctest
它是 CMake 的测试驱动器。后续当我们用 add_test 添加了单元测试后,ctest 会自动批量运行所有测试并生成报告。
构建目录管理:In-source vs Out-of-source
既然提到了 build 目录,这里必须把两种构建方式的对比如图钉一样钉进你的脑海。
In-source 构建(源码内构建)
直接在源码根目录执行 cmake .(注意那个点),CMake 会在源码目录里生成一堆 CMakeFiles/、Makefile、CMakeCache.txt,和你的源代码混在一起。
为什么不推荐?
- 污染源代码树,看着就脏。
- 清理困难:你可能不小心删掉自己写的 .cpp 文件。
- 无法并行维护多个构建配置。
Out-of-source 构建(源码外构建)
就是我们这节课用的方式:在独立的 build 目录里运行 cmake ..。
优势:
- 随时从零开始:构建出问题?直接
rm -rf build然后重新来,源码毫发无伤。 - 多配置并存:
mkdir build-debug && cd build-debug && cmake -DCMAKE_BUILD_TYPE=Debug .. mkdir build-release && cd build-release && cmake -DCMAKE_BUILD_TYPE=Release .. - Git 友好:在
.gitignore里加一行build/,世界瞬间清净。
Modern CMake 的几乎所有最佳实践,都建立在 Out-of-source 的前提之上。请把它变成肌肉记忆。
本节小结与自检清单
到这里,你已经完成了从零到一的跨越。不妨对照下面这份清单,确认自己是否掌握:
- 能独立写出包含
cmake_minimum_required、project、add_executable的最小CMakeLists.txt。 - 理解
cmake ..和cmake --build .的区别与使用场景。 - 能解释 Configure、Generate、Build 三阶段各自在做什么。
- 坚决使用 Out-of-source 构建,并且知道如何在
.gitignore中忽略构建目录。
下节课,我们将进入 CMakeLists.txt 的语法基础,学习变量、列表、条件判断和函数。把地基打牢,后面盖高楼才不会晃。


没有回复内容