Skip to content

Commit c188c38

Browse files
authored
docs: §18 八个角度的实现后复核(v1.5) (#314)
* docs: §18 八个角度的实现后复核(v1.5) §6 那张表是设计时写的,实现之后没回头修订。逐条回看,八条里有四条被推翻或需要 重述——把它们留在原样比不写更糟,下一个人会照着一张已经不成立的表做决定。 推翻两条: 架构 「新增仓数量 = 0」→ 实际新建五个。错在把「进已有 fork」当成可选的组织 方式;它不是,一个 fork 仓对应一个上游一个版本。 兼容性 「全部新增,不动任何已发布包」→ 动了三个。错在把「新增」等同于「无风 险」:DISCOVERY 加一行会让现有提供方自动继承它;新增一个消费者会暴露 compat.freetype 的源码列表缺口。 重述四条: 稳定性 风险不在难写的代码里,在写完之后没人检查的断言里。实现期代价最高的 三类失败都不在设计时的风险清单上。 跨平台 真正咬人的不是 CPU 架构,是编译器与标准库(static inline 实例化、 <sstream> 传递包含),两个都只有 llvm 腿抓得到。 一致性 「生成物签进仓」被推翻——改由 build.mcpp 构建期产出,由此消掉了 「数据与代码不一致」这个状态本身。 无感升级 同版本重发换了三种形态出现,结论是 tag 指向历史、资产承载分发。 成立两条:优雅(feature 机制把「替消费者选」变成「让消费者选」)、用户体验 (模块层不只是换写法,它是放置适配代码的位置)。 只改 .agents/docs/。 * docs: §6 每行标注结论并指向 §18 §18 的整个理由是「下一个人会照着 §6 那张表做决定」——那 §6 自己就必须说它 已经被推翻,否则读者根本走不到 §18。加一列结论标记(❌/⚠/✅)+ 小节号, 以及一句「不要单独引用本节」。
1 parent 217884d commit c188c38

1 file changed

Lines changed: 130 additions & 11 deletions

File tree

.agents/docs/2026-08-30-graphics-stack-coverage-design.md

Lines changed: 130 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# mcpp 图形栈:从「能跑通」到「能开发」的覆盖面设计
22

3-
Date: 2026-08-30 · 前置:[`2026-08-30-gbm-cross-repo-closed-loop-plan.md`](2026-08-30-gbm-cross-repo-closed-loop-plan.md) §19/§20 · **状态:已实现并闭环验证(v1.4,§11 总账 / §12 客户端侧 / §14 fork 规范 / §17 桌面栈补齐与判据修正)**
3+
Date: 2026-08-30 · 前置:[`2026-08-30-gbm-cross-repo-closed-loop-plan.md`](2026-08-30-gbm-cross-repo-closed-loop-plan.md) §19/§20 · **状态:已实现并闭环验证(v1.5,§11 总账 / §14 fork 规范 / §17 桌面栈补齐 / §18 八角度实现后复核)**
44

55
## 0. 这份文档解决什么
66

@@ -349,16 +349,19 @@ G6 libudev 路线决策 → libseat ← 最后,单独评估
349349

350350
## 6. 多角度评估
351351

352-
| 角度 | 评估 |
353-
|---|---|
354-
| **架构** | G1 不新建仓,复用 mcpplibs/libglvnd 已有的 `mcpp/generated/` 与 path 依赖机制;G3 复用 mcpplibs/wayland 的 scanner。**新增仓数量 = 0** |
355-
| **稳定性** | 四张生成表已实跑;CI 的 diff 守卫扩四行即可。风险集中在 G1b(模块导出)与 G3(体积),两者都可先验 |
356-
| **优雅/简洁** | 只做三个无 X11 的 GL 库,不碰 GLX/GL —— 覆盖合成器的全部需要,且不把 X11 拖进任何消费者 |
357-
| **用户体验** | 合成器作者的 `mcpp.toml` 从「缺渲染链」变成可写;`import khronos.glesv2;` 与既有命名一致 |
358-
| **兼容性** | 全部是新增条目,不动任何已发布包。唯一的行为变更是 G2a,而它修的是「静默落到软件渲染」 |
359-
| **跨平台** | GL 家族的 per-arch entry stub 沿用 `build.mcpp` 里已有的 `target_arch()` 选择,aarch64/ppc64 由构造成立;pixman 的 SIMD 自门控,不需要额外机制 |
360-
| **一致性** | 模块命名(khronos.*)、生成物签进仓、path 依赖三条规则原样复用,不引入新范式 |
361-
| **无感升级** | 全是新增包,无同版本重切 tag 的问题(那正是上一轮的教训) |
352+
> **这张表是设计时写的,八条里有四条已被实现推翻或需要重述。**
353+
> 每行末尾的标记给出结论,逐条的证据在 **§18**。不要单独引用本节。
354+
355+
| 角度 | 评估(设计时) | |
356+
|---|---|---|
357+
| **架构** | G1 不新建仓,复用 mcpplibs/libglvnd 已有的 `mcpp/generated/` 与 path 依赖机制;G3 复用 mcpplibs/wayland 的 scanner。**新增仓数量 = 0** | ❌ 实际五个仓,§18.1 |
358+
| **稳定性** | 四张生成表已实跑;CI 的 diff 守卫扩四行即可。风险集中在 G1b(模块导出)与 G3(体积),两者都可先验 | ⚠ 真正的风险不在此,§18.2 |
359+
| **优雅/简洁** | 只做三个无 X11 的 GL 库,不碰 GLX/GL —— 覆盖合成器的全部需要,且不把 X11 拖进任何消费者 | ✅ 且被 feature 加强,§18.3 |
360+
| **用户体验** | 合成器作者的 `mcpp.toml` 从「缺渲染链」变成可写;`import khronos.glesv2;` 与既有命名一致 | ✅ 且多一层,§18.4 |
361+
| **兼容性** | 全部是新增条目,不动任何已发布包。唯一的行为变更是 G2a,而它修的是「静默落到软件渲染」 | ❌ 动了三个,§18.5 |
362+
| **跨平台** | GL 家族的 per-arch entry stub 沿用 `build.mcpp` 里已有的 `target_arch()` 选择,aarch64/ppc64 由构造成立;pixman 的 SIMD 自门控,不需要额外机制 | ⚠ 咬人的是编译器不是架构,§18.6 |
363+
| **一致性** | 模块命名(khronos.*)、生成物签进仓、path 依赖三条规则原样复用,不引入新范式 | ⚠ 第二条已推翻,§18.7 |
364+
| **无感升级** | 全是新增包,无同版本重切 tag 的问题(那正是上一轮的教训) | ⚠ 换三种形态出现,§18.8 |
362365

363366
---
364367

@@ -1429,3 +1432,119 @@ RESULT: PASS
14291432
**pango 是唯一量准后仍需决策的**:57k 行本身不大,但硬依赖 glib,而 glib 是
14301433
`glib/` + `gobject/` + `gio/` 三大块 300k+ 行,自己还缺 `pcre2` 和 `libmount`。
14311434
按 §17.1 的新判据,它的难点也不在行数——需要先数它的生成器再定。
1435+
1436+
---
1437+
1438+
## 18. 八个角度的实现后复核
1439+
1440+
§6 的表是**设计时**写的。实现之后逐条回看,**八条里有四条被推翻或需要重述**——
1441+
把它们留在原样比不写更糟,因为下一个人会照着一张已经不成立的表做决定。
1442+
1443+
### 18.1 架构 —— ❌ 推翻
1444+
1445+
> 设计时:「不新建仓,**新增仓数量 = 0**」
1446+
1447+
实际新建 **五个** fork 仓:`wayland-protocols`、`libevdev`、`libxkbcommon`
1448+
(前三个见 §10.2)、`libdisplay-info`、`fontconfig`、`cairo`。
1449+
1450+
**错在哪**:当时以为「进已有 fork」是可选的组织方式。它不是——**一个 fork 仓对应
1451+
一个上游、一个版本**。wayland-protocols 有自己的版本号和发布周期,塞进
1452+
mcpplibs/wayland 就等于让两个上游共用一个 tag,那才是真正的架构错误。
1453+
1454+
**留下的规则**:fork 仓的数量由**上游的数量**决定,不由「想少建几个仓」决定。
1455+
1456+
### 18.2 稳定性 —— ⚠ 重述
1457+
1458+
设计时说「风险集中在模块导出与体积,两者都可先验」。体积确实可先验(cairo 47.8MB
1459+
→ 1.8MB,§17.4)。模块导出也确实是风险,但**真正咬人的不是它**。
1460+
1461+
实现期的失败按代价排序,前三名都不在当时的风险清单上:
1462+
1463+
1. **探测答案错**(§17.5、§17.6)——编过、链过、报 SUCCESS,行为错。三次。
1464+
2. **环境陈旧**(§17.7)——沙箱/CI/store 各一次,报错全部指向别处。
1465+
3. **上游自己的 `.gitignore`**(§17.6)——fork 少了 5 个发布物文件。
1466+
1467+
**留下的规则**:风险不在「难写的代码」里,在「写完之后没人检查的断言」里。所以
1468+
每个 fork 的 CI 都要有一条**拿产物和上游对照**的检查,而不只是「能编过」。
1469+
1470+
### 18.3 优雅/简洁 —— ✅ 成立,而且被 feature 机制加强
1471+
1472+
设计时的「只做三个无 X11 的 GL 库」成立。cairo 把同一个想法做到了更好的形态:
1473+
**不是替消费者选,而是让消费者选**——`default = ["ft","fc","png"]`,X11 是
1474+
feature(§17.4)。
1475+
1476+
差别是真实的:「只做无 X11 的库」意味着要 X11 的人没得用;feature 意味着他写一行
1477+
就有。
1478+
1479+
### 18.4 用户体验 —— ✅ 成立,且比预期多一层
1480+
1481+
`import khronos.glesv2;` 那条成立。多出来的一层是**模块消掉了上游的缺陷**:
1482+
libdisplay-info 七个公共头一个 `extern "C"` 都没有,C++ 消费者 `#include` 会
1483+
mangle 到链接失败;`import freedesktop.displayinfo;` 把包装做在模块内(§14.2)。
1484+
1485+
**留下的规则**:模块层不只是「换个写法」,它是**放置适配代码的位置**。
1486+
1487+
### 18.5 兼容性 —— ❌ 推翻
1488+
1489+
> 设计时:「全部是新增条目,**不动任何已发布包**」
1490+
1491+
实际动了三个已发布包:
1492+
1493+
- `compat.freetype` 补 `ftsynth.c`(#311)——cairo 无条件调它
1494+
- `xim:mesa` 改成只声明自己填的路径(xim#733)——加一行 DISCOVERY 造成的回归
1495+
- `graphics.consumer_envs` 跳过标量行(xim#735)——同一行造成的第二处回归
1496+
1497+
**错在哪**:当时把「新增」等同于「无风险」。**新增一个条目会改变现有条目的行为**
1498+
——DISCOVERY 表加一行,`declare_subos_env(tag)` 不传 `only` 的调用点就自动继承了
1499+
它;新增一个消费者,`compat.freetype` 的源码列表缺口就暴露了。
1500+
1501+
**留下的规则**:见 §14 的三项检查清单,以及「一个 compat 包的源码列表就是它的
1502+
ABI 承诺」(§17.6)。
1503+
1504+
### 18.6 跨平台 —— ⚠ 重述
1505+
1506+
设计时只考虑了 CPU 架构(aarch64/ppc64)。实现期真正咬人的跨平台维度是
1507+
**编译器与标准库**:
1508+
1509+
- `wl_shm_create_pool` 是 `static inline`,取地址强制实例化 → GNU ld 经传递
1510+
DT_NEEDED 解析,**lld 不会**(#301)
1511+
- `<sstream>` 被 libstdc++ 经 `<fstream>` 传递包含,**libc++ 不会**(#310)
1512+
1513+
两个都只有 mcpp-index 的 **llvm 腿**抓得到——它没有 sysroot 且用 lld。
1514+
1515+
**留下的规则**:fork 里任何 `build.mcpp` 或模块包装的改动,推之前先
1516+
`mcpp toolchain default llvm` 跑一遍。cairo 的 fork CI 已经把这一步固化。
1517+
1518+
### 18.7 一致性 —— ⚠ 重述
1519+
1520+
三条规则(模块命名、生成物签进仓、path 依赖)里,**第二条被推翻**:生成物不再
1521+
签进仓,改由 `build.mcpp` 在构建期产出(§14.1)。
1522+
1523+
这不是风格变更,是消掉了一整类状态:**没有签进仓的生成物,就没有「数据与代码不
1524+
一致」这个状态**,也就不需要「重生成再 diff」的 CI 步骤。
1525+
1526+
另外两条原样成立,并新增一条:**feature 的 `sources` 逐条列,不用 glob**
1527+
(§17.4)。
1528+
1529+
### 18.8 无感升级 —— ⚠ 重述
1530+
1531+
> 设计时:「全是新增包,无同版本重切 tag 的问题」
1532+
1533+
同版本重发的问题**换了三种形态出现**:
1534+
1535+
| 形态 | 后果 |
1536+
|---|---|
1537+
| tag archive 做 URL | 重切直接打断已发布描述符的 sha256 |
1538+
| store 按 `(name, version)` 缓存 | 同版本重发**不重新解压** |
1539+
| 删 tag 再建 | release 掉成**草稿**,资产 404 |
1540+
1541+
三个都指向同一条,已成为本轮的操作规则:**tag 指向历史,资产承载分发,不要动
1542+
tag**——把新内容作为 release 资产发布,旧资产原样保留,零窗口(§12.2)。
1543+
1544+
### 18.9 一句话
1545+
1546+
八条里**两条推翻**(架构、兼容性)、**四条重述**(稳定性、跨平台、一致性、无感
1547+
升级)、**两条成立且被加强**(优雅、用户体验)。
1548+
1549+
设计时的判断有一半没扛过实现——而这正是「验证要更新到文档」的意义:留下的应该是
1550+
**被证伪之后的那一版**。

0 commit comments

Comments
 (0)