Skip to content

Latest commit

 

History

History
301 lines (214 loc) · 15 KB

File metadata and controls

301 lines (214 loc) · 15 KB

SPEC-004:mcpp.toml 的语义与风格

规范编号 SPEC-004
标题 mcpp.toml 的平面划分、条件化形状、解析轴与命名规约
状态 草案(Draft)
版本 1.1
最后修改 2026-09-07
最低实现版本 条件化形状:mcpp 2026.8.29.1([target.<selector>.build-dependencies] 起齐备);目标轴:mcpp 2026.9.6.4
作者/维护 mcpp-community
相关设计文档 .agents/docs/2026-09-07-mcpp-toml-unified-semantics-design.md
.agents/docs/2026-06-04-manifest-schema-ownership.md
.agents/docs/2026-09-03-xlings-workspace-as-the-one-table.md
相关使用文档 docs/05 —— mcpp.toml 字段参考

规范用语

按 RFC 2119:必须(MUST)/ 禁止(MUST NOT) 强制;应当(SHOULD) 强烈建议, 偏离需理由;可以(MAY) 可选。

实现状态标记

标记 含义
已实现 当前实现与本条一致
部分实现 已有实现,语义或覆盖面有差异(差异已注明)
未实现 本规范要求但尚未支持;当前行为已注明

1. 范围

本规范陈述 mcpp.toml结构语义:一个 section 属于哪个平面、条件写在哪里、 一个条目按什么解析、键怎么命名。它不列举字段——字段在 docs/05。

它回答的是一个新字段或新 section 该长什么样,以及一份 manifest 为什么这样组织。

边界。 本规范不覆盖字段的准入条件,那由 docs/05 附录 A(Schema Ownership Principle)规定,本规范不重复它,只在 §6 引用并补充一条。

2. 平面

一份 manifest 的 section 必须落在下列平面之一。平面是"这段话在谈什么", 不是"它长什么样"。

平面 section 谈的是
身份 [package] 这个包是谁
产物 [targets.<n>][lib] 要产出什么
编译 [build][profile.<n>][toolchain] 怎么编
库依赖 [dependencies][dev-dependencies][build-dependencies] 需要哪些 mcpp 包
工具与环境 [xlings] 需要哪些载荷与工具
[features][feature-deps.<f>][feature-xlings.<f>] 什么条件下要
条件 [target.<selector>.<section>] 在哪个目标上要
产物元数据 [runtime][resources] 产出的东西是什么
生命周期 [hooks] 构建前后跑什么

状态:已实现。

库依赖与工具是两个平面,不是一个。 库有模块与 ABI,参与解析与链接;载荷是可执行 的工具或被编译对着的输入,不参与模块图。二者的解析规则不同(§4),因此禁止把工具 写进 [dependencies],也禁止把 mcpp 包写进 [xlings]

3. 条件化的唯一形状

3.1 条件在外,section 在内

条件化必须写成:

[target.<selector>.<section>]

<selector> 是目标三元组或 cfg(...) 谓词。禁止把条件写成尾部键 ([xlings.workspace.linux])或值的兄弟键。

今天接受 <section> 为:builddependenciesdev-dependenciesbuild-dependenciesfeature-deps.<f>runtimexlingsfeature-xlings.<f>

状态:已实现(上列 section)。

3.2 门可以嵌进条件

[target.<selector>.feature-deps.<f>] 合法:条件决定这个门拉进什么,不决定 这个门存不存在。feature 本身必须无条件注册,否则在不匹配的平台上请求它会 触发"未知 feature"诊断。

状态:已实现(mcpp-index#359)。

3.3 门的拼法

必须拼成顶层的 <限定词>-<section>,与 dev-dependencies 同构:

dev-dependencies      build-dependencies     ← 限定词是用途
feature-deps          feature-xlings         ← 限定词是门

禁止为第二种门发明第二种语法。一个门拉进包和一个门拉进工具,是关于同一个门的 同一句话。

不采用 [features.<f>.deps],理由是 TOML 而非风格。 [features] 的值允许写成 内联表(rules-sycl = { sources = [...] }),而 TOML 禁止用子表扩展内联表—— [features.rules-sycl.deps] 在真实 manifest 里是语法错误。要让它合法必须禁掉 内联写法,而 [features] 是 Cargo 兼容面,其值还可以是数组(default = ["base"]), 数组开不了 section。

状态:已实现。

4. 解析轴

4.1 两条轴

一个条目按宿主还是按目标解析,由它谈的是什么决定,不由它写在哪里决定。

谈的是
宿主 在构建机上执行的东西 xim:dpcppxim:shadercxim:ninja
目标 被编译对着的东西 xim:glibcxim:linux-headers、目标 sysroot

交叉构建(宿主与目标不同)时二者分叉。非交叉时二者恰好一致,因此这条差异在非交叉 构建上不可观测

4.2 写法与轴的对应

写法 状态
顶层 [xlings],值写成平台键对象 { linux=…, macosx=…, windows=…, default=… } 宿主 已实现
[target.<selector>.xlings…] 目标 已实现(2026.9.6.4)
[target.<triple>].sysroot 目标 已实现

顶层平台键对象不是遗留拼法,它是宿主轴正确的写法:工具必须能在这台机器上 执行。[target.<triple>].sysroot 是目标轴 xpkg 引用的既有先例。

4.3 两条轴的写法

[target.<selector>.xlings.workspace][target.<selector>.feature-xlings.<f>] 被接受,并按目标解析:

[xlings.workspace]
"xim:dpcpp" = "7.1.0"              # 宿主轴:它在这台机器上执行

[target.'cfg(os = "linux")'.xlings.workspace]
"xim:glibc"         = ""           # 目标轴:产物编译时对着它
"xim:linux-headers" = ""

产物编译或链接时对着的东西应当写在目标轴上;在构建机上执行的工具应当写在 顶层 [xlings]。把目标事实写在顶层在非交叉构建上恰好正确(§4.1),在交叉构建上不 正确;既有写法保持原义,被废弃。

两条轴同时命名一个包时,[target.<selector>] 一侧是更具体的陈述,必须是被采用 的那一条;实现应当报告这次覆盖。去重按而非按地址进行:xim:glibcxim:glibc@2.40 是同一次安装的两个地址,两条都保留会让环境取决于供给顺序。

[target.<selector>.xlings] 禁止接受 subos:一个工程只有一个环境,而不是每个 目标一个。实现必须报错而不是忽略。

工具的消费者看到的是两条轴的并集:xpkg_dir 与供给都读折叠后的一张表,所以一个 规则包不需要知道某个载荷是由哪条轴声明的。

由此产生一个必须说清的后果:物化出来的 .mcpp/.xlings.json 描述的是最后一次构建的 目标。 该文件是 mcpp 对"这次构建的环境"的物化,不是对"这个工程"的物化;同一个工程 先按 A 构建再按 B 构建,文件里是 B。读它的东西必须按这个含义读。

一个已发布的包,其诊断里给出的写法必须是它声明的引擎下界能接受的那一种。 诊断 文本是作者会逐字抄走的东西,推荐一种旧引擎会拒绝的写法就是把升级悬崖搬进了别人的 工程。目标轴要求 mcpp 2026.9.6.4,因此在下界低于该版本的包里,诊断应当继续给出 顶层写法。

状态:已实现(2026.9.6.4)。

4.3.1 工具的 selector 禁止命名被解析的

[target.<selector>.xlings…]<selector> 禁止命名由依赖解析回答的五个层 (c-abic++-abicompilercompiler-runtimekernel-abi)。

理由是时序而非风格:这五个层由依赖解析回答,因此命名它们的谓词被推迟到第二遍合并, 而那一遍在工具供给之后、每个包的 build.mcpp 之后。在那里被接受的条目会被声明却永远 装不上,产生的失败形态是最坏的一种:构建成功,工具不在。

accelerator 被接受,且划分依据正是上面那条时序而不是这个键的主题。它不由任何东西 解析:它是 --accel[build] accel,在第一个包被查找之前就已读入,因此以它为谓词的 条目在第一遍合并里就已折叠,与任何其他条目一样被安装。曾经把它一并拒绝的代价落在 每一个带设备孤岛的工程的每一次构建上——厂商工具包只能无条件声明或完全不声明,于是 一次 CPU-only 构建为它并不编译的设备下载数 GB。

实现必须在第一个载荷被取回之前拒绝被禁止的谓词,消息必须同时点出工具与谓词, 并指出两条出路:按目标条件化,或用 [feature-xlings.<f>] 做门——feature 在任何东西被 供给之前就已知。

状态:已实现(2026.9.6.4;accelerator 的接纳为 2026.9.6.5)。

4.3.2 目标轴不进描述符

已发布的 xim 描述符按平台分块(xpm.linuxxpm.macosxxpm.windows),而 selector 不是平台。因此目标轴条目不产生描述符边;实现必须在发布时报告,而不是把它映射到 某一块上——映射需要为每个平台假定一个代表三元组,而谓词不满足该三元组的条目会消失在 同一种沉默里。

使用者装到的东西来自顶层 [xlings.workspace](即 §4.2 的宿主轴);目标轴对"本包 自己的构建对着什么"仍然正确。

状态:已实现(2026.9.6.4)。

4.4 条件不得写两遍

[target.<selector>.xlings…] 之下的值禁止再写平台键对象:条件已经在外层, 里面再写一层就是同一个事实的两处陈述,而两处可以不一致。实现必须报错而不是 择一,并在消息里点出外层 selector——那是作者要删掉的一半,也是他没有在看的一半。

状态:已实现(2026.9.6.4)。

4.5 一个包一个版本

[xlings.workspace] / [xlings] deps / [feature-xlings.<f>] 里一条地址的身份是 (namespace, name)版本永远是这个包上的约束,不是它名字的一部分。 命名空间缺省 为 xim,与 [<ns>:]<name>[@<version>] 的解析一致。

一次构建里同一身份只安装一个版本,由两步决定:

  1. 裁决——离产物更近的声明赢:工程 > 它依赖的包。同一份 manifest 内的两条轴仍按 §4.3 的「更具体的赢」。不带版本的声明弃权:它陈述了「要这个包」而没有陈述「要 哪一版」,因此不参与这个问题。全都不带版本时,结果就是那个裸地址。
  2. 校验——赢家必须满足每一条落败的要求>= / ^ / ~ / 逗号组合是 要求;不带运算符的裸版本是选择,由裁决处理而不是校验。两条精确钉写得不同,是 两个选择,较近的那条赢并必须被报告;精确钉不满足某条要求时,实现必须拒绝 并同时点出两侧各自的声明与出路。

校验是一次比较,不是在索引里搜索。选哪一版由裁决决定,因此实现不需要「有哪些版本 可选」这个输入,也就不需要约束求解器。代价是明确的:一些求解器本可满足的组合会被拒绝 (工程写 >=8.0、规则写 8.5.0、索引最新 8.3),而拒绝消息里写着出路。

版本位接受范围表达式,并且必须在两个方向上都被求解:>=2026.1 装到满足它的最高 版本,>=2099.1 被拒绝。实现必须mcpp::xpkg_dir 回答范围——安装了却答「不 存在」,是让规则包无法声明下界的那个缺口。

状态:已实现(2026.9.6.6)。

5. 命名规约

5.1 两种 case,按面划分

case
Cargo 继承面(依赖、feature、profile) kebab dev-dependenciesdefault-featureshost-module
mcpp 自有构建面 snake include_dirscxx_runtimemodule_extensions

新键应当按它所在的面选 case。

状态:已实现(事实上一致,此前未成文)。

5.2 已发布的键不改名

已进入已发布描述符的键禁止改名。不一致处应当写成规则并注明历史来源, 而不是通过改名消除。

由此保留的已知不一致:dev-dependencies 用全词 dependencies,而 feature-deps 用简写 depsdepsdependencies 的既有简写;两种拼法都在已发布的描述符里。

状态:已实现。

6. 新增条件化的准入

除 docs/05 附录 A 的准入条件外,新的条件化需求必须先尝试用 [target.<selector>.<section>] 表达。表达不了才讨论新语法,并必须在设计文档里 说明为什么表达不了。

状态:本规范新增。

7. 判据

本规范的可检验推论:

  1. [target.<selector>.<section>] 之外不存在第二种条件写法(值的平台键对象 除外,它按 §4.2 是轴而非条件)。
  2. 目标轴与宿主轴的差异只能在解析后的目标与宿主不同时观测。因此验证 §4.3 的 测试必须跨目标,非交叉的绿色对它零信息量。判据:同一份 manifest 按两个不同 的目标各解析一次,目标轴条目随之出现与消失,而宿主轴条目两次都在 (tests/unit/test_target_xlings_axis.cpp)。
  3. §4.4 的"两处条件"必须报错,判据是一份同时写了外层 selector 与内层平台键的 manifest 被拒绝。
  4. §4.3 的"按包去重"判据:两条轴各写一次同一个包,xlings.deps 里该包只出现 一次,且是 [target.<selector>] 那条。
  5. §4.3.1 的判据:一份用被解析的层谓词声明工具的 manifest 被拒绝,且拒绝发生 在任何下载之前;而同一份 manifest 把谓词换成 accelerator 时构建通过并装上工具。 两个方向都要跑:只跑拒绝那半,一个把所有层谓词都拒掉的实现同样通过。
  6. §4.5 的判据必须同时观察「装了什么」和「答了什么」。只断言 store 里有一个版本 目录,会在一个装 A 而 xpkg_dir 答 B 的实现上通过;只断言答案,会在一个装两份的 实现上通过(tests/e2e/627_one_package_one_version.sh)。
  7. §4.5 的拒绝判据必须带反向腿:把钉抬到满足要求后同一份工程构建通过。否则一个 「凡工程与依赖同时声明同一个包就拒绝」的实现也会通过 (tests/e2e/628_a_pin_below_a_stated_floor_is_refused.sh)。

变更记录

版本 日期 变更
1.0 2026-09-07 首版。平面(§2)、条件化唯一形状(§3)、两条解析轴(§4)、命名规约(§5)、条件化准入(§6)。目标轴列为未实现。
1.1 2026-09-07 目标轴落地(mcpp 2026.9.6.4):§4.3.1 工具 selector 禁止命名目标侧层;[target.<selector>.xlings…][target.<selector>.feature-xlings.<f>] 转为已实现;§4.3 补两条轴同时命名一个包时的取舍与按包去重;§4.4 转为已实现;§7 补第 4 条判据。
1.2 2026-09-07 一个包一个版本(mcpp 2026.9.6.6):新增 §4.5(身份=(namespace, name),版本是约束;裁决与校验两步;范围必须双向可解且可被 xpkg_dir 回答);§4.3.1 改为「禁止命名被解析的层」,accelerator 明确被接受(2026.9.6.5);§7 补第 5 条的反向腿与第 6、7 条判据。