| 项 | 值 |
|---|---|
| 规范编号 | SPEC-006 |
| 标题 | 工具链管理:身份、来源、选择与载荷契约 |
| 状态 | 草案 v0.2 |
| 最后修改 | 2026-09-24 |
| 对应实现 | 逐条标注;标为「已实现」的条款对应 mcpp >= 2026.9.24.1。标为「未实现」的条款计划与下一批 LLVM 工具链一同落地,届时按实测修订本规范 |
| 相关设计文档 | .agents/docs/2026-09-24-toolchain-selection-and-payload-trust-design.md、.agents/docs/2026-09-24-685-687-msvc-stl-and-toolchain-payloads.md |
| 相关 issue | mcpp#685、mcpp#687 |
| 使用文档 | docs/20 - 工具链、docs/32 - 编写载荷、docs/91 - 工具链内部 |
本规范定义 mcpp 对工具链的命名、选择和使用方式,以及一个工具链载荷在发布前必须满足的条件。 目标侧的层模型见 SPEC-002;清单的平面划分与解析轴见 SPEC-004。
用语按 RFC 2119:必须 / 禁止(强制)、应当(强烈建议)、可以(可选)。
| 术语 | 含义 |
|---|---|
| 工具链 | 执行编译的程序及其随附部分:编译器驱动、编译器运行时(builtins 与展开器)、随编译器发布的 C++ 标准库、汇编器、链接器与归档工具 |
| 载荷 | 由 xlings 从索引安装的预构建目录树,身份为命名空间、名字与版本 |
| 来源 | 工具链或 sysroot 的出处:managed(生态包)或 system(在本机定位) |
| sysroot | 编译时所针对的目标环境。Linux 目标上是 C 库载荷;MSVC ABI 目标上是 MSVC toolset(STL、vcruntime、CRT)及其 Windows SDK |
| 构建环境 | 产出一个载荷的机器或容器,以及其中的 C 库 |
工具链必须写作 <族>@<版本>,族为 gcc、llvm、msvc、emsdk、android-ndk。
部分版本必须解析为匹配的最高版本。
只有 msvc 有系统来源,写作 msvc@system。其他族写 @system,必须在读取处被拒绝。
不带族的 system(PATH 上的编译器)必须被拒绝,拒绝信息给出可用的写法。
xim:<族>@<版本> 表示只取生态包。对没有系统来源的族,它与不带前缀的写法等价,规范写法去掉前缀;
对 msvc,前缀改变含义,规范写法保留前缀。xim:msvc@system 与 xim: 之外的命名空间必须被拒绝。
msvc@<版本> 必须先在本机已安装的 toolset 中匹配,匹配不到再取生态包;规则见 §3.4。
mcpp toolchain default msvc@<版本> 在本机已有该 toolset 时必须直接接受,不要求先安装生态包。
前缀必须按版本分量匹配:14.4 匹配 14.4.x,不匹配 14.44。「最高」必须按数字元组比较。
当前:本机 MSVC toolset 的匹配与比较按本条实现;其他族的前缀匹配方式未逐一核对。
一次构建中,工具链及其 sysroot 必须只解析一次;编译、依赖扫描、标准库模块、链接与缓存键必须读取同一个结果。
clang 以 MSVC ABI 为目标时,所选 toolset 与 SDK 以 -Xmicrosoft-visualc-tools-root、-Xmicrosoft-windows-sdk-root、
-Xmicrosoft-windows-sdk-version 显式交给编译器驱动,std.ixx 取自同一个 toolset。
项目写明的版本必须优先于环境变量与本机扫描;被忽略的环境变量必须以一行说明报告。
| 条目 | 状态 |
|---|---|
受管 MSVC 忽略 WindowsSdkDir 并报告 |
已实现 |
VSINSTALLDIR 优先于 vswhere |
已实现 |
写明版本时忽略 VCToolsInstallDir 与 VSINSTALLDIR,并报告 |
已实现 |
凡由探测得到的选择,其来源、版本与 SDK 必须打印在构建输出中,写入 resolution.json,并进入缓存键。
MSVC ABI 目标上:SDK 以 ucrt@<版本> 进入运行时身份;clang 行的 toolset 与 SDK 写入 resolution.json 的
msvc_toolset 与 windows_sdk,toolset 目录与 SDK 版本进入缓存键,stdlibVersion 记为 toolset 的版本。
本机候选必须覆盖全部 VS 实例(含预发布版)及每个实例下的全部 toolset。
msvc@system 必须按以下顺序取第一个完整的候选:
VCToolsInstallDir所指的 toolset;VSINSTALLDIR或VCINSTALLDIR所指实例的默认 toolset;PATH上cl.exe所在的 toolset;- 带 C++ 组件、且安装版本最高的实例的默认 toolset;
- 没有 vswhere 时,扫描固定路径得到的候选。
带版本的写法必须依次尝试本机候选、已安装的生态包、索引;三者都没有时,必须报错并列出三类候选。
「完整」指本次构建所需的文件都在:库目录;需要 import std 时的 modules/std.ixx;cl.exe 行的 cl.exe。
不完整的候选必须被跳过,并报告。
cl.exe 行与 clang 行使用同一个选择。
在 *-windows-msvc 目标上,编译器是工具链,MSVC toolset 是 sysroot。
[target.<三元组>].sysroot在这些行上可以写作msvc@system、msvc@<版本>或xim:msvc@<版本>,含义同 §2。- 未写时必须等同于
msvc@system。 - 编译器为
cl.exe时,sysroot 必须是该编译器所属的 toolset;另写一个不同的 sysroot,必须被拒绝。 - SDK 必须跟随 toolset 的来源:生态包 toolset 用随其安装的
windows-sdk载荷,本机 toolset 用本机扫描的结果。 - 目标侧报告(SPEC-002)中,c-abi 与 c++ 两层记为预制来源;具体的 toolset 与 SDK 记入
resolution.json(§3.3)。 - 该值禁止作为 C 库包进入项目环境的安装集合。
描述产物的属性(最低系统版本、三元组中的版本段)必须按目标判定,与宿主无关; 只有在宿主上执行的编译(build.mcpp)按宿主判定。macOS 的 deployment target 在任何宿主上都按目标解析与施加。
载荷在使用时禁止依赖构建环境中的路径。以下文件含构建环境的路径,但不参与编译与链接,可以豁免:
libtool 的 .la;gcc 的 plugin/include/configargs.h 与 install-tools/mkheaders.conf。
gcc 载荷的 lib/gcc/<三元组>/<版本>/include-fixed/ 禁止含带 fixincludes 横幅(auto-edited by fixincludes)的头文件。
这类头文件是构建环境 C 库的冻结副本,在搜索顺序上排在构建所用的 C 库之前。gcc 自己生成、不带横幅的头文件不受此限。
当前:gcc 13.3.0、15.1.0 与 11.5.0 的 x86_64-linux-gnu 发布归档含这类文件(mcpp#687)。索引中的 gcc 配方在安装时删除它们(openxlings/xim-pkgindex#870 之后生效);
在此之前安装的载荷由 mcpp self doctor 报告(§6.4)。故标为部分实现:安装后的载荷满足本条,发布归档不满足。
| 要求 | 状态 |
|---|---|
安装后的改写必须限于一张列出的清单:ELF 的 PT_INTERP 与 RUNPATH、clang 的 .cfg |
部分实现:改写集中在单一入口,清单未写成规范 |
改写必须由标记文件 .mcpp-fixup.json 记录 |
已实现 |
| 改写必须经副本与原子重命名完成 | 部分实现:mcpp 的安装后修正管线如此;xlings 安装时所做的改写未核对 |
| 清单之外的文件必须与发布归档一致 | 未实现:没有检查 |
gcc 的 specs 禁止在安装时改写 |
已实现 |
载荷必须包含其声明的能力所需的全部文件,例如 llvm 载荷的 share/libc++/v1/std.cppm,以及 libc++ 运行期依赖的 libatomic.so.1。
当前:准入脚本检查 llvm 载荷的这几项,但它不在 CI 中运行。
载荷根目录的 .mcpp-toolchain.json 已支持 schema、frontend、platform_floor、std_module_defines、runner(已实现)。
它应当另外记录来源(未实现):配方所在仓库与提交、编译器的配置行、上游源码的 sha256、构建所用的 C 库及其版本。
一个版本在索引中的内容由 sha256 固定。内容有任何变化,必须使用新的资产名,因为 GitCode 上的发布资产既不能替换,也不能删除。
每个发布的载荷必须能由一份检入仓库的配方重新构建。
当前:llvm 子包、musl、glibc 等由 xim-pkgindex 中的构建脚本产出;gcc 由 fromsource 配方产出。已发布的三个 gcc 载荷出自两个不同的构建环境,载荷本身不记录构建环境。配方与构建 CI 所在的仓库待定。
构建必须在固定的容器中进行;构建 sysroot 必须取自索引发布的 C 库载荷,禁止取自某台机器的 subos。
| 等级 | 含义 | 要求 |
|---|---|---|
| 可重建 | 由配方与固定输入重新得到功能等价的载荷 | 必须 |
| 可验收 | 由第 6 节的程序判定合格与否 | 必须 |
| 逐字节可复现 | 固定时间戳、路径前缀映射与归档顺序 | 可以 |
必须有一个程序对一个载荷目录执行 §4.1 至 §4.4 的全部检查,并在发布前运行。 每一项检查必须有反向测试:把对应的缺陷放回载荷,该项检查必须失败。
载荷发布或 C 库绑定变化时,必须编译一个矩阵:一维是该目标行在索引中可解析的全部编译器版本,另一维是全部 C 库版本。
格子必须由索引枚举得到,禁止使用手写清单。
每格至少编译一个使用 <memory>、<mutex>、<thread> 的程序,以及一个 import std 的程序。
在 MSVC ABI 目标上,矩阵的另一维是 toolset 版本。
当前:描述符变化时,CI 只安装不带版本号时解析出的那一个版本(即 latest),并编译一个 import std 程序。gcc 描述符的改动因此只在 16.1.0 上验证过,13.3.0 与 15.1.0 未被覆盖。
xim-pkgindex 的准入脚本 verify-toolchain.sh 对一个载荷归档做一次真实的编译、链接与运行,并对 llvm 载荷检查完整性与 CRT 的解析位置(已实现)。
它必须在 CI 中运行(未实现),并必须覆盖 §6.2 的程序(未实现)。当前它默认使用 glibc 2.39,测试程序不含线程头文件。
mcpp self doctor 必须对已安装的载荷执行 §6.1 中不依赖归档的检查;发现问题时,必须给出重装命令。
当前:doctor 执行 §4.2 的检查(gcc 载荷 include-fixed/ 中带 fixincludes 横幅的文件);§4.1、§4.3、§4.4 的检查未实现。
移动 C 库绑定或某个载荷的 latest 之前,§6.2 的矩阵必须在新版本上全部通过。
| 版本 | 日期 | 变更 |
|---|---|---|
| v0.1 | 2026-09-24 | 初版草案:身份与写法、来源与选择(含 MSVC ABI 目标的 sysroot)、载荷契约、构建、验收、发布顺序 |
| v0.2 | 2026-09-24 | 随 mcpp 2026.9.24.1 更新实现状态:§2.3、§2.4、§3.1 至 §3.6 已实现;§4.2、§6.4 部分实现;§2.2 更正:不带族的 system 被拒绝 |