Skip to content

Commit cf6023b

Browse files
committed
docs: 第四轮记录 —— EGL 源码化、模块命名归属、沙箱干净房间验证
跨仓主文档补上 §19,涵盖这一轮的四件事: §19.2 模块命名。规则定为「跟拥有**接口**的组织,不跟发实现的人」,依据是 mcpplibs.openkal 已经付过的学费(0.1.0 被撤回,因为它把消费者 import 的模块放在了 实现的控制之下)。于是 wayland 三个模块加 freedesktop 前缀,EGL 的模块叫 khronos.egl —— 包名与模块名故意不一致,因为 freedesktop 发这份代码而 Khronos 拥有这份规范。 §19.3 生成器该放哪。判据是「依不依赖目标平台」:能预先算定的签进仓 + CI diff(两个 fork 的构建路径上都没有 Python),依赖架构算不出来的才进 build.mcpp(GLdispatch 的 entry stub)。把 genmod.py 改写成 build.mcpp 反而会把生成器塞进每个消费者的构建。 §19.4 沙箱干净房间验证。合成 home、从零 clone + 装 + 构建,而沙箱里 /usr 是宿主的 —— 所以证的不是「宿主不在」,而是「宿主在、可达、且全部落败」。整条链跑通, eglInitialize 拿到 EGL 1.5 vendor Mesa Project,gbm_bo_create 真分配出 256x256。 顺带记了三个坑,其中 [indices] 同一 path 挂两个键会产生二义那条最耗时间。 §19.6 记一处自己造成的破坏:为承载模块重命名原地重切了 wayland 的 v1.26.0,于是在本 PR 合并前 main 上的 sha256 对不上。正确次序应当是先改描述符再重切 tag。同版本重切还 会被 store 掩盖(只按 name+version 索引),清理命令一并记下。 §19.5 xlings pin:§15「不下调到 .4」的结论仍然成立,但那条要求指出的缺口是真的 —— 两个 fork 的 CI 根本没钉,现已钉到与 kXlingsVersion 一致的 .5。
1 parent e8f0840 commit cf6023b

1 file changed

Lines changed: 190 additions & 0 deletions

File tree

‎.agents/docs/2026-08-30-gbm-cross-repo-closed-loop-plan.md‎

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Date: 2026-08-30 · 起因:`compat.libgbm`(mcpp-index PR #281)· 状态:待 revi
99

1010
| 想知道 | 看哪节 | 注意 |
1111
|---|---|---|
12+
| **最新一轮(EGL 源码化 + 模块命名)** | **§19** | 最权威;§19.6 记了一处自己造成的破坏 |
13+
| **沙箱干净房间验证** | **§19.4** | 宿主库在场且可达却全部落败 |
14+
| 模块该叫什么名字 | §19.2 | 跟接口的所有者,不跟发实现的人 |
15+
| 生成器放 build.mcpp 还是签进仓 | §19.3 | 判据是「依不依赖目标平台」 |
16+
| xlings 该 pin 哪个版本 | §15 + §19.5 | §15 的结论仍成立,§19.5 补了它指出的真缺口 |
1217
| **最终结论与交付** | §12(实现结果)、§13(状态)、§14(要不要做 mesa 包) | 权威 |
1318
| 为什么 `compat.libgbm` 该独立存在 | §3、§10.1 | 理由换过三次,结论没变 |
1419
| 任务拆分与依赖 | §11 | |
@@ -1113,3 +1118,188 @@ backends dir: <registry>/subos/default/usr/lib/gbm ← subos 给的,不是
11131118
`index.toml` 的 `min_mcpp = 2026.8.3.3` 是**假的**,而且是本包证明的 —— 详见提交
11141119
`feat(libgbm): the package sheds its workaround; index floor corrected`。
11151120
已上调到 `2026.8.27.2`,同时把「floor 与 CI pin 一起动」这条早已漂移的不变量恢复了。
1121+
1122+
---
1123+
1124+
## 19. 第四轮:EGL 从绑定改为源码构建,并统一模块命名(2026-08-30 下午)
1125+
1126+
§18 之后栈里还剩一处不自洽:`compat.egl` 是绑定,而它**自己的注释**写着这是错的 ——
1127+
「libglvnd **is** a separable project,按判据本该源码构建,现在仍是绑定纯粹是工作量问题」。
1128+
判据里没有「工作量」这一项。这一节把这笔账还上,并顺带解决了模块命名的归属问题。
1129+
1130+
### 19.1 交付物
1131+
1132+
| 仓 | 变更 | 状态 |
1133+
|---|---|---|
1134+
| **mcpplibs/libglvnd**(新建) | libglvnd v1.7.0 + mcpp 构建支持,出 `libEGL.so.1` 与 `libGLdispatch.so.0` | `v1.7.0`,CI 四 job 全绿 |
1135+
| **mcpplibs/wayland** | 三个模块重命名;CI 钉 xlings | `v1.26.0` 重切,CI 全绿 |
1136+
| **mcpp-index** | `compat.egl` → `freedesktop.egl`;5 个描述符 sha256;测试成员;文档 | PR #293 |
1137+
| **mcpp-community/mcpp** | 示例 09 改用 `freedesktop.egl`,打印两个发现变量 | 待 #293 合并后推 |
1138+
| mcpp-res(gitcode) | wayland / libglvnd 两个 CN 镜像 | 已发布,与 GLOBAL 字节一致 |
1139+
1140+
### 19.2 模块命名:跟**接口的所有者**,不跟发实现的人
1141+
1142+
索引既有的惯例是「namespace 不进模块名」,这一点有反例可证:
1143+
1144+
| 包 | 模块 |
1145+
|---|---|
1146+
| `chriskohlhoff.asio` | `import asio` |
1147+
| `fmtlib.fmt` | `import fmt` |
1148+
| `boost-ext.ut` | `import boost.ut` |
1149+
| `opencv.opencv` | `import opencv.cv` |
1150+
1151+
这一轮把它推进一步:模块名不是随便挑的,而是**拥有那份接口的组织**。于是
1152+
1153+
```
1154+
wayland.client → freedesktop.wayland.client freedesktop 确实拥有 wayland 协议
1155+
wayland.server → freedesktop.wayland.server
1156+
wayland.util → freedesktop.wayland.util
1157+
egl → khronos.egl EGL 是 Khronos 规范
1158+
```
1159+
1160+
**注意 EGL 的包名与模块名故意不一致**:包是 `freedesktop.egl`(freedesktop 确实在发
1161+
这份代码),模块是 `khronos.egl`(Khronos 拥有这份规范,libglvnd 只是实现之一)。
1162+
1163+
依据不是审美,是 `mcpplibs.openkal` 已经付过的学费 —— 它的 0.1.0 是**被撤回**而不是保留的:
1164+
1165+
> It placed the module a consumer imports **under the control of the
1166+
> implementation**, which contradicts what the specification is for
1167+
1168+
模块名是全局且长期的,包名不是。换实现不该逼消费者改 `import`,否则一个「除了 include
1169+
那一行什么都不改」的包装层就失去了意义。副作用是好的:两个 EGL 提供方现在会在模块名上
1170+
**硬冲突**,而不是静默共存 —— 这正是 GLVND「一个进程一个 dispatch 点」想要的。
1171+
1172+
### 19.3 build.mcpp 与「签进仓的生成物」的分界
1173+
1174+
这一轮两次遇到「生成器该放哪」,答案不一样,规则是同一条:
1175+
1176+
> **build.mcpp 管的是「依赖目标平台、算不出来」的决定;能预先算定的生成物就签进仓 + CI diff。**
1177+
1178+
| 生成物 | 依赖目标? | 放哪 |
1179+
|---|---|---|
1180+
| libglvnd 的 dispatch 表(~1000 行 Python × 2.7MB gl.xml) | 否 | `mcpp/generated/`,CI 重生成并 diff |
1181+
| wayland 的协议代码 + 模块包装(`genmod.py`) | 否 | `mcpp/generated/`,CI 重生成并 diff |
1182+
| GLdispatch 的 **entry stub 选择** | **是**(架构 × 线程存储模型) | `build.mcpp`,用 `mcpp::target_arch()` |
1183+
1184+
两个 fork 的**构建路径上都没有 Python**(`mcpp.toml` / `build.mcpp` / 根清单都不提它),
1185+
消费者只需要 mcpp。把 `genmod.py` 改写成 build.mcpp 反而会**把生成器塞进每个消费者的
1186+
构建**,并且会撞上 **mcpp#534**(build.mcpp 的 action 产物与本包编译之间没有 order-only 边)
1187+
—— wayland 的协议代码当初签进仓正是因为这个。
1188+
1189+
GLdispatch 那条则相反:entry stub 分架构**且**分线程存储模型,而且和 libffi 的不同,
1190+
**它们自身没有任何门控**。在 `sources` 里写死 x86_64 会让包只能在 x86_64 上用、而且哪儿
1191+
都不写明;`build.mcpp` 用 `mcpp::target_arch()` 做的正是上游 `gl_dispatch_type` 的选择。
1192+
1193+
### 19.4 生态真实验证:沙箱干净房间(`--sandbox --gpu`)
1194+
1195+
§12.3 验证过的是 **GBM**。这一轮验证的是**整条链**,而且在一个 `/home/speak` 是合成的、
1196+
开发机上任何 checkout / 缓存 / 构建树都进不来的沙箱里,**从零 clone + 安装 + 构建**。
1197+
1198+
关键在于:**沙箱里 `/usr` 是宿主的**。
1199+
1200+
```
1201+
host has /usr/lib/x86_64-linux-gnu/libEGL.so.1
1202+
host has /usr/lib/x86_64-linux-gnu/libgbm.so.1
1203+
host has /usr/lib/x86_64-linux-gnu/libdrm.so.2
1204+
host has /usr/lib/x86_64-linux-gnu/libwayland-client.so.0
1205+
```
1206+
1207+
所以要证的**不是**「宿主不在」,而是「宿主在、可达、且依然全部落败」—— 后者才是用户机器
1208+
上会发生的情形。用二进制自己的 loader 列闭包:
1209+
1210+
```
1211+
libgbm.so.1 => <registry>/xpkgs/compat-x-libgbm/25.0.7/.../libgbm.so.1
1212+
libdrm.so.2 => <project>/target/.../bin/libdrm.so.2
1213+
libEGL.so.1 => <project>/target/.../bin/libEGL.so.1
1214+
libGLdispatch.so.0 => <project>/target/.../bin/libGLdispatch.so.0
1215+
libwayland-client.so.0 => <project>/target/.../bin/libwayland-client.so.0
1216+
libwayland-server.so.0 => <project>/target/.../bin/libwayland-server.so.0
1217+
libffi.so.8 => <project>/target/.../bin/libffi.so.8
1218+
1219+
PASS: the host's copies were present and reachable, and none of them won
1220+
```
1221+
1222+
真跑(`--gpu`):
1223+
1224+
```
1225+
EGL_VERSION 1.5 libglvnd ← 自建 dispatch 在答
1226+
/dev/dri/renderD128 drm driver nvidia-drm
1227+
eglInitialize EGL 1.5, vendor Mesa Project
1228+
/dev/dri/card0 drm driver simpledrm
1229+
gbm_bo_create 256x256 stride=1024 ← 真分配出 buffer object
1230+
eglInitialize EGL 1.5, vendor Mesa Project
1231+
```
1232+
1233+
`import khronos.egl;` 与两个 `import freedesktop.wayland.*;` 一起编译链接通过 ——
1234+
模块层与 C 库在同一次干净构建里都成立。
1235+
1236+
复现脚本见提交历史;三个坑值得记下:
1237+
1238+
1. **`[indices]` 同一个 path 不能挂两个键**。写 `compat` 和 `freedesktop` 都指向同一个
1239+
checkout,会注册成两个独立 project,之后每次查找都报
1240+
`package 'compat:libdrm@2.4.134' is ambiguous, candidates: … 'compat' … 'freedesktop'`。
1241+
只重定向被测的那个 namespace,其余走已发布索引。
1242+
2. **沙箱里 `find ~/.xlings -name mcpp` 会抓到别的 subos 的二进制**,它们的 interpreter
1243+
不在,失败信息是干巴巴的 `not found`,读起来像「mcpp 没装上」。要先试本 subos 的 `bin/`。
1244+
3. **沙箱不挂 cwd**,落点是合成的 `/home/speak`(只有 dotfile)。脚本要放进 subos 目录
1245+
才进得去。
1246+
1247+
### 19.5 xlings pin:§15 的结论仍然成立,但它指出的缺口是真的
1248+
1249+
任务里再次出现「pin 内部依赖的 xlings 到 `2026.8.27.4`」。**§15 已经查过并否掉了**:
1250+
`.4` 比 `.5` **旧**三小时,三仓的 `kXlingsVersion` 已经都在 `.5`,而 `.5` 有 `.4` 没有的
1251+
行为(声明在解析时压过索引)。下调等于让生态退回一个能力更弱的版本。
1252+
1253+
但这条要求指出的**缺口是真的,只是位置不对**:两个 fork 的 CI **根本没钉**,
1254+
`curl … | bash` 拿的是当天最新。已改为钉 `XLINGS_VERSION: "2026.8.27.5"`,与
1255+
`kXlingsVersion` 一致。
1256+
1257+
**mcpp 本身仍不钉**,这是刻意的:这些包依赖的正是**生态**(`xim:mesa` 声明
1258+
`GBM_BACKENDS_PATH` / `__EGL_VENDOR_LIBRARY_DIRS`),而钉死的 mcpp tarball 带的是生态的
1259+
冻结快照。**钉住装的工具,放开被测的生态** —— 这个切分才让失败可归因。
1260+
1261+
### 19.6 一个自己造成的破坏,记下来
1262+
1263+
为了承载模块重命名,`mcpplibs/wayland` 的 `v1.26.0` 被**原地重切**(上游没有更新版本
1264+
可跟,而版本号必须与上游对齐)。后果是立即的:
1265+
1266+
```
1267+
main 上 freedesktop.wayland.lua 的 sha256 0a5dd54a…
1268+
tag 上 tarball 的实际 sha256 961a900d… ← 不匹配
1269+
```
1270+
1271+
在 PR #293 合并之前,`freedesktop.wayland@1.26.0` 从 main 装不下来。**正确的次序应当是
1272+
先改描述符、合并,再重切 tag**,而不是反过来。
1273+
1274+
还有一个更隐蔽的:store 只按 `(name, version)` 索引,所以**已经装过 1.26.0 的机器不会
1275+
重新下载**,会继续用旧模块名而毫无提示 —— 这正是
1276+
`stale-global-index-masks-descriptor-bugs` 那条。开发机上需要手动清:
1277+
1278+
```bash
1279+
rm -rf ~/.mcpp/registry/data/xpkgs/freedesktop-x-wayland*/1.26.0 \
1280+
~/.mcpp/registry/data/xpkgs/freedesktop-x-egl/1.7.0 \
1281+
~/.mcpp/build-cache/v1/pkg/freedesktop ~/.mcpp/build-cache/v1/tool/freedesktop
1282+
```
1283+
1284+
### 19.7 多角度审视
1285+
1286+
| 角度 | 这一轮的结果 |
1287+
|---|---|
1288+
| **架构** | 五个包里只剩 GBM 一个绑定,而它是唯一真正过不了「可否独立分发」的。判据终于和实现一致 |
1289+
| **稳定性** | CI 新增「符号内容 + obj 计数」——SONAME 检查对**空库**也会绿。两个 fork 都钉了 xlings |
1290+
| **优雅/简洁** | 一个索引条目出两个库(`libGLdispatch` 走 path 依赖),消费者只写一行 |
1291+
| **用户体验** | `import khronos.egl;` 换实现不用改代码;`EGL_*` 宏仍需 include,这一点写进了描述符 |
1292+
| **兼容性** | 模块重命名是**破坏性**的。选在 wayland 合并当天做,消费者只有示例 09 |
1293+
| **跨平台** | 按架构选 stub 移进 `build.mcpp`,aarch64/ppc64 由构造成立而非靠改代码 |
1294+
| **一致性** | 模块命名规则统一为「接口所有者」,与 openkal 的教训对齐 |
1295+
| **无感升级** | ⚠ **没做到**,见 §19.6:同版本重切 tag 会被 store 掩盖。这是本轮唯一的真缺陷 |
1296+
1297+
### 19.8 仍未闭合
1298+
1299+
- **PR #293 必须合并**才能修好 main 上 wayland 的 sha256(§19.6)。
1300+
- 示例 09 的更新(mcpp PR #532)在本地待推,依赖 #293 先合。
1301+
- `libGL` / `libGLX` / `libGLESv{1,2}` 未构建。各自是 fork 里一个成员加一张
1302+
`mcpp/generated/` 里的 dispatch 表;索引里目前没有消费者。
1303+
- `freedesktop.egl` 的 `libEGL.so.1` 比 payload 的多三条 `DT_NEEDED`
1304+
(libstdc++/libm/libgcc_s),因为模块接口单元被编译进库里。`freedesktop.wayland` 完全
1305+
同形,是「模块层随库一起发」的既定结果,已写进描述符。

0 commit comments

Comments
 (0)