引言:当”施工队长”眺望远方的新地平线
在之前的十一章里,我们的 CMake “施工队长”已经身经百战:从本地盖楼到海外工程,从手工砌砖到自动化质检,从单打独斗到融入包管理生态。但一位真正优秀的工程领袖不会只盯着眼前的工地,他还会眺望地平线——看看未来建筑行业的技术潮流,提前为团队准备新的工具和工艺。
CMake 从 2000 年诞生至今,已经走过了二十多个年头。它从最初解决跨平台 Makefile 生成的小工具,成长为现代 C++ 生态的事实标准构建系统。但技术世界从不停止转动:C++20 带来了颠覆性的模块(Modules)机制,云计算让”分布式施工”成为可能,而其他构建系统如 Bazel 也在某些领域展现出独特优势。在这一节中,我们将把目光投向未来,看看 CMake 正在朝哪些方向进化,以及这些趋势将如何影响你的下一个项目。
CMake 4.x:下一张施工蓝图
截至目前,CMake 3.x 系列仍在积极迭代(3.29、3.30 及更高版本),但社区和官方已经透露了关于 CMake 4.x 的远期规划。虽然 4.0 的发布日期尚未确定,但从官方 issue 讨论和年度开发者峰会(CMake Summit)中,我们可以窥见一些关键趋势。
更激进的现代化语法
CMake 3.x 的一个重要使命是”平缓过渡”——既要推动 Modern CMake(基于目标),又要兼容大量遗留的全局变量式写法。而 4.x 可能会成为破旧立新的版本:
- 废弃旧命令:像
include_directories()、link_libraries()、add_definitions()等全局命令可能会被标记为废弃(deprecated),甚至在未来版本中彻底移除。 - 增强的目标模型:进一步扩展
TARGET的属性系统,可能引入更细粒度的”构建单元”概念,让接口库(Interface Library)和对象库(Object Library)的能力更接近完整的构建图节点。 - 原生包管理集成:虽然 CMake 目前通过
find_package、FetchContent与外部包管理器协作,但 4.x 可能会提供更原生的依赖声明语法,进一步简化第三方库的引入流程。
实验性特性预览
在当前的 CMake 3.28+ 版本中,已经可以看到一些为 4.x 探路的实验特性:
- 改进的 Presets 系统:除了现有的 Configure Presets、Build Presets 和 Test Presets,未来可能支持更复杂的 Workflow Presets 条件分支和预设继承宏扩展。
- 更强的生成器表达式:引入更多字符串处理和列表操作的原生生成器表达式,减少对
set()临时变量的依赖。 - 文件集(FILE_SET)的完善:CMake 3.23+ 引入的
FILE_SET用于声明目标的公共头文件集合,未来这将成为头文件安装和导出的主要方式,取代传统的PUBLIC_HEADER等属性。
C++20 Modules:模块化地基的工程难题
如果说 C++98/11/14/17 的构建模型都是”把头文件当蓝图,把源文件当砖块”,那么 C++20 Modules 彻底改变了游戏规则。模块接口单元(.cppm 或 .ixx)需要先编译成二进制模块接口(BMI, Binary Module Interface),其他文件才能导入(import)它们。这对构建系统提出了前所未有的挑战。
为什么 Modules 让构建系统”头疼”
传统的构建流程中,CMake 可以轻松地根据文件依赖关系决定编译顺序:先编译 a.cpp 和 b.cpp,再链接成可执行文件。但 Modules 引入了编译期依赖拓扑:
- 你必须先编译模块接口文件,生成 BMI。
- 导入该模块的源文件必须在 BMI 生成后才能开始编译。
- 模块之间可能存在复杂的 DAG(有向无环图)依赖关系。
- 不同编译器(GCC、Clang、MSVC)生成和查找 BMI 的方式完全不同。
这意味着 CMake 不再只是”发号施令”的队长,它还必须成为一位精通模块拓扑的调度员。
CMake 的应对与现状
从 CMake 3.25 开始,官方加入了对 C++20 Modules 的实验性支持,并在 3.28 中大幅改进。以下是一个简化的现代配置示例:
cmake_minimum_required(VERSION 3.28)
project(MyModuleProject CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "0ff") # 根据版本可能需要调整
add_library(math)
target_sources(math
PUBLIC
FILE_SET CXX_MODULES FILES
math.cppm
)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE math)
在这个例子中,FILE_SET CXX_MODULES 告诉 CMake:math.cppm 是一个模块接口文件。CMake 会与编译器进行深度配合(通过编译器的模块扫描器),自动推导出 main.cpp 对 math 模块的依赖,并确保编译顺序正确。
然而,目前的挑战依然存在:
- 编译器差异:MSVC 对 Modules 的支持相对成熟,而 GCC 和 Clang 在某些场景下仍有局限,CMake 需要维护三套不同的生成逻辑。
- 增量构建:如果模块接口的私有实现细节发生变化,是否应该触发所有导入者的重编译?如何精确追踪 BMI 的变更粒度,仍是优化难点。
- 与现有生态的冲突:大量的第三方库仍基于头文件模型。在可预见的未来,CMake 必须同时支持”头文件世界”和”模块世界”的混合构建。
构建系统云化:施工队的远程协作
随着项目规模膨胀到数万甚至数十万个编译单元,即使 CMake 的生成逻辑再高效,本地单机的编译速度也会成为瓶颈。未来的构建系统必须学会调用云端算力。
分布式编译与缓存
CMake 本身并不直接编译代码,它生成构建描述文件(如 Ninja、Makefiles)交给底层工具。因此,”云化”的关键在于 CMake 与这些工具链的协同:
- 编译器启动器(Compiler Launcher):通过
CMAKE_CXX_COMPILER_LAUNCHER,CMake 可以透明地调用ccache、sccache或distcc。例如:find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") endif()sccache甚至支持将编译缓存上传到云存储(如 AWS S3、Azure Blob),让全球分布的开发团队共享编译成果。 - 远程执行(Remote Execution):Bazel 生态中的 Remote Execution API 已经被业界广泛接受。Ninja 和 CMake 社区也在探索如何让生成的 Ninja 文件支持将编译任务分发到远程构建集群。这意味着你的笔记本电脑只需运行 CMake 配置,实际的编译可以发生在拥有数百核的云端容器中。
构建缓存的智能化
未来的 CMake 可能会更深入地整合构建缓存概念:
- 不仅是对象文件(
.o)的缓存,还包括 BMI 的缓存(针对 C++20 Modules)。 - 基于内容的寻址缓存(Content-Addressable Cache):只要输入源文件和编译选项的哈希值不变,就直接拉取缓存结果,无需本地计算哈希。
- 与 CI/CD 的无缝衔接:当持续集成系统编译过一次后,本地开发者可以通过缓存秒级获取相同的构建产物。
构建系统的融合:与 Bazel 等系统的互操作
CMake 是现代 C++ 的王者,但它并非唯一的构建系统。Google 的 Bazel、Meta 的 Buck2、以及 Python 生态的 Meson,都在特定领域展现出优势。未来的趋势不是谁取代谁,而是互操作性(Interoperability)的增强。
为什么需要互操作
想象这样一个场景:你的核心算法库使用 CMake 管理,依赖了几十个传统的 C/C++ 开源库;但你的 AI 团队使用 Bazel 管理 TensorFlow 相关的推理引擎。如何让 Bazel 项目轻松找到你的 CMake 库,或者反之?
目前的解决方案往往很笨拙:手动写 BUILD 文件封装 CMake 项目,或者通过 ExternalProject 在 CMake 中调用 Bazel。未来,社区正在推动标准化:
- Common Package Specification:一种跨构建系统的包描述格式,让 Bazel、CMake、Meson 都能理解同一个库的依赖关系、头文件路径和链接标志。
- CMake 导出 Bazel 模块:通过工具(如
cmake-bazel转换器)自动生成BUILD文件,让 Bazel 消费 CMake 项目。 - Bazel 的 CMake 规则:反向地,在 Bazel 中提供高阶规则,自动下载并执行 CMake 配置,将生成的库暴露给 Bazel 的目标图。
标准化与生态融合
除了与 Bazel 的互操作,CMake 本身也在向更开放的标准靠拢:
- SBOM(软件物料清单)生成:未来 CMake 可能内置生成 SPDX 或 CycloneDX 格式的依赖清单,满足供应链安全合规要求。
- 编译数据库(compile_commands.json)的进化:这是 CMake 与各类工具(语言服务器、静态分析器、AI 辅助编程)之间的桥梁。未来可能会有更丰富的”构建数据库”格式,不仅包含编译命令,还包含模块依赖图、目标属性等元数据。
结语:拥抱变化的施工哲学
回顾我们整个 CMake 系列,从第一节的”Hello World”到如今的未来展望,不变的是 CMake 的核心哲学:以目标为中心、显式声明依赖、生成底层构建描述。而变的是它不断进化的工具和语法。
无论 CMake 4.x 带来怎样的语法革新,无论 C++20 Modules 如何重构编译模型,无论云端构建如何改变我们的工作流,只要你掌握了Modern CMake 的思维方式——把那些变化当作新的”施工机械”和”建筑材料”——你就能始终站在技术潮流的前沿。
施工队长的新征程,才刚刚开始。


没有回复内容