Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
417 changes: 417 additions & 0 deletions .agents/docs/2026-09-24-685-687-msvc-stl-and-toolchain-payloads.md

Large diffs are not rendered by default.

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions .agents/docs/2026-09-24-toolchain-selection-implementation-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
subject: plan
status: active
---

# 工具链选择与载荷可信度:实施计划

- 日期:2026-09-24
- 设计:`2026-09-24-toolchain-selection-and-payload-trust-design.md`(v2)
- 规范:`docs/specs/toolchain-management.md`(SPEC-006)
- 维护者对设计 §10 的答复:
- `sysroot` 语义接受;
- `xim:` 前缀对所有工具链族都接受;
- SPEC-006 保持一份,不拆分。

## 1. 约束

| 角度 | 约束 |
|---|---|
| 架构 | MSVC 的选择只在 prepare 里做一次,结果存进 `Toolchain`,所有读者都读它。选择逻辑是纯函数,只有枚举候选依赖 Windows。deployment target 的解析函数以目标三元组为参数,调用方无法再按宿主判定 |
| 稳定性 | 不写版本时,默认结果与今天一致,例外只有设计 §3.9 列出的几种(每一种都更好)。任何回落都打印一行说明 |
| 简洁 | 不新增清单键;clang 行复用 `[target.<triple>].sysroot`。三组 `-Xmicrosoft-*` 参数由 `LinkModel` 这一个地方产生 |
| 用户体验 | 构建开头打印一行,说明用的是哪个 toolset、来自哪里、SDK 版本。找不到时列出三类候选。写了版本而环境变量被忽略时,打印说明 |
| 兼容性 | `msvc@system`、`msvc@<版本>`、`gcc@…`、`llvm@…` 这些写法都继续有效。`msvc@<版本>` 在机器上有同版本时改用系统的那一份,这一点写进 CHANGELOG。缓存键的变化只影响 Windows clang 行 |
| 跨平台 | 选择逻辑、写法解析、清单解析的单测都在 Linux 上运行;Windows 行为由 Windows CI 的 e2e 覆盖。#685 的修正对 macOS 宿主保持原有输出 |
| 一致性 | `cl.exe` 行与 clang 行共用同一个选择函数。「最高」统一按数字元组比较 |
| 无感升级 | 已有项目不需要改清单。新写法在旧引擎上是响亮的失败,不会被静默忽略;文档写明所需的最低版本 |
| 测试覆盖 | 每个判据都配反向用例(撤回修复就变红)。纯函数单测覆盖边界情形;e2e 覆盖真实二进制 |

## 2. 任务

| 编号 | 仓库 | 内容 | 主要文件 | 依赖 |
|---|---|---|---|---|
| T1 | mcpp | #685:deployment target 与宿主无关,是否适用按目标判定 | `modules/platform/src/macos/macos.cppm`、`src/build/prepare.cppm`(deployment target 相关位置)、`src/build/prepare_inputs.cppm`、`src/toolchain/hostflags.cppm`(`-mmacosx-version-min`)、`src/toolchain/stdmod.cppm`、`src/build/flags.cppm`、`src/build/build_program.cppm`、对应单测 | 无 |
| T2 | mcpp | #687 的 doctor 诊断:已安装 gcc 的 `include-fixed` 里带横幅的文件 | `src/doctor.cppm`、单测 | 无 |
| T3 | mcpp | 工具链写法的 `xim:` 前缀(所有族) | `src/toolchain/registry.cppm`、`src/toolchain/compat.cppm`、单测 | 无 |
| T4 | mcpp | MSVC 候选枚举与纯函数 `select_toolset()`;`cl.exe` 行的 `msvc@system` 与带版本的写法改用它 | `src/toolchain/msvc.cppm`、`src/build/prepare.cppm`(工具链解析段)、单测 | T3 |
| T5 | mcpp | clang 行的 MSVC sysroot:清单解析、在 prepare 中解析、`Toolchain` 字段、`-Xmicrosoft-*` token、`std.ixx`、`stdlibVersion`、链接环境、`resolution.json`、构建开头那一行 | `modules/manifest/src/toml.cppm`、`modules/toolchain-model/src/model.cppm`、`linkmodel.cppm`、`src/toolchain/hostflags.cppm`(链接 token)、`src/toolchain/clang.cppm`、`src/build/flags.cppm`(链接一侧与含空格路径的转义)、`src/build/cache_key.cppm`、`src/build/prepare.cppm` | T4 |
| T6 | mcpp | `mcpp toolchain list` 在 Windows 上列出机器上的 toolset | `src/toolchain/lifecycle.cppm` | T4 |
| T7 | mcpp | Windows e2e:多版本、`xim:`、环境变量被忽略、`std.ixx` 与头文件同源 | `tests/e2e/`、`.github/workflows/ci-windows*.yml` | T5 |
| T8 | mcpp | 文档:`docs/20`、`docs/22`、`docs/04` 及 `docs/zh/` 对应译文;SPEC-006 的实现状态;CHANGELOG;版本号 | 文档与版本文件 | T1 到 T7 |
| T9 | xim-pkgindex | `gcc.lua` 清理代码的引号修正与日志;`tests/g/test_gcc.py` 断言 | `pkgs/g/gcc.lua`、`tests/g/test_gcc.py` | 无 |
| T10 | — | 两个 PR 的 CI 全绿后合入:xim-pkgindex 先合,mcpp 后合 | — | T1 到 T9 |
| T11 | mcpp、xlings-res、xim-pkgindex | 发布:打 tag、四平台构建、本地 gtc 补 GitCode、核验镜像、合入 bot 的索引 PR、在 PR 里前移 bootstrap pin | — | T10 |
| T12 | — | 生态验证:在 xlings subos 沙箱里(mcpp 与 xlings 都配 CN 镜像)装已发布的 mcpp,逐项验证 #685、#687 以及 mcpp-index 的若干包 | — | T11 |

并行方式:

- T1 与 T2 交给一个子代理,在 worktree `mcpp-685` 里做;
- T9 交给另一个子代理,在 xim-pkgindex 的 worktree 里做;
- T3 到 T8 由主线在 `mcpp-tcsel` 里做;
- T1、T2 做完后合入主线分支,形成一个 PR。

文件归属按上表划分。`src/build/prepare.cppm` 由两边在互不重叠的区段修改:T1 只动 deployment target 相关的函数与调用处,T4、T5 只动工具链解析段与 `resolution.json`。

## 3. 判据

| 编号 | 判据 | 反向 |
|---|---|---|
| T1 | 在 Linux 宿主上,`aarch64-macos` 目标的 `build.ninja` 带有 manifest 里写的版本;改值后指纹变化;env 优先于 manifest | macOS 宿主构建非 Apple 目标时不出现 `-mmacosx-version-min` |
| T2 | 15.1.0 载荷副本触发诊断 | 16.1.0 不触发 |
| T3 | `xim:gcc@16.1.0` 与 `gcc@16.1.0` 解析结果相同;`xim:msvc@system` 被拒绝 | — |
| T4 | 纯函数单测覆盖设计 §3.10 列出的全部用例 | 去掉「先匹配系统」后,对应用例变红 |
| T5 | 单测:MSVC 目标的 link model 产出三组 token,并进入缓存键;Windows CI 构建一个 `import std` 程序 | MinGW 目标的 token 为空 |
| T7 | Windows e2e 的五条 | — |
| T9 | 安装后 `include-fixed` 里没有带横幅的文件 | 撤回修正后断言变红 |
| T12 | 沙箱里所有检查通过,并把没有运行的检查单独列出 | — |
179 changes: 179 additions & 0 deletions .agents/docs/2026-09-24-toolchain-selection-verify.sh

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion .agents/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
---
```

303 records.
306 records.

## By subject

Expand All @@ -30,6 +30,7 @@ Records that declare one. Everything else is listed by date below.

### design

- [MSVC toolset 的选择、#685、#687 与工具链管理规范:总体设计](2026-09-24-toolchain-selection-and-payload-trust-design.md) — active
- [openkal 生态:能力的时刻模型,以及 C 环境方案空间的划分](2026-09-20-openkal-c-environment-ecosystem-design.md) — active
- [#674:`presents = "posix"` 在 Windows 上兑现契约的下半段](2026-09-19-issue-674-cenv-posix-preinclude-design.md) — superseded
- [The build database of #636, and two defects on the way to the latest xlings](2026-09-14-636-build-database-and-the-latest-xlings.md) — active
Expand All @@ -52,6 +53,7 @@ Records that declare one. Everything else is listed by date below.

### plan

- [工具链选择与载荷可信度:实施计划](2026-09-24-toolchain-selection-implementation-plan.md) — active
- [openkal 生态:完整性收尾与验收方案](2026-09-21-openkal-ecosystem-completion-and-acceptance.md) — active
- [C 环境生态方案:执行计划](2026-09-20-ecosystem-execution-plan.md) — active

Expand All @@ -75,6 +77,7 @@ Records that declare one. Everything else is listed by date below.

### triage

- [#685、#687 与 Windows clang 的 MSVC STL:三个问题的归属,以及工具链载荷的规范化](2026-09-24-685-687-msvc-stl-and-toolchain-payloads.md) — active
- [运行时绑定方案 v3:让 mcpp 真正安装它所声明的运行时](2026-09-17-runtime-binding-multi-repo-plan.md) — landed
- [#662:目标侧由依赖图提供时,编译器的隐式头文件搜索仍指向宿主](2026-09-17-issue-662-graph-target-header-isolation-plan.md) — active
- [Issue #660 分析:`glibc@2.44` 绑定在 2.44.3 发布后解析失败](2026-09-17-issue-660-glibc-line-binding-analysis.md) — landed
Expand All @@ -94,6 +97,9 @@ Records that declare one. Everything else is listed by date below.

### 2026-09

- [工具链选择与载荷可信度:实施计划](2026-09-24-toolchain-selection-implementation-plan.md) — active
- [MSVC toolset 的选择、#685、#687 与工具链管理规范:总体设计](2026-09-24-toolchain-selection-and-payload-trust-design.md) — active
- [#685、#687 与 Windows clang 的 MSVC STL:三个问题的归属,以及工具链载荷的规范化](2026-09-24-685-687-msvc-stl-and-toolchain-payloads.md) — active
- [openkal 生态:完整性收尾与验收方案](2026-09-21-openkal-ecosystem-completion-and-acceptance.md) — active
- [本轮生态级自审](2026-09-20-wave-self-review.md) — active
- [openkal 生态:能力的时刻模型,以及 C 环境方案空间的划分](2026-09-20-openkal-c-environment-ecosystem-design.md) — active
Expand Down
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,90 @@
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。

## [2026.9.24.1] - 2026-09-24

### MSVC ABI 上的 toolset:只选一次,可以指定,记录在案

clang 以 `*-windows-msvc` 为目标时,编译所针对的 MSVC 环境(STL、CRT、Windows SDK)由两个互不相关的
选择器决定:clang 驱动自己探测头文件与库(`VCToolsInstallDir`、`PATH`、最新实例的默认 toolset,
`%INCLUDE%` 存在时整体采用它),mcpp 另行按 `VSINSTALLDIR`、`vswhere -latest`、目录名最大者定位
`std.ixx`。机器上装有多个 toolset 时,两者可能指向不同的 toolset;项目无法指定用哪一个;选择结果
既不进缓存键,也不出现在任何记录里。

现在 MSVC toolset 是这一行的 sysroot,由 `[target.<triple>].sysroot` 指定,prepare 解析一次:

```toml
[toolchain]
windows = "llvm@22.1.8"

[target.x86_64-windows-msvc]
sysroot = "msvc@14.44.35207" # 或 "msvc@system"(默认),或 "xim:msvc@14.44.35207"
```

结果以 `-Xmicrosoft-visualc-tools-root`、`-Xmicrosoft-windows-sdk-root`、
`-Xmicrosoft-windows-sdk-version` 传给编译、链接与 `std` 模块预编译(这几个是 clang-cl
`/vctoolsdir` 等选项的别名;带上它们后,clang 不再读 `VCToolsInstallDir` 与 `%INCLUDE%`),
`std.ixx` 取自同一个 toolset。构建打印一行 `Resolved sysroot msvc@system → MSVC <版本> (...)`,
`resolution.json` 新增 `msvc_toolset` 与 `windows_sdk`,toolset 目录与 SDK 版本进入缓存键,
SDK 版本也成为这一行的运行时身份 `ucrt@<版本>`。

写法在 cl.exe 行与 clang 行上相同:

| 写法 | 含义 |
|---|---|
| `msvc@system` | 本机默认:`VCToolsInstallDir` → `VSINSTALLDIR` 实例的默认 toolset → `PATH` 上的 `cl.exe` → 带 C++ 组件的最新实例的默认 toolset |
| `msvc@<toolset>` | 本机已装的同版本 toolset 优先(所有实例中查找),没有时安装载荷;环境变量不参与,被忽略时打印说明 |
| `xim:msvc@<toolset>` | 只用载荷,SDK 随载荷固定 |

`xim:` 是唯一的工具链命名空间。gcc、llvm 的工具链总来自载荷,`xim:gcc@16.1.0` 与 `gcc@16.1.0` 等价,
这一点与过去相同;过去任何 `<ns>:` 前缀都被静默剥掉,现在其他命名空间在读取处被拒绝。`xim:msvc@system`
不是一种写法,同样在读取处被拒绝(过去它在更晚的阶段失败,Linux 上报的是「只在 Windows 宿主可用」)。

**行为变化。** `msvc@<toolset>` 过去一律使用载荷;现在机器上有同版本时直接用机器的那一份,SDK
随之取机器上的。需要载荷(连同它的 SDK)的项目改写为 `xim:msvc@<toolset>`。cl.exe 行的
`msvc@system` 改为取实例的默认 toolset(`Microsoft.VCToolsVersion.default.txt`)而不是目录名最大者,
并开始读取 `VCToolsInstallDir`;某个 toolset 缺少 `std.ixx` 时,不再借用另一个 toolset 的。
cl.exe 行上指向另一个 toolset 的 `sysroot` 被拒绝。`mcpp toolchain list` 在 Windows 上列出
本机已装的 toolset。

**旧引擎读新写法。** 在 Windows runner 上用 2026.9.21.3 实测:MSVC 行上的 `sysroot = "msvc@system"`
与 `"msvc@<toolset>"` 让整份清单被拒(「is not an xpkg reference」);`"xim:msvc@<toolset>"` 被接受
而不生效:不安装任何东西,clang 针对机器上的 toolset 编译,构建输出却把这个值列为 c-abi 层。
依赖所写 toolset 的项目应把 mcpp 固定在 2026.9.24.1 或更高。

### `macos_deployment_target` 按目标生效,不再按宿主(#685)

在 Linux 或 Windows 宿主上 `mcpp build --target aarch64-macos`,产物的 `LC_BUILD_VERSION minos` 恒为
14.0:`[build] macos_deployment_target` 与 `MACOSX_DEPLOYMENT_TARGET` 都被忽略,改了值也不重建。
解析函数只在 `#if defined(__APPLE__)` 下读这两个输入,其他宿主上返回空,三元组于是回落到内置的
`14.0`;指纹也只在宿主是 macOS 时折入这个值;平台事实 `macos.deployment-target` 同样为空,包里
针对它的版本要求在这种构建里不被检查。反方向同理:macOS 宿主交叉到非 Apple 目标时,编译命令里
仍带 `-mmacosx-version-min`。

现在解析(env > manifest > 14.0)与宿主无关,是否适用由构建的目标决定:三元组、指纹、平台事实、
`-mmacosx-version-min` 与 std 模块预编译都按目标判定;build.mcpp 的宿主编译按它自己的目标(即宿主)
判定。macOS 宿主构建 macOS 目标时输出不变。判据是 `tests/e2e/746_…`:在 Linux 宿主上用
`[target.aarch64-macos] toolchain = "llvm@…"` 走 `--configure-only`,不需要 macOS SDK,断言
`--target=arm64-apple-macos11.0`、改值后指纹变化、env 优先于 manifest;把修复撤回,三条全红。

### `mcpp self doctor` 报告 gcc 载荷里冻结的 fixincludes 头(#687)

gcc 13.3.0、15.1.0(以及 11.5.0)的 x86_64-linux-gnu 载荷在 `include-fixed/` 里带着构建机上 glibc
头的 fixincludes 副本(`pthread.h` 等)。它们在搜索顺序上排在构建所用的 glibc 2.44 之前,于是
`<mutex>`、`<memory>` 编译失败。索引里本有一段安装时删除它们的清理代码,但单引号嵌套让 grep 搜索的是
当前目录而不是载荷,从未生效;配方的修正在 openxlings/xim-pkgindex#870,新安装会被清理。

已安装的载荷不会因为索引更新而重新清理。doctor 新增一项检查:列出每个 gcc 载荷里带
`auto-edited by fixincludes` 横幅的文件、横幅里的源路径,以及重装命令
(`mcpp index update`,然后 `mcpp toolchain remove gcc@<v>` 与 `mcpp toolchain install gcc@<v>`)。
这是警告,不是失败。

### SPEC-006:工具链管理(草案)

`docs/specs/toolchain-management.md`:身份与写法、来源与选择、载荷契约、构建、验收与发布顺序,
每条标注实现状态。载荷契约与验收(载荷 lint、编译器 × C 库兼容矩阵、准入门进 CI)计划与下一批
LLVM 工具链一同实现。

## [2026.9.21.3] - 2026-09-21

### `mcpp test --no-run`:为一个跑不了的目标构建测试,并把这当成答案
Expand Down
5 changes: 4 additions & 1 deletion docs/04-mcpp-toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,10 @@ cargo/rustc, cc, etc.) > this field (the project default, similar to SwiftPM's
`platforms:`) > the **built-in default `14.0`** (rustc-style — every target has a
baseline, and 14.0 is the floor of LLVM's official static libraries themselves).
This value enters the BMI fingerprint, so switching targets automatically rebuilds
the module cache.
the module cache. Resolution and application both follow the TARGET, not the
machine running mcpp: `mcpp build --target aarch64-macos` honors this field
(and the environment variable) the same way on Linux or Windows as it does on
a Mac, and a non-macOS target never sees it.

### Build concurrency (`jobs`) and module scheduling (`bmi_schedule`)

Expand Down
5 changes: 3 additions & 2 deletions docs/09-commands-by-scenario.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,9 @@ $ mcpp xpkg parse mcpp.plugins.lua --json
## Environment diagnosis

`mcpp self doctor` checks the toolchain, the `std` module, the registry, cache
health and the last runtime-closure verdict, and reports what it found rather
than only what failed:
health, the last runtime-closure verdict, and installed GCC payloads for
fixincludes-frozen headers left over from the build machine that produced
them, and reports what it found rather than only what failed:

```
$ mcpp self doctor
Expand Down
Loading
Loading