**读者:**正在判断 mcpp 适不适合手头这份工作、以及这份工作会用到它哪些功能的 开发者。
**本章回答的那一个问题:**mcpp 服务哪几类工程,而对其中一类,要用到哪些功能、 按什么顺序。
不在这里:字段参考,那是 04;示例目录,那是 03 —— 它按示例索引而不是按场景;以及命令查阅,那是 09。本章按工作本身索引。
每个场景陈述:处境、mcpp 对它贡献了什么、穿过章节的路径、可以跑的一个工程,以及 最常让人意外的那一件事。只读与手头工作相符的那个场景即可;它们之间不相互叠加。
| 场景 | 运行 | |
|---|---|---|
| 1 | 命令行工具或服务 | examples/01-hello → 03-pack-static |
| 2 | 供他人 import 的库 | examples/11-features、05-lib-distribution |
| 3 | 一个仓库里的多个包 | examples/04-workspace |
| 4 | 图形应用 | mcpp new … --template ocornut.imgui |
| 5 | 为另一个操作系统构建 | examples/06-openkal-cross |
| 6 | 没有操作系统的目标 | mcpp new … --template riscv-virt-rt |
| 7 | GPU 或加速器上的计算 | examples/09-heterogeneous |
| 8 | 图形渲染 | examples/10-graphics/offscreen |
| 9 | 工程需要的一步构建工作,以及把它共享出去 | examples/08-build-rules、12-a-new-device-language |
| 10 | 为别人打包一个工具、一个驱动或一块板子 | xim-pkgindex 与 mcpp-index 里的描述符 |
处境。 一个由 C++23 模块构建的程序,带几个依赖,而且要跑在一台没有 mcpp 的 机器上。
mcpp 贡献了什么。 import std 零配置可用;编译器是被钉住的载荷,而不是机器
上恰好有的那个;并且一条命令产出一个自带所需之物的二进制。
路径。
- 00 —— mcpp 是什么 —— 五个名词。
- 01 —— 快速开始 —— 把程序跑起来。
- 05 —— 依赖与解析 ——
[dependencies]、锁文件。 - 08 —— 测试 ——
tests/**/*.cpp。 - 20 —— 工具链管理 —— 当编译器版本要紧,或者工程必须钉住 一个的时候。
- 10 —— 发布打包 ——
mcpp pack。
跑。 examples/01-hello,然后
02-with-deps,再
03-pack-static。
最让人意外的一件事。 debug 构建与 release 构建互不失效。每种配置在 target/
下有自己的指纹目录,所以在两者之间来回切换不是一次重建。
处境。 别的包会把它写进 [dependencies] 的代码,其中可能有可选的部分。
mcpp 贡献了什么。 模块接口就是被发布的表面;feature 让其中一部分成为可选而 不必拆出第二个包;预建二进制可以声明它与哪些工具链兼容。
路径。
- 04 —— mcpp.toml 工程文件指南 ——
[lib]、库根。 - 06 —— Feature 与能力 —— 可选部分,以及它们 拉取的依赖。
- 08 —— 测试 ——
[dev-dependencies]。 - 11 —— 发布一个库 —— 描述符。
- 12 —— 分发预编译库 —— 如果同时交付二进制。
跑。 examples/11-features 把三种 feature 形态
都声明了一遍;05-lib-distribution 是
生产方与消费方成对的那个。
最让人意外的一件事。 判断一个可选后端是否真的可选,判据不是「默认构建仍然 能过」,而是「默认构建的解析里不出现那个包」。一个被解析出来却没被用到的依赖, 仍然要付下载的代价。
处境。 多个包一起开发,彼此按路径依赖。
mcpp 贡献了什么。 一条命令构建或测试整组;成员之间不经注册表就能相互解析; 每个成员保留自己的 manifest 与身份。
路径。
- 07 —— 工作空间 ——
[workspace]、path 依赖。 - 05 —— 依赖与解析 —— path 依赖做什么、不做什么。
- 08 —— 测试 —— 扇出。
最让人意外的一件事。 工作空间的成员不是一个根。任何枚举「每一个包」的工具 都必须说清它指的是两者中的哪一个,而答案会改变构建什么。
处境。 一个带窗口、渲染器与字体的桌面程序。
mcpp 贡献了什么。 窗口与渲染栈就是普通依赖;模板脚手架出一个能跑的工程;产物 在运行期需要什么是被声明的,不是被发现的。
路径。
- 01 —— 快速开始 ——
mcpp new --template。 - 05 —— 依赖与解析 —— 那套栈。
- 10 —— 发布打包 —— 可执行文件旁边还要带什么。
跑。 mcpp new myapp --template ocornut.imgui。
最让人意外的一件事。 在 Linux 上,mcpp 的产物跑在一个不查 /usr/lib 的
私有加载器后面。任何必须由宿主供给的东西 —— 图形驱动、Vulkan ICD —— 都经由工程
声明的适配包到达,而不是靠它装在机器上。
处境。 一份源码树要产出 Linux、Windows 与 macOS 的二进制。
mcpp 贡献了什么。 目标是一个参数,不是第二份检出;交叉工具链是载荷;而这台 宿主服务不了的目标会被拒绝,不会被悄悄按宿主构建。
路径。
- 21 —— 目标三元组 —— 目标怎么命名,哪台宿主服务哪个。
- 24 —— 基于 openkal 的交叉构建 —— 机制。
- 22 —— 目标侧 —— manifest 必须按目标不同时。
跑。 examples/06-openkal-cross —— 一个
程序在任意宿主上为四个目标构建。
最让人意外的一件事。 --target 不要求机器上已经有那条工具链。它要求的是
这个目标能被这台宿主服务;21 里的支持矩阵说明哪些
可以。
处境。 板子或单片机上的固件:没有 OS、默认没有 libc、有链接脚本与向量表。
mcpp 贡献了什么。 板级支持包供给整个目标世界 —— 链接脚本、启动代码,以及
runner —— 于是 mcpp run 在模拟器上与在真实硬件上是同一条命令。
路径。
- 40 —— 裸机与 freestanding 目标 —— 目标、档位、
std还剩什么。 - 41 —— 抵达一台设备 —— runner、具名 runner。
- 08 —— 测试 —— 跑在板子上的测试。
跑。 mcpp new blinky --template riscv-virt-rt。
最让人意外的一件事。 模拟器与真实板子相差的是一个 feature,不是两个包。
mcpp run --features hardware 移动的是默认 runner;开发者敲的那条命令不变。
处境。 程序的一部分是由厂商编译器编译、再链接进普通二进制的 kernel。
mcpp 贡献了什么。 设备工具包由规则包声明、由构建安装;加速器是一条只写一次 的轴;产物记录下它携带了哪些设备的代码。
路径。
- 42 —— 异构硬件构建 ——
accel、岛、接缝。 - 30 —— 构建程序 —— 规则怎样到达图。
- 06 —— Feature 与能力 —— 选中一条 lane 的 那个 feature。
跑。 先
examples/09-heterogeneous/boundary
—— 它不需要设备 —— 再
…/cuda。
最让人意外的一件事。 不点名加速器的构建一个字节都不下载。多 GB 的工具包
被取之前要开两道闸:选中规则的那个 feature,以及说明「这次构建确实为设备编译」的
cfg(accelerator = …) 选择器。
处境。 编译到 SPIR-V 的着色器、一条管线,以及必须正确的像素。
mcpp 贡献了什么。 着色器编译器是被声明的载荷;编译好的着色器以模块到达, 而不是一个没人写过其名字的生成头;并且渲染结果可以在没有 GPU 的情况下被断言。
路径。
- 42 —— 异构硬件构建 —— 着色器那条 lane。
- 30 —— 构建程序 —— 要模块表面的那一行。
- 10 —— 发布打包 —— 交付它。
跑。 examples/10-graphics/offscreen
—— 离屏渲染一个三角形,并与软件光栅器逐像素比对。
最让人意外的一件事。 软件设备不自动等价于硬件的替身。框架可能因为设备的类型 就拒绝它,哪怕它满足框架要求的每一项;那是框架自己的策略,不是打包缺陷。
处境。 代码生成、嵌入资源、一项检查,或者第二个编译器 —— 引擎没有规则的东西。
mcpp 贡献了什么。 这一步变成与其余一切同一张图上的边:被定序、进指纹、可增量, 失败时按自己的名字被报出。它不是一个构建前脚本。
路径。
- 30 —— 构建程序 ——
mcpp::action、四种 role。 - 31 —— 编写规则包 —— 如果别的工程也该用上它。
- 23 —— 项目环境 —— 声明这一步要跑的工具。
跑。 examples/08-build-rules 是做检查与
嵌入的规则;12-a-new-device-language
教会 mcpp 一门引擎从未听说过的语言。
最让人意外的一件事。 这一步调用的那个工具本身是它的输入。少了这一条,改动 生成器会让每条边都是干净的,产物保留上一个生成器产生的字节 —— 一次覆盖在陈旧结果 之上的绿色构建。
处境。 别的工程应当能够声明并得到的东西:一个编译器、一个着色器编译器、一个 模拟器、一个宿主图形驱动、一块板子。
mcpp 贡献了什么。 消费者按名字声明它就能得到一个能用的程序 —— 载荷被安装、它的 库进了产物的搜索路径,而档位决定「一次从不运行的构建要不要为它付代价」。
路径。
- 32 —— 编写一个载荷 —— 由 mcpp 安装的工具或预编译库。
- 33 —— 编写运行时适配包 —— 当那个库属于宿主、 不可再分发时。
- 34 —— 编写板级支持包 —— 一块板子、它的内存布局,以及 抵达它的方式。
- 31 —— 编写规则包 —— 如果有一步构建工作要驱动 这个工具。
跑。 描述符本身:载荷看 xim-pkgindex/pkgs/g/glslang.lua,适配包看
mcpp-index/pkgs/c/compat.vulkan-runtime.lua,板子看 mcpplibs/cortex-m-rt。
最让人意外的一件事。 该用哪个答案,由许可证与 ABI 决定,不由偏好决定。 用户态与内核模块处于锁步、且许可证禁止再分发的驱动不能是载荷;它是宿主能力, 而适配包是产物够到它的方式。开源驱动就是载荷,用着它的机器完全不需要适配包。
- 这里列出的场景,都是背后有可运行的工程或已发布模板的那些。mcpp 服务得了、 但本仓库没有任何东西演示的场景不在此列 —— 一条没有工程可跑的路径是一个主张, 不是一个场景。
- 在既有构建系统内部采用 mcpp 不是这里的场景。mcpp 构建它自己拥有的工程;与另一个 构建系统的产物互操作既无文档也无示例覆盖。