3. 1.3 第一个CMake项目

开篇:让引擎先转起来

前两节课,我们认识了 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.cppCMakeLists.txt 放在同一层,但build 目录一定要独立出去

为什么要分离?

  • 方便清理:编译产生的 .o、.obj、可执行文件、CMake 缓存混在一起,Out-of-source 构建只需要 rm -rf build/ 就能回到纯净状态。
  • 多配置并行:你可以同时创建 build-debugbuild-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/MakefileCMakeCache.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 的前提之上。请把它变成肌肉记忆。

本节小结与自检清单

到这里,你已经完成了从零到一的跨越。不妨对照下面这份清单,确认自己是否掌握:

  1. 能独立写出包含 cmake_minimum_requiredprojectadd_executable 的最小 CMakeLists.txt
  2. 理解 cmake ..cmake --build . 的区别与使用场景。
  3. 能解释 Configure、Generate、Build 三阶段各自在做什么。
  4. 坚决使用 Out-of-source 构建,并且知道如何在 .gitignore 中忽略构建目录。

下节课,我们将进入 CMakeLists.txt 的语法基础,学习变量、列表、条件判断和函数。把地基打牢,后面盖高楼才不会晃。

请登录后发表评论

    没有回复内容

正在唤醒异次元光景……