11# 00 —— mcpp 是什么
22
3- ## 背景: C ++ 工程侧的工具现状
3+ ## 背景: C++ 工程侧的工具现状
44
5- 一个 C++ 工程需要四样东西同时成立: 一份构建描述、一组依赖、一个新到足以编译这份
6- 代码的编译器, 以及一个能让产物真正运行起来的环境。C++ 没有任何一个工具同时负责
7- 这四样, 它们由四类互不隶属的工具分别承担, 而把它们对齐是工程自己的工作。
5+ 一个 C++ 工程需要四样东西同时成立: 一份构建描述、一组依赖、一个新到足以编译这份
6+ 代码的编译器, 以及一个能让产物真正运行起来的环境。C++ 没有任何一个工具同时负责
7+ 这四样, 它们由四类互不隶属的工具分别承担, 而把它们对齐是工程自己的工作。
88
9- CMake 是其中第一样的事实标准。事实标准陈述的是采用率, 不是使用体验: CMake 不解析
10- 依赖、不安装编译器、也不描述运行期环境, 而这三件恰好是新参与者在第一天遇到的。
11- 补齐它们意味着再引入一个包管理器、发行版的软件包, 以及 README 里的一段环境说明
12- —— 三套新的模型, 而它们之间的对齐仍然落在工程身上。
9+ CMake 是其中第一样的事实标准。事实标准陈述的是采用率, 不是使用体验: CMake 不解析
10+ 依赖、不安装编译器、也不描述运行期环境, 而这三件恰好是新参与者在第一天遇到的。
11+ 补齐它们意味着再引入一个包管理器、发行版的软件包, 以及 README 里的一段环境说明
12+ —— 三套新的模型, 而它们之间的对齐仍然落在工程身上。
1313
14- 最经常失效的是环境这一层, 因为只有它没有任何东西在检查。构建描述会在配置阶段
15- 报错, 包管理器会解析失败, 而「机器上的库不是这份代码构建时所依据的那一份」在
16- 链接之前不产生任何信号, 有时到程序运行时才产生。
14+ 最经常失效的是环境这一层, 因为只有它没有任何东西在检查。构建描述会在配置阶段
15+ 报错, 包管理器会解析失败, 而「机器上的库不是这份代码构建时所依据的那一份」在
16+ 链接之前不产生任何信号, 有时到程序运行时才产生。
1717
18- 模块给这个结构再加一条约束。C++20 的模块改变的是一个翻译单元的代价: 接口只声明
19- 一次并被 import, 而不是被每个需要它的文件从头文件里重新解析一遍; 消费者看到的是
20- 作者导出的东西, 不是头文件恰好 include 进来的一切。代价是这个语言特性由构建系统
21- 实现: 构建要扫描源里的 ` import ` 、据此给编译定序、缓存编译好的接口并正确地让它
22- 失效, 而且要在一个新到具备这个特性的编译器上完成这些。` import std ` 再加一条:
23- 标准库自己的模块必须先被构建出来, 别的东西才能用它。
18+ 模块给这个结构再加一条约束。C++20 的模块改变的是一个翻译单元的代价: 接口只声明
19+ 一次并被 import, 而不是被每个需要它的文件从头文件里重新解析一遍; 消费者看到的是
20+ 作者导出的东西, 不是头文件恰好 include 进来的一切。代价是这个语言特性由构建系统
21+ 实现: 构建要扫描源里的 ` import ` 、据此给编译定序、缓存编译好的接口并正确地让它
22+ 失效, 而且要在一个新到具备这个特性的编译器上完成这些。` import std ` 再加一条:
23+ 标准库自己的模块必须先被构建出来, 别的东西才能用它。
2424
25- 于是采用模块的工程需要上述四层全部成立, 而不是其中三层。这个落差是具体的。在写
26- 这一章的这台机器上:
25+ 于是采用模块的工程需要上述四层全部成立, 而不是其中三层。这个落差是具体的。在写
26+ 这一章的这台机器上:
2727
2828``` console
2929$ g++ --version
3030g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
3131```
3232
33- 这个编译器编不了 ` import std ` 。工程本身没有任何问题; 是这台机器不是这个工程需要
33+ 这个编译器编不了 ` import std ` 。工程本身没有任何问题; 是这台机器不是这个工程需要
3434的那台机器。
3535
3636## 这一现状的代价
3737
38- ** 对人, 代价是一次搭建** , 每台机器一次、每个新参与者一次: 装一个更新的编译器、
39- 确定哪些构建旗标把模块打开、取得依赖, 然后在链接失败时判断坏的是这三者中的哪一
40- 件。这个代价不随工程成熟而下降, 它按人数和机器数重复。
38+ ** 对人, 代价是一次搭建** , 每台机器一次、每个新参与者一次: 装一个更新的编译器、
39+ 确定哪些构建旗标把模块打开、取得依赖, 然后在链接失败时判断坏的是这三者中的哪一
40+ 件。这个代价不随工程成熟而下降, 它按人数和机器数重复。
4141
42- ** 对 agent, 代价是上下文** , 并且在写下第一行代码之前就消耗在三处: 读构建描述以
43- 确定它究竟做了什么、重建它所假定的环境,以及顺着一个头文件穿过传递 include 去
44- 确定究竟声明了什么 。
42+ ** 对 agent, 代价是上下文** , 并且在写下第一行代码之前就消耗在三处: 读构建描述以
43+ 确定它究竟做了什么、重建它所假定的环境,以及顺着一个头文件的传递 include 一路
44+ 追下去,才能确定其中究竟声明了什么 。
4545
46- 模块消掉了第三处, 因为接口是显式的, ` import ` 陈述了用到什么。mcpp 消掉另外两处,
46+ 模块消掉了第三处, 因为接口是显式的, ` import ` 陈述了用到什么。mcpp 消掉另外两处,
4747并把第一处收敛为一条命令。
4848
4949## mcpp 的组成
5050
5151```
52- mcpp = 通用构建系统
53- + 构建插件
54- + 包管理
55- + 工具链管理
56- + 环境与运行时 (xlings)
52+ mcpp = build system
53+ + build plugins
54+ + package manager
55+ + toolchain management
56+ + the environment and runtime (xlings)
5757```
5858
59- 多数 C++ 工程要把这五个部分从不同工具里拼起来, 而上面那份搭建代价正是拼装本身的
60- 代价: 构建文件假定机器上有某个编译器, 包管理器假定一份不是它写的构建文件, 而环境
59+ 多数 C++ 工程要把这五个部分从不同工具里拼起来, 而上面那份搭建代价正是拼装本身的
60+ 代价: 构建文件假定机器上有某个编译器, 包管理器假定一份不是它写的构建文件, 而环境
6161是 README 里的一段话。
6262
63- mcpp 是一个程序, 五个部分共用同一套模型。
63+ mcpp 是一个程序, 五个部分共用同一套模型。
6464
65- 对于已经在用相应工具的读者:
65+ 对于已经在用相应工具的读者:
6666
6767| 组成部分 | 在 mcpp 中的形式 | 可对照的工具 |
6868| ---| ---| ---|
6969| 通用构建系统 | ` mcpp.toml ` 、模块图、ninja 后端 | CMake、Meson |
7070| 构建插件 | ` build.mcpp ` 、规则包 | ` build.zig ` 、xmake rules |
7171| 包管理 | ` [dependencies] ` 、` mcpp.lock ` 、索引 | Conan、vcpkg |
72- | 工具链管理 | 编译器作为被安装并钉住的载荷 | Zig 自带的工具链、rustup、手工安装 GCC / LLVM / MSVC |
72+ | 工具链管理 | 已安装并钉版本的编译器载荷 | Zig 自带的工具链、rustup、手工安装 GCC / LLVM / MSVC |
7373| 环境与运行时 | ` [xlings] ` 、载荷、运行期搜索路径 | Nix、conda |
7474
75- 整体上最接近的两个类比是 Cargo 与 Zig, 二者各对应同一个想法的一半。Cargo 是一个
76- 程序同时充当构建、包管理、锁文件与测试运行器, 于是一个 Rust 工程克隆下来直接构建、
77- 没有前置步骤。Zig 把工具链随工具一起发布, 并且默认支持交叉编译, 于是编译器不是
75+ 整体上最接近的两个类比是 Cargo 与 Zig, 二者各对应同一个想法的一半。Cargo 是一个
76+ 程序同时充当构建、包管理、锁文件与测试运行器, 于是一个 Rust 工程克隆下来直接构建、
77+ 没有前置步骤。Zig 把工具链随工具一起发布, 并且默认支持交叉编译, 于是编译器不是
7878机器必须先具备的东西。
7979
80- mcpp 在 C++ 上是这个形状, 外加二者都没有的一部分: 环境层 —— 工程通过它声明构建
80+ mcpp 在 C++ 上是这个形状, 外加二者都没有的一部分: 环境层 —— 工程通过它声明构建
8181所需的非编译器工具。
8282
83- ** 这张表为各部分定位, 不宣称等价。** 表中每个工具在它自己的领域里做的都比 mcpp
84- 多, 需要那种深度的工程使用它。
83+ ** 这张表为各部分定位, 不宣称等价。** 表中每个工具在它自己的领域里做的都比 mcpp
84+ 多, 需要那种深度的工程使用它。
8585
8686## 核心保证
8787
88- > ** 拿到任何一个 mcpp 工程, ` mcpp build ` 就能构建** —— 不需要自行安装编译器、
89- > 配置环境, 也不需要寻找依赖。
88+ > ** 拿到任何一个 mcpp 工程, ` mcpp build ` 就能构建** —— 不需要自行安装编译器、
89+ > 配置环境, 也不需要寻找依赖。
9090
91- 这里写下两条边界, 以便这句话可以被依赖: 面向设备的工程第一次构建仍会下载该设备的
92- 工具包; 而这台机器服务不了的目标会被点名拒绝, 不会被错误地构建出来。
91+ 这里写下两条边界, 以便这句话可以被依赖: 面向设备的工程第一次构建仍会下载该设备的
92+ 工具包; 而这台机器服务不了的目标会被点名拒绝, 不会被错误地构建出来。
9393
9494## 最小示例
9595
96- 还是那台机器, 唯一的 C++ 编译器是上面那个 GCC 13。
96+ 还是那台机器, 唯一的 C++ 编译器是上面那个 GCC 13。
9797
9898``` console
9999$ mcpp new hello
100100Created bin package 'hello' at /tmp/zero-demo/hello
101101Next: cd hello && mcpp build && mcpp run (or `mcpp test`)
102102```
103103
104- 四个文件, manifest 五行:
104+ 四个文件, manifest 五行:
105105
106106``` toml
107107[package ]
@@ -112,7 +112,7 @@ license = "Apache-2.0"
112112```
113113
114114** 没有声明编译器、没有声明语言档位、没有声明任何依赖。** 这就是一个读者在改动
115- 这个工程之前需要理解的全部。而源码用的正是这台机器的编译器不具备的那个特性:
115+ 这个工程之前需要理解的全部。而源码用的正是这台机器的编译器不具备的那个特性:
116116
117117``` cpp
118118import std;
@@ -133,17 +133,17 @@ Hello from hello!
133133Built with import std + std::println on modular C++23.
134134```
135135
136- 墙钟 1.25 秒, 含首次运行。完成这次构建的编译器:
136+ 墙钟 1.25 秒, 含首次运行。完成这次构建的编译器:
137137
138138``` console
139139$ mcpp self env
140140default toolchain = gcc@16.1.0
141141```
142142
143- mcpp 安装了 GCC 16 并使用它。宿主上没有任何东西被改动, 而克隆这个工程的同事 ——
144- 或者 agent —— 得到的是同一个编译器, 不是那台机器恰好自带的那个。
143+ mcpp 安装了 GCC 16 并使用它。宿主上没有任何东西被改动, 而克隆这个工程的同事 ——
144+ 或者 agent —— 得到的是同一个编译器, 不是那台机器恰好自带的那个。
145145
146- ** 增加一个依赖是一行, 不需要其他步骤: **
146+ ** 增加一个依赖是一行, 不需要其他步骤: **
147147
148148``` toml
149149[dependencies ]
@@ -154,18 +154,18 @@ mcpp 安装了 GCC 16 并使用它。宿主上没有任何东西被改动,而克
154154
155155## 适用范围
156156
157- mcpp 围绕 ** C++20/23 模块与较新的语言特性** 建立, 它维护的生态由此而来, 而不是
158- 来自一个泛化的目标:
157+ mcpp 围绕 ** C++20/23 模块与较新的语言特性** 建立, 它维护的生态由此而来, 而不是
158+ 来自一个泛化的目标:
159159
160160| | |
161161| ---| ---|
162162| 模块化 C++ | ` import std ` 零配置、模块扫描、跨工程 BMI 缓存 |
163163| 嵌入式与裸机 | freestanding 目标、板级支持包、从源码到一个可运行镜像只需一条命令 |
164- | 异构计算与 GPU | CUDA、HIP、SYCL、Vulkan/SPIR-V 与 Ascend C, 每一条都是规则包而不是引擎特性 |
165- | 图形 | 着色器作为构建的一部分被编译, 并以模块到达 |
164+ | 异构计算与 GPU | CUDA、HIP、SYCL、Vulkan/SPIR-V 与 Ascend C, 每一条都是规则包而不是引擎特性 |
165+ | 图形 | 着色器随构建一起编译, 并以模块到达 |
166166| 内核与底层 | 零 libc 档、显式的链接模型、没有隐藏的宿主依赖 |
167167
168- 不需要其中任何一项的工程, 同样得到上述保证; 需要其中之一的工程, 不必离开这个
168+ 不需要其中任何一项的工程, 同样得到上述保证; 需要其中之一的工程, 不必离开这个
169169工具去得到它。
170170
171171## 阅读路径
@@ -176,5 +176,5 @@ mcpp 围绕 **C++20/23 模块与较新的语言特性**建立,它维护的生态
176176| 判断 mcpp 是否适用于当前工作 | [ 02 —— 场景] ( 02-scenarios.md ) |
177177| 阅读一个结构相近的工程 | [ 03 —— 示例项目] ( 03-examples.md ) |
178178
179- 本章之后的一切都是参考: manifest 可以陈述什么、依赖怎样解析、目标怎样命名。本章
179+ 本章之后的一切都是参考: manifest 可以陈述什么、依赖怎样解析、目标怎样命名。本章
180180按设计不写任何字段与旗标。
0 commit comments