Skip to content

fix(toolchain,build): absent SubOS description must not stop the build; C-only units link with the C driver - #429

Merged
Sunrisepeak merged 4 commits into
mainfrom
fix/issues-426-427
Aug 15, 2026
Merged

fix(toolchain,build): absent SubOS description must not stop the build; C-only units link with the C driver#429
Sunrisepeak merged 4 commits into
mainfrom
fix/issues-426-427

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

Closes #427, closes #426. Also fixes main, which is red on all three platforms.

三条互不相关的缺陷,共同点是一个事实的缺失被当成了矛盾


#427mcpp build / mcpp toolchain install 在 Linux 上硬失败

ensure_post_install_fixup 在调用方没给出运行时身份时,自己去读硬编码的
<xlings home>/subos/default,并把「读不到」变成 std::unexpected

触发条件与沙箱无关。 默认 SubOS 由早于 subos_info 块的 xlings 创建即可 ——
实测现场是一个 16 字节、只有 {"workspace":{}} 的清单。实测影响面(同一环境):

动作 之前 现在
mcpp build ✅ (或仅因 hermeticity 失败)
mcpp build + allow_host_libs 救不了
mcpp toolchain install ✅ exit 0

对照实验:只补一个 subos_info 块、其它不动 → 构建成功 0.11s。那道门是唯一的墙。

真因是一次没做完的迁移

调用点 prepare.cppm:953 的注释写着「fixup 是 RuntimeBinding 的消费者」,
runtimeId 参数也早已加上并从四处传入,旧的自行推导没有删。而被它保护的
gcc_post_install_fixup:439 本来就正确处理空值(warning 降级)—— 那个 else 从未
执行过。

  • 删掉第二处推导;身份只能来自调用方。副产品:「项目选了 foo 却按 default 打补丁」
    这条独立缺陷随之消失。
  • 未知降级,矛盾仍然失败:身份为空 ⇒ 跳过并说明;身份声明了却兑现不了 ⇒ 报错。
  • 严重程度归调用方:build 以 info 级说明一次(按载荷去重),toolchain install
    以 warning 级说明并给出 xlings self update
  • 降级不写 marker,以免「什么都没做」被读成「已经做过」。
  • ⚠️ toolchain_install 自己解析一次 RuntimeBinding —— 缺了这一步,单删兜底会让它
    永远跳过 fixup
    ,把硬失败换成静默的坏安装。

#426 — 纯 C 的共享库依赖 libstdc++

同一个对象、同一份 ldflags,只换驱动的实测对照:

驱动 NEEDED
g++ libstdc++.so.6 libm.so.6 libgcc_s.so.1 libc.so.6
gcc libc.so.6

唯一像 C++ 的未定义符号是 __cxa_finalize@GLIBC_2.2.5(glibc 的弱符号)。三个 NEEDED
全部由驱动带入,零真实引用。

  • 按内容选驱动,谓词照 unit_needs_std 的形状写。⚠️ 查不到的对象(action 产物)
    保守判为 C++,与那个谓词的取值相反。
  • 只换驱动不够:-lstdc++exp 是显式命名的,-stdlib=libc++ 与 macOS 的
    libc++.a 路径在契约表里。故 CompileFlags 增加 ldC(与 ld 同一表达式
    产生,ld 由构造保证不变),契约表增加 unitFlagsC(-static-static-libgcc
    属于 libc / 编译器运行时,保留)。
  • 顺带补上 std.compat.o 缺失的 unit_needs_std 收窄 —— obj/std.o 被无条件链进每个单元:纯 C 的 compat 包因此依赖 libstdc++.so.6 #416 只修了 std.o

MSVC 无变化:separateLinker 直接调 link.exe,驱动不参与链接。


main 的 bench pin 守卫 —— 断言本身是错的

发版收尾 bump .xlings.json 后三平台 e2e 全红(linux ·
windows ·
macos)。

该守卫要求 reference_mcpp 等于 bootstrap pin,理由是「参照臂就是 CI 装的那个」。
两半都不成立:标准集不在任何 workflow 里(开发机手工跑,runtimedir 并存多版本);
bootstrap pin 是自举下界,可合理滞后于最新发布。而它声称防止的危险 ——「old 列静默变成
另一个 release」—— 已由 run-standard.sh 自己防住(按精确版本解析 要求二进制
自述版本,取不到就明说列缺失)。

  • 删掉跨文件相等断言;保留编译器 pin 那条耦合。
  • 报告表头直接写出实测版本(engine 键本来就带 mcpp@<ver>),运行时把请求与实际
    解析到的路径写进 meta.json

文档

  • edit-body 改写为三种情形的表(.cppm 移动行号 / .cppm 原地等长 / 独立 .cpp),
    不再读作否定级联抑制的价值。
  • ⚠️ 订正 SPEC.md 中已被实测证否的解释:决定因素是行号移动,不是「成员函数体
    进 BMI」—— Version::str() 原地修改后 BMI 逐字节相同。
  • 记录一个具名缺口:没有「原地等长修改」的场景,而那正是日常改动。
  • docs/05-mcpp-toml.md(中英)说明 C-only 目标不承担 C++ 运行时契约。

测试

  • tests/unit/test_post_install.cpp +4:门的四个分支。⚠️ 必须是单测 ——
    任何低成本 e2e 都用符号链接继承载荷,而 fixup 对继承载荷提前返回,被测代码一行都
    执行不到(与 221 是同一种假绿)。原有 4 个 DetectBakedLoader 测试保留。
  • tests/e2e/237 用户可见契约;tests/e2e/238 驱动选择,两个方向都钉
    (纯 C → c_shared 且无 C++ 运行时;含一个 C++ TU → cxx_shared;C++ 二进制 →
    cxx_link 且能跑)。

分析与自我 review:.agents/docs/2026-08-15-issues-426-427-analysis.md

…uild; link C-only units with the C driver (2026.8.15.2)

三条互不相关的缺陷,共同点是「一个事实的缺失被当成了矛盾」。

#427 —— `mcpp build` 与 `mcpp toolchain install` 在 Linux 上硬失败

`ensure_post_install_fixup` 在调用方没给出运行时身份时,自己去读硬编码的
`<xlings home>/subos/default`,并把「读不到」变成 `std::unexpected`。触发条件与
沙箱无关:默认 SubOS 由早于 `subos_info` 块的 xlings 创建即可(实测现场是一个
16 字节、只有 `{"workspace":{}}` 的清单)。`allow_host_libs` 救不了 —— 该判定
发生在 hermeticity 策略之前;`mcpp toolchain install` 同样死。已发布三周。

真因是没做完的迁移。调用点的注释写着「fixup 是 RuntimeBinding 的消费者」,
`runtimeId` 参数也早已加上并从四处传入,旧的自行推导没有删。而被它保护的
`gcc_post_install_fixup` 本来就正确处理空值(warning 降级)—— 那个 `else`
从未执行过。

  * 删掉第二处推导。身份只能来自调用方。
  * 未知降级,矛盾仍然失败:身份为空 ⇒ 跳过并说明;身份声明了却兑现不了 ⇒ 报错。
  * 严重程度归调用方:`build` 以 info 级说明一次(按载荷去重),
    `toolchain install` 以 warning 级说明并给出 `xlings self update`。
  * 降级不写 marker,以免「什么都没做」被读成「已经做过」。
  * `toolchain_install` 自己解析一次 RuntimeBinding —— 缺了这一步,单删兜底会让
    它永远跳过 fixup,把硬失败换成静默的坏安装。

#426 —— 纯 C 的共享库依赖 libstdc++

所有链接一律走 `$cxx`。同一个对象、同一份 ldflags,只换驱动的实测对照:
g++ 给出 `libstdc++.so.6` `libm.so.6` `libgcc_s.so.1` `libc.so.6`,gcc 只给出
`libc.so.6` —— 三个 NEEDED 全部由驱动带入,零真实引用。

  * 按内容选驱动,谓词照 `unit_needs_std` 的形状写;⚠️ 查不到的对象(action 产物)
    保守判为 C++,与那个谓词相反。
  * 只换驱动不够:`-lstdc++exp` 是显式命名的,`-stdlib=libc++` 与 macOS 的
    `libc++.a` 路径在契约表里。故 `CompileFlags` 增加 `ldC`(与 `ld` 同一表达式
    产生,`ld` 由构造保证不变),契约表增加 `unitFlagsC`(`-static` 与
    `-static-libgcc` 属于 libc/编译器运行时,保留)。
  * 顺带补上 `std.compat.o` 缺失的 `unit_needs_std` 收窄 —— #416 只修了 `std.o`。

main 的 bench pin 守卫 —— 断言本身是错的

发版收尾 bump `.xlings.json` 后三平台 e2e 全红。该守卫要求 `reference_mcpp` 等于
bootstrap pin,理由是「参照臂就是 CI 装的那个」;两半都不成立(标准集不在任何
workflow 里;bootstrap pin 是自举下界,可合理滞后),而它声称防止的危险已由
`run-standard.sh` 自己防住(按精确版本解析 + 要求二进制自述版本)。

  * 删掉跨文件相等断言,保留编译器 pin 那条真耦合。
  * 报告表头直接写出实测版本(engine 键本来就带),运行时把请求与实际解析到的
    路径写进 `meta.json`。

文档

  * `edit-body` 改写为三种情形的表(`.cppm` 移动行号 / `.cppm` 原地等长 /
    独立 `.cpp`),不再读作否定级联抑制的价值。
  * ⚠️ 订正 SPEC.md 中已被实测证否的解释:决定因素是行号移动,不是「成员函数体
    进 BMI」——`Version::str()` 原地修改后 BMI 逐字节相同。
  * 记录一个具名缺口:没有「原地等长修改」的场景,而那正是日常改动。

测试

  * `tests/unit/test_post_install.cpp` +4:门的四个分支。⚠️ 必须是单测 ——
    任何低成本 e2e 都用符号链接继承载荷,而 fixup 对继承载荷提前返回,
    被测代码一行都执行不到(与 221 是同一种假绿)。
  * `tests/e2e/237` 用户可见契约;`tests/e2e/238` 驱动选择,两个方向都钉。
⚠️ 本机绿 CI 红,方向与平时相反:这次是**本机有** runner 没有的东西。

原来的绿色一半是「加上 `allow_host_libs = true` 必须能构建」。落回宿主需要宿主
真有一份 C 运行时,而 runner 上 `crt1.o` / `crti.o` / `libm` 一个都没有 ——
那半个断言实际在断言运行机器的属性,不是 mcpp 的行为。

换成不依赖机器的对照:**同一个 home、同一批载荷、同一个工程,只给 SubOS 补上
`subos_info` 块**,必须构建成功。这同时更强 —— 它证明第 1 部分里的失败确实只是
降级,而不是这个环境本来就编不出东西。

绑定哪个 glibc 也不能猜:向真实 home 的 `subos_info.runtime` 要,取不到才回落到
目录扫描。装了两个 glibc 载荷的机器上,`ls | head -1` 会按字母序挑中工具链没有
针对它打过补丁的那个,让对照因为与被控变量无关的原因失败。
@Sunrisepeak
Sunrisepeak merged commit f622f02 into main Aug 15, 2026
18 checks passed
@Sunrisepeak
Sunrisepeak deleted the fix/issues-426-427 branch August 15, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants