|
1 | 1 | # mcpp 图形栈:从「能跑通」到「能开发」的覆盖面设计 |
2 | 2 |
|
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 八角度实现后复核)** |
4 | 4 |
|
5 | 5 | ## 0. 这份文档解决什么 |
6 | 6 |
|
@@ -349,16 +349,19 @@ G6 libudev 路线决策 → libseat ← 最后,单独评估 |
349 | 349 |
|
350 | 350 | ## 6. 多角度评估 |
351 | 351 |
|
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 | |
362 | 365 |
|
363 | 366 | --- |
364 | 367 |
|
@@ -1429,3 +1432,119 @@ RESULT: PASS |
1429 | 1432 | **pango 是唯一量准后仍需决策的**:57k 行本身不大,但硬依赖 glib,而 glib 是 |
1430 | 1433 | `glib/` + `gobject/` + `gio/` 三大块 300k+ 行,自己还缺 `pcre2` 和 `libmount`。 |
1431 | 1434 | 按 §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