一、CMake初识:什么是CMake、CMake的历史与哲学、为什么现代C++项目离不开CMake

1.1 什么是CMake?

CMake,全称是 Cross-platform Make。说白了,它是一个跨平台的构建系统生成器

你写C++代码,总得把它变成可执行文件吧?这个过程叫「构建」。不同平台(Windows、Linux、macOS)用的构建工具不一样——Windows有Visual Studio的.sln方案,Linux有Makefile,macOS有Xcode项目。你总不能每换一个平台,就手动重写一套构建脚本吧?

CMake就是来解决这个问题的。你只需要写一份 CMakeLists.txt,CMake就能自动生成对应平台的构建文件。我刚开始接触CMake时,觉得它就是个「翻译官」——把你的构建意图翻译给各个平台的构建工具听。

核心理解: CMake不是编译器,也不是构建工具本身。它是「构建工具的生成器」。你写CMake脚本,它帮你生成Makefile或Visual Studio解决方案,然后你再用make或VS去编译。

1.2 CMake的历史:从工具到事实标准

CMake诞生于2000年,最初是为了支持美国国家医学图书馆的ITK(Insight Segmentation and Registration Toolkit)项目。当时Kitware公司的几位工程师发现,维护一套跨平台的构建系统实在太痛苦了——每个平台都要单独写构建脚本,而且不同平台的编译器行为还不一样。

于是他们决定自己造一个工具。嗯,这就是CMake的起点。

我记得2010年左右,CMake还只是众多构建工具中的一个选项。那时候还有Autotools、SCons、Premake等一堆选择。但到了2015年以后,CMake几乎成了C++项目的标配。为什么?

  • 2014年,CMake 3.0发布——引入了target-based设计,让依赖管理变得清晰
  • 2017年,CMake 3.9开始支持C++17——紧跟标准步伐
  • 2019年,CMake 3.15完善了FetchContent——让源码依赖管理变得简单
  • 2020年以后——几乎所有主流C++库(Boost、Qt、OpenCV、LLVM)都提供了CMake支持

说白了,CMake的发展史就是一部C++构建工具从混乱走向统一的历史。

1.3 CMake的哲学:约定优于配置,但给你自由

CMake的设计哲学,我个人总结为三点:

  1. 声明式构建:你告诉CMake「我要什么」,而不是「怎么做」。比如你声明一个可执行文件需要链接哪些库,CMake自己会处理编译顺序和链接参数。
  2. 模块化:每个目录可以有自己的CMakeLists.txt,通过add_subdirectory组合。这就像C++的命名空间——各管各的,互不干扰。
  3. 可移植性:同一份CMake脚本,在Windows、Linux、macOS上都能跑。我在项目中遇到过最头疼的事,就是Windows路径用反斜杠、Linux用正斜杠——CMake帮你统一处理了。

一个小技巧: CMake的变量作用域是「目录级」的。你在父目录设置的变量,子目录默认看不到。想传递变量?用 set(... PARENT_SCOPE) 或者 target_compile_definitions 这种target-based方式。我早期踩过这个坑,变量传不过去,排查了半天。

1.4 为什么现代C++项目离不开CMake?

这个问题,我直接给你列几个场景,你感受一下:

场景 没有CMake 有CMake
跨平台编译 Windows写VS项目,Linux写Makefile,macOS写Xcode项目——三套脚本,三倍痛苦 一份CMakeLists.txt,cmake -G "Visual Studio 2022"cmake -G "Unix Makefiles" 搞定
依赖第三方库 手动下载源码、配置路径、设置头文件目录、链接库文件——每次升级库都得重来一遍 FetchContentfind_package 一行搞定,版本管理自动处理
大型项目组织 所有源文件堆在一个目录,或者手动维护复杂的Makefile依赖关系 按模块分目录,每个目录一个CMakeLists.txt,target_link_libraries 自动传递依赖
持续集成 每个CI平台(GitHub Actions、GitLab CI、Jenkins)都要单独写构建脚本 统一用 cmake --build .,CI脚本只需要几行

你想想看,现在哪个C++项目不是跨平台的?哪个项目不用第三方库?哪个项目不需要CI?

所以我说,CMake不是「可选项」,而是「必需品」。我见过一些老项目还在用纯Makefile,每次换人维护都像在考古——没人敢改构建脚本,因为一改就崩。而CMake项目,新成员上手快,维护成本低。

避坑指南: 我曾经接手过一个项目,它的CMakeLists.txt有2000多行,全是全局变量和硬编码路径。这种「CMake面条代码」比没有CMake还可怕。记住:CMake也要讲究代码规范——用target-based设计,少用全局变量,多用函数和模块。

1.5 CMake的核心工作流程

CMake的工作流程分两步:

  1. 配置阶段(Configure):读取CMakeLists.txt,检测编译器、库、头文件,生成构建文件(Makefile或.sln)
  2. 构建阶段(Build):用生成的构建文件去编译代码

用命令表示就是:

# 配置阶段
mkdir build && cd build
cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release

# 构建阶段
cmake --build . --config Release

这里有个细节:cmake --build . 会自动调用你系统上的make或msbuild,你不用关心底层用的是哪个工具。这就是CMake的抽象能力。

1.6 本章知识体系

下面这张图,帮你梳理本章的核心脉络:

CMake初识:知识体系 CMake 是什么? 跨平台构建系统生成器 历史 2000年诞生 → 事实标准 哲学 声明式 · 模块化 · 可移植 为什么离不开? 跨平台 · 依赖管理 · CI友好 工作流程 配置 → 构建 第1章 · CMake初识

1.7 小结

这一章我们聊了:

  • CMake是跨平台构建系统生成器,不是编译器也不是构建工具本身
  • 它从2000年诞生,经过20多年发展,已经成为C++项目的事实标准
  • 它的哲学是声明式、模块化、可移植——你告诉它要什么,它帮你搞定怎么干
  • 现代C++项目离不开它,因为跨平台、依赖管理、CI集成这些刚需,CMake都能优雅解决

下一章,我们会动手写第一个CMakeLists.txt,从零开始构建一个C++项目。到时候你会发现——原来CMake这么简单。


公众号:蓝海资料掘金营,微信deep3321