| 项 | 值 |
|---|---|
| 规范编号 | SPEC-002 |
| 标题 | 目标侧模型与能力声明(mcpp: 保留命名空间) |
| 状态 | 评审中 v1.0 |
| 最后修改 | 2026-08-24 |
| 对应实现 | mcpp >= 2026.8.24.2 |
| 相关设计文档 | .agents/docs/2026-08-24-target-side-design.md |
| 使用文档 | docs/22 - 目标侧 |
本规范定义一次构建的目标侧由哪些层构成、每一层可以由谁供给、 供给者与需求者如何声明,以及引擎据此执行的规则。
用语按 RFC 2119:必须 / 禁止(强制)、应当(强烈建议)、可以(可选)。
一次构建的目标侧必须由且仅由以下五层构成:
| 层名 | 内容 |
|---|---|
compiler |
执行编译的程序 |
compiler-runtime |
编译器自身的运行时:builtins 与展开器 |
kernel-abi |
平台接口或其等价物 |
c-abi |
C 库 |
c++-abi |
C++ 库及其 ABI 运行时 |
层名是编译进引擎的闭集。实现名禁止出现在引擎代码中。
除 compiler 外的每一层可以由包供给。compiler 禁止由包供给:
编译器是引擎安装并驱动的载荷,族与族之间的差异是引擎必须持有的事实。
包可以要求某个 compiler。
每一层的来源必须是以下四者之一:
| 来源 | 报告用词 | 可知时刻 |
|---|---|---|
| 编译器载荷 | payload |
依赖解析之前 |
| 被点名的预制载荷 | prebuilt |
依赖解析之前 |
| 依赖图中的包 | graph |
依赖解析之后 |
| 无 | — |
— |
目标侧必须在依赖解析完成之后解析,且必须只解析一次。
mcpp:<层名>[=<实现名>]
<层名> 必须属于 §1.1 的闭集;不属于时的处置见 §5.1(根工程报错、依赖警告)。
不以 mcpp: 开头的条目属于特性系统,引擎必须原样透传。
省略 =<实现名> 时,该层的实现名取包名。
与 provides 同语法。语义为:被命名的层必须解析为被命名的实现,
否则引擎必须在编译开始之前拒绝该构建。
省略 =<实现名> 时,语义为该层必须有供给者。
供给 c++-abi 的包可以在 [build] 下声明:
| 键 | 类型 | 含义 |
|---|---|---|
std-module |
路径 | std 模块源,相对包根 |
std-compat-module |
路径 | std.compat 模块源 |
std-module-flags |
字符串数组 | 编译该模块源所需的 flag |
三者的 [package] 写法必须继续被接受。[build] 写法应当优先,
因为只有它可以按 [target.'cfg(...)'.build] 条件化。
声明了 std-module 而没有相应 provides 条目的包,引擎必须报错。
部分实现:std-module 与 std-compat-module 是单一路径,不可条件化;
仅 std-module-flags 参与条件合并。
预制载荷的描述符应当能够声明它供给哪一层、该层在载荷内的位置、
以及该层的真实版本。当前实现从载荷的包名切出接口名,
因此一个名为 musl-gcc 的载荷在供给 c-abi 时被报告为 musl-gcc 而非 musl。
在其落地之前,预制载荷供给的层由 [target.<三元组>].sysroot 与目标表的
sysroot 列点名,接口名取包名。
同一层出现两个供给者时,引擎必须在解析期报错, 并必须同时指出两个包及各自进入依赖图的路径(直接依赖或传递依赖)。
同一个包在 provides 中为同一层给出多条条目时,视为一个供给者;
其中命名了实现名的一条必须优先。
一个实现必须在它曾被配置的层之上使用。引擎必须执行:
- 编译器载荷的
c++-abi,仅在c-abi同样来自该载荷时可用; - §2.2 声明的每一条
requires。
「compiler-runtime 必须与 compiler 同族」当前依赖供给者自行声明
requires,引擎不独立推断 —— 因为推断需要一张实现名到族的映射表,
而 §1.1 禁止引擎持有实现名。
两层来自同一来源时,引擎禁止介入它们之间的关系。 两层来自不同来源时,只有引擎同时知道两边的地址,因此必须由引擎接线。
当前实现覆盖「两层均来自图」与「两层均来自载荷」。 「一层预制、一层来自图」的接线尚不完整。
三元组的 env 段必须被当作对 c-abi 的请求,而非其答案。
- 段缺席 ⇒ 未作陈述,任何供给者都不与之矛盾;
- 段存在且与解析出的
c-abi不同,且后者来自图 ⇒ 引擎必须报出该不一致, 并必须给出不含该段的目标拼写;禁止据此使构建失败。
拒绝曾被实现并被实测否掉:它打破了每一个把宿主目标拼作 x86_64-linux-gnu
的工程与 CI 配置 —— 而那正是 mcpp toolchain list 打印的拼写。
判据是该请求不改变任何东西:图两种写法下都供给同一个 C 库,
因此该段是被忽略而非被违反。
规范化会把 x86_64-linux 写成 x86_64-linux-gnu,因为身份必须是全的。
请求必须在规范化之前捕获;报告应当显示工程书写的拼写。
目标行的 pin 命名的是供给该目标 C 库的载荷,不是偏好的编译器。
它必须仅在两个条件同时成立时生效:清单对该目标未作陈述,
且依赖图中无人供给 kernel-abi 或 c-abi。
第二个条件在依赖解析之后才可知,因此工具链必须在其之后解析。
过早决定被双向实测否掉:无条件应用会替换用户用 mcpp toolchain default
设下的工具链;不应用会让一个零依赖的交叉构建从可用变为不可用。
构建必须报告解析出的结果而非清单中的意图。 默认应当只列出来源不是编译器载荷的层;五层均来自载荷时应当只输出目标行。
MCPP_VERBOSE=1 时必须列出全部五层。
诊断必须列出该判断所依据的每一层,包含来源为编译器载荷的层。
报告应当使用与 [dependencies] 中一致的包名拼写。
全限定名应当仅在以下三种情形出现:MCPP_VERBOSE、
同一次构建中两个短名相同、以及诊断信息。
谁的清单决定答案。
- 出现在根工程清单中的未知层名,引擎必须报错。 这是作者自己的拼写,而他正看着这次构建。
- 出现在依赖清单中的未知层名,引擎禁止据此使构建失败; 必须警告并忽略该层。该清单是对着一个更新的引擎写的。
清单中其它位置的未知键,引擎禁止据此使整份清单加载失败;应当警告并忽略。
第二条来自一次实测。在其落地之前,一个声明了新层名的包 在该层被命名之前发布的每一个引擎上都无法加载,因此层名词表对已发布的包 永远不可扩展:
error: dependency 'openkal-llvm-runtime': mcpp.toml: error:
`provides = ["mcpp:compiler-runtime=compiler-rt"]` names no
capability mcpp knows.
这条规定只在未来的引擎上生效。一个包若要声明某个层名, 其使用者的引擎仍须不早于该层名被引入的版本。
- 能力名
hosted-standard-library必须继续表示c++-abi层; - 工具链族拼写
openkal-llvm必须继续解析,归一为llvm; [package]下的三个std-module*键必须继续被接受。
| 版本 | 日期 | 变更 |
|---|---|---|
| v1.0 | 2026-08-24 | 初版。五层闭集、provides/requires 语法、三条规则、报告与兼容性条款。 |