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

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.

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


#427 — mcpp 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

Development

Successfully merging this pull request may close these issues.

mcpp build fails inside an xlings sandbox since 2026.8.10.x (bisected; not a regression of the latest release) A pure-C shared library depends on libstdc++.so.6: everything is linked with the C++ driver

2 participants