构建配置工具 + 构建执行工具 + 编译器

构建配置工具

检测操作系统:判断当前操作系统的环境,win,linux,mac方便调用工具链。

确定构建的文件:哪些文件哪些目录需要去编译,如果不想编译

确定引入的库:程序需要链接哪些库文件,在这里配置

构建配置工具有:

cmake(跨平台) 生成makefile或者build.ninja文件交给构建执行工具

xmake(跨平台) 自己配置,自己调用编译不需要构建执行工具

Makefile这个不是构建工具,这个只是我们自己编写的让make去执行的脚本。

构建执行工具(构建的后端)

1. 头文件依赖的“精确制导”(这是最要命的)

假设你修改了 global_defines.h 这个头文件。这个头文件被 3000 个 .cpp 文件引用了。

  • 如果直接让 CMake 调用 gcc:CMake 不知道这个头文件影响了谁,它只能全部重新编译 3000 个文件。
  • 中间层(Ninja)的做法:在第一次完全编译时,GCC 配合 -MMD 参数生成了一个 .d 依赖文件。Ninja 读取这个 .d 文件,精确记录了“global_defines.h 影响了哪 3000 个 .o”。当你改了 global_defines.h,Ninja 只重新编译这 3000 个,如果只改了某个不常用的 .cpp,它可能只编译 1 个。这种“按需编译”的能力,是构建执行工具的核心命脉。
2. 作业调度器(Job Server)—— 避免“打架死锁”

GCC 本身不支持多核编译一个文件,但 Ninja 支持同时启动多个 GCC。 Ninja 内置了图论拓扑排序算法。例如:

  • 必须先编译 core_a.cpp 和 core_b.cpp,才能链接出 libcore.a。
  • 有了 libcore.a,才能编译 main.cpp。 Ninja 会先启动 16 个核心同时编译 core_a 和 core_b,等它们全部完成后,再启动 main.cpp 的编译。 如果让 CMake 干这个活,CMake 得自己写一套复杂的线程锁和信号量机制(Semaphore),这相当于让设计师去当建筑工地的安全调度员,它根本干不来,强行写会极其臃肿且容易死锁。
3. 构建状态的“永久记忆”(.ninja_log)

Ninja 不仅是看文件时间,它还存储了每次编译命令的哈希值(Hash)。

  • 假设你昨天编译时用的 GCC 版本是 10.0,今天你升级到了 11.0。
  • 即使 .cpp 文件没变,Ninja 检测到编译器的哈希值变了,它会自动重新编译所有文件,因为不同版本的 GCC 生成的机器码可能不一样。
  • 如果 CMake 直接调用,它根本不会记录这些元数据,会导致你用了新编译器,程序却还在跑旧编译器的残留缓存,引发极其诡异的崩溃 Bug。

Make (GNU Make) NMake (微软) Ninja MSBuild (VS)

编译器

GCC (gcc/g++) Clang MSVC (cl.exe)