Skip to content

fix(pack): 宿主能力清单从解析后的图取,不再从根 manifest 取 (2026.8.10.3) - #409

Merged
Sunrisepeak merged 5 commits into
mainfrom
fix/host-requirements-from-resolved-graph
Aug 10, 2026
Merged

fix(pack): 宿主能力清单从解析后的图取,不再从根 manifest 取 (2026.8.10.3)#409
Sunrisepeak merged 5 commits into
mainfrom
fix/host-requirements-from-resolved-graph

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

跟在 #408 后面。2026.8.10.2 引入的「自带 libc 的档拒绝宿主能力」只做到了一半,
这个 PR 补齐,并把那条本来应该抓到它的测试改对。

缺陷

宿主能力清单是从根 manifest[runtime] 取的。而几乎没有应用会自己声明
capability:opengl.glx.driver —— 它依赖某个声明了的包(glfw / SDL 封装 / GL runtime),
resolver 会给每条需求盖上请求者身份。

读根 manifest 回答的是「作者写没写」(几乎总是没写),而该问的是「解析出来的图需不需要」。

实测(真实 imgui 工程,不是 fixture)

mcpp why runtime 明明白白列着:

requirements:
  - capability:opengl.glx.driver [run] <- compat.glfw@3.4 (required)
  - capability:x11.display       [run] <- compat.glx-runtime@2026.08.08 (required)

修复前:

$ mcpp pack --mode self-contained
      Packed target/dist/b-0.1.0-x86_64-linux-gnu-bundle-all.tar.gz     ← 照打不误

HOST-REQUIREMENTS 也是空的 —— 而空文件会被读成「什么都不需要」,
所以现在根本不写空文件。

修复后:

$ mcpp pack --mode self-contained
error: --mode self-contained cannot be used by a program that needs the host to
       provide abi:glibc, opengl.glx.driver, x11.display.

  use: --mode vendored — third-party .so travel with the artifact; libc and the
       capability above both come from the host.

$ mcpp pack --mode vendored     # 正常打包
$ cat target/dist/*/HOST-REQUIREMENTS
capability=abi:glibc discovery=unknown
capability=opengl.glx.driver discovery=unknown
capability=x11.display discovery=unknown

为什么原来的测试没抓到 —— 这才是重点

216 的 fixture 在根工程声明了能力,恰恰是真实工程唯一不具备的形态。
它通过的理由比它声称覆盖的范围窄,而窄在哪里没有被说出来 ——
这一轮反复在写的就是这条判据,这次轮到我自己。

216 已改成真实形态:

  • 能力由依赖的描述符声明,消费方 mcpp.toml 什么都不声明;
  • 加一条前置断言:那条需求必须先出现在 mcpp why runtime 里,
    否则「两档都拒绝」可能只是因为根本没有需求可拒 —— 测试等于空转;
  • 顺带端到端验到 discovery 的声明式链路:依赖描述符里写的
    discovery = "rpath-of-dispatch" 原样出现在消费方的 HOST-REQUIREMENTS 里。

另含

2026-08-10-graphics-closure-acceptance.md —— #408 的验收实测,
把「验过是好的」与「本机验不了」分开写(GPU 那一半在本机是 NOT_EXERCISED,
理由与证据都在里面),并记下了我自己的一次测量错误(find | head -1 不是判据)。

unit 77/77 通过。

验收记录只写实测,并把「验过是好的」与「本机验不了」分开写 —— 这正是本轮到处在建的
那条判据,用在自己的验收上。

## 验到的

- **#405 在真实 imgui 模板上端到端验过**:清缓存 → 项目 a(MISS)OK →
  项目 b(HIT)`Cached imgui v0.0.6 (9 units)` OK。修复前 b 必挂。
- **加载器标签在真实图形产物上验过**:`bin/b` 是 DT_RPATH,12 个 X11 库全部
  DT_RUNPATH,rule E 记录 13 条全 `ok`,零 violation。
- **CI 干净机器上 192 条 e2e 全过、0 失败**(本地 25 红全部是环境)。

## 验不了的,写清楚为什么

GPU 那一半在本机是 **NOT_EXERCISED**:宿主 GL 本身好(RTX 4080 / GL 4.6 /
direct rendering yes),但这个 home 的 `<subos>/lib` 里一个 GL 都没有、`.wiring`
不存在 —— 从来没有被接线过;而共享 gcc 载荷的 specs 又被历次安装污染
(`--dynamic-linker` 指向已改名的 glibc 2.44、rpath 里约 40 条已删除沙箱路径),
这也是本地 24 条 e2e 红的单一根因,用已发布的 2026.8.8.2 逐条复现过。

## 记下一次我自己的测量错误

我一度报告产物 rpath「指向一个不存在的版本」—— 那是 `find … | head -1` 先返回了
同目录下另一个版本造成的。**`head -1` 不是判据。** 留在文档里,因为这一轮的主题
恰恰是「判据要说清楚验的是哪一片」。

## 设计文档的两处出入(以实施为准)

- `.wiring` 增强**没有做**:主判据(mcpp 自算路径身份)可独立成立,读别人一份
  条件语义未表达的记录是净增耦合。
- `discovery` **不由 mcpp 推断**:第一版按能力名推断,被既有守卫
  `test_runtime_contract` 当场拦下 —— 那是把 provider 专属知识写进 mcpp。
  改为声明式。**守卫是对的。**
`2026.8.10.2` 的「自带 libc 的档拒绝宿主能力」只在**根工程自己声明**能力时生效。
而几乎没有应用会自己声明 `capability:opengl.glx.driver` —— 它依赖某个声明了的包,
resolver 给每条需求盖上请求者身份。读根 manifest 回答的是「作者写没写」
(几乎总是没写),该问的是「解析出来的图需不需要」。

## 实测

真实 imgui 工程,`mcpp why runtime` 明明白白列着:

    capability:opengl.glx.driver [run] <- compat.glfw@3.4 (required)

而修复前 `mcpp pack --mode self-contained` **照打不误**,`HOST-REQUIREMENTS` 也是空的
(而空文件会被读成「什么都不需要」)。修复后:

    error: --mode self-contained cannot be used by a program that needs the host
           to provide abi:glibc, opengl.glx.driver, x11.display.
      use: --mode vendored — …

`--mode vendored` 正常打包,三条全部写进清单。

## 为什么原来的测试没抓到

`216` 的 fixture **在根工程声明了能力** —— 恰恰是真实工程唯一不具备的形态。
测试通过的理由比它声称覆盖的范围窄,而窄在哪里没有被说出来:本轮反复写的就是这条,
这次轮到我自己。

`216` 已改成:能力由**依赖**声明、消费方什么都不声明,并加一条前置断言 ——
那条需求必须先出现在 `mcpp why runtime` 里,否则「两档都拒绝」可能只是因为
根本没有需求可拒,测试等于空转。

顺带验到 `discovery` 的声明式链路端到端可用:依赖描述符里写的
`discovery = "rpath-of-dispatch"` 原样出现在消费方的 `HOST-REQUIREMENTS` 里。

unit 77/77 通过。
本地 25 红的对照结论因此闭环:干净机器上 0 失败。
另外特意确认五个用例名逐个出现在 PASS 行 —— `# requires:` 里一个不认识的 token
会让用例从不运行而不报错,只看总数是看不出来的。
`00_fixture_path_hygiene` 在 Windows 上抓到的:manifest 是 mcpp 读的、不是 shell 读的,
所以写进 mcpp.toml 的路径必须是**宿主拼写** —— 一个 MSYS `/c/...` 是 Windows 打不开的路径。
这条守卫正是为此存在的(记忆里已经炸过一次),而我新加的依赖索引 fixture 又撞上了。

在 Linux 上 `host_path` 是恒等,所以这条只在 Windows 上有差别 —— 也就是说
只在本机跑是看不出来的。
「本地构建能跑」和「用户装到的能跑」是两个断言,所以按 xlings 生态路径重做:
install -> use -> 版本核对 -> imgui 双项目(含缓存命中那格) -> 标签与 rule E。

⚠️ `xlings install` 不会自动切换已装的旧版本,它会明说;只看 install 的成功输出
就以为切过去了,是这一步最容易的误读。

顺带把 .2 与 .3 在同一个真实工程上的差别写下来:.2 的那道门读根 manifest,
而真实工程的能力来自依赖 —— 这正是本 PR 修的东西,现在有了现场对照。
@Sunrisepeak
Sunrisepeak merged commit e53204a into main Aug 10, 2026
18 checks passed
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.

2 participants