From 58cf8b7246fe77f5ecfe0b077e7e665ec62ecdfb Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 31 Aug 2026 13:39:07 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(=E6=A8=A1=E5=9D=97):=20=E6=89=98?= =?UTF-8?q?=E7=AE=A1=E6=96=B9=E4=B8=8D=E6=98=AF=E8=B4=9F=E8=B4=A3=E6=96=B9?= =?UTF-8?q?=20=E2=80=94=E2=80=94=20=E6=94=B9=E5=90=8D=20cairo/displayinfo,?= =?UTF-8?q?=E5=B9=B6=E8=A1=A5=E9=BD=90=E4=B8=A4=E4=B8=AA=E4=B8=8D=E5=AE=8C?= =?UTF-8?q?=E5=A4=87=E7=9A=84=E5=8C=85=E8=A3=85=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改名:freedesktop.cairo -> cairo,freedesktop.displayinfo -> displayinfo。 索引 namespace 是货架标签,从不进模块名。cairo 是它自己的项目,freedesktop.org 托管 git 但不拥有那套接口;libdisplay-info 解析的是 VESA 的 EDID/DisplayID。同 一条规则下 freedesktop.egl 导出的是 khronos.egl。 ⭐ 而且两个包装体都不完备: cairo 470 -> 697 缺 cairo_t,以及全部 192 个枚举量 displayinfo 206 -> 501 缺全部 295 个枚举量 cairo_t 的原因很具体:正则 cairo_[a-z0-9_]+_t 要求 cairo_ 与 _t 之间至少一个 字符,于是 `typedef struct _cairo cairo_t;` —— 这个库里用得最多的类型 —— 恰好 是它唯一匹配不到的名字。 为什么一直没人发现,这才是要记的: - cairo:唯一 import 那个模块的测试同时写了 #include ,头文件把模块 缺的都补上了。一个从没被要求独立站住的 import,不是被测试了,是被装饰了。 - displayinfo:没有任何测试点过一个枚举量。而且只在 GCC 上加一个也发现不了 —— 导出 enum 会让枚举量在 GCC 可见,clang 拒绝同一个文件。 它是从外面被发现的:gnome.pangocairo 不能混用两条消费路线,只能问模块要 cairo_t,结果什么都没拿到。pango 那一版因此自己扫了 cairo.h;现在 cairo 修好, 那个变通已经删掉,普通的 export import cairo; 就够了。 发布形态:这两个包用 release 资产而不是 tag 归档,所以走 -mcpp3 新资产,tag 不 动、零窗口。pango 重切 tag(sha 0882a260…,CN 容器 tag 1.56.1-5)。 ⚠️ 验证时撞上了本地 store 按 (name, version) 索引的固定代价:已解开过 1.18.2 的 机器拿不到新资产,报 `failed to read compiled module`,清掉 store 条目才对。已 写进 §23.4。 cairo 的测试拆成两个文件,一条路线一个,module.cpp 一个头都不包;displayinfo 的 测试比较两个枚举量 —— 导出清单再塌陷就是编译错误。 五个示例 × 两条工具链全绿。 --- ...26-08-30-graphics-stack-coverage-design.md | 71 ++++++++++- pkgs/f/freedesktop.cairo.lua | 36 +++++- pkgs/f/freedesktop.libdisplay-info.lua | 29 ++++- pkgs/p/gnome.pango.lua | 6 +- pkgs/p/gnome.pangocairo.lua | 26 ++-- pkgs/p/gnome.pangoft2.lua | 6 +- tests/examples/cairo/tests/cairo.cpp | 19 ++- tests/examples/cairo/tests/module.cpp | 114 ++++++++++++++++++ .../libdisplay-info/tests/libdisplay-info.cpp | 15 ++- 9 files changed, 295 insertions(+), 27 deletions(-) create mode 100644 tests/examples/cairo/tests/module.cpp diff --git a/.agents/docs/2026-08-30-graphics-stack-coverage-design.md b/.agents/docs/2026-08-30-graphics-stack-coverage-design.md index 5125f09..14e25f5 100644 --- a/.agents/docs/2026-08-30-graphics-stack-coverage-design.md +++ b/.agents/docs/2026-08-30-graphics-stack-coverage-design.md @@ -1,6 +1,6 @@ # mcpp 图形栈:从「能跑通」到「能开发」的覆盖面设计 -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.9,§11 总账 / §14 fork 规范 / §18 八角度复核 / §19 合成器闸门与 GObject 栈,含沙箱实测 · §20 gio 与一次被推翻的判断 · §21 namespace 是契约 · §22 pango:文本排版接上了)** +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 · **状态:已实现并闭环验证(v2.0,§11 总账 / §14 fork 规范 / §18 八角度复核 / §19 合成器闸门与 GObject 栈,含沙箱实测 · §20 gio 与一次被推翻的判断 · §21 namespace 是契约 · §22 pango:文本排版接上了 · §23 模块名与包装体完备性)** ## 0. 这份文档解决什么 @@ -2174,3 +2174,72 @@ error: dependency 'gnome.gobject' is requested as both a version dep | 端到端 | 1,492 个像素 | **文本排版那条线接上了。** + +--- + +## 23. 模块名与包装体完备性:两个已发布包的修正(2026-08-31) + +### 23.1 托管方不是负责方 —— 模块名改了两个 + +| 包(索引键) | 旧模块名 | 新模块名 | +|---|---|---| +| `freedesktop.cairo` | `freedesktop.cairo` | **`cairo`** | +| `freedesktop.libdisplay-info` | `freedesktop.displayinfo` | **`displayinfo`** | + +**索引 namespace 是货架标签,从不进模块名。** cairo 是它自己的项目, +freedesktop.org 托管 git 但不拥有那套接口;libdisplay-info 解析的是 **VESA** 的 +EDID/DisplayID,`freedesktop.*` 这个模块前缀留给 freedesktop 自己的规范(像 +`freedesktop.wayland.client`)。同一条规则下 `freedesktop.egl` 导出的是 +`khronos.egl`。 + +改名会让老消费者**编译报错并点名模块** —— 这是最该发现问题的地方。 + +### 23.2 ⭐ 两个包装体都不完备,而且都是「没人问过」 + +| | 导出 | 缺了什么 | +|---|---|---| +| `cairo` | 470 → **697** | `cairo_t`,以及**全部 192 个枚举量** | +| `displayinfo` | 206 → **501** | **全部 295 个枚举量** | + +`cairo_t` 的原因很具体:正则是 `cairo_[a-z0-9_]+_t`,要求 `cairo_` 与 `_t` 之间 +至少有一个字符 —— 于是 +```c +typedef struct _cairo cairo_t; /* cairo.h:135 */ +``` +**这个库里用得最多的类型,恰好是它唯一匹配不到的名字。** + +**为什么一直没人发现,这才是要记的:** + +- cairo:唯一 import 那个模块的测试**同时**写了 `#include `,头文件把 + 模块缺的都补上了。**一个从没被要求独立站住的 import,不是被测试了,是被装饰了。** +- displayinfo:**没有任何测试点过一个枚举量**。而且只在 GCC 上加一个也发现不了 —— + 导出 enum 会让枚举量在 GCC 可见,clang 拒绝同一个文件(§21.5)。 + +### 23.3 它是从**外面**被发现的 + +`gnome.pangocairo` **不能混用两条消费路线**(`pangocairo.h` 经 glib 到 +``),所以它只能问模块要 `cairo_t` —— 结果什么都没拿到。 + +于是 pango 那一版带了个变通:**自己扫 `cairo.h`**。现在 cairo 修好了, +**那个变通已经删掉**,普通的 `export import cairo;` 就够了 —— 本来就该够。 + +> 一个包的包装体缺不缺,自己的测试可能永远问不出来;**是别的包在用它的时候问出来的。** + +### 23.4 发布形态:发资产,不重切 tag + +这两个包用的是 **release 资产**(`cairo-1.18.2-mcpp2.tar.gz`)而不是 tag 归档, +所以修正走 `-mcpp3` 新资产:**tag 不动,零窗口**,而且资产名自带修订号。 +(对比 glib/pango 用 tag 归档,只能原地重切 + 换 sha。) + +⚠️ 本地 store 按 `(name, version)` 索引,所以已经解开过 `1.18.2` 的机器**拿不到 +新资产** —— 本次验证时就撞上了(`failed to read compiled module`),清掉 store +条目才对。这是「包版本与上游对齐」这条约定的固定代价,值得写在这里。 + +### 23.5 结果 + +| | | +|---|---| +| `cairo` | 697 个导出;测试拆成两个文件,一条路线一个,module.cpp **一个头都不包** | +| `displayinfo` | 501 个导出;测试比较两个枚举量,导出清单再塌陷就是编译错误 | +| `gnome.pangocairo` | 变通删除,`export import cairo;` | +| 验证 | 五个示例 × 两条工具链全绿 | diff --git a/pkgs/f/freedesktop.cairo.lua b/pkgs/f/freedesktop.cairo.lua index 95cfa61..deb3252 100644 --- a/pkgs/f/freedesktop.cairo.lua +++ b/pkgs/f/freedesktop.cairo.lua @@ -47,6 +47,36 @@ -- `cairo_paint` working, and gives every PATH garbage fixed-point coordinates: -- `cairo_rectangle(4,4,16,16)` came out with extents -8.03e+06 … 4.37e+06 and -- `cairo_fill` changed zero pixels. Measured. +-- +-- ───────────────────────────────────────────────────────────────────────── +-- ⚠️ THE MODULE IS `cairo`, NOT `freedesktop.cairo` +-- +-- import cairo; +-- +-- cairo is its own project; freedesktop.org hosts the git and does not own the +-- interface. **The index namespace is a shelf label and never enters the module +-- name** — the same reason `freedesktop.egl` exports `khronos.egl`. +-- +-- This changed in the mcpp3 asset. A consumer on the old name gets a compile +-- error naming the module, which is the right place to find out. +-- +-- ⭐ AND THE WRAPPER WAS INCOMPLETE: 470 names, missing `cairo_t` and all 192 +-- enumerators. It was found from OUTSIDE, by `gnome.pangocairo`, which cannot +-- mix the two consumption routes and therefore had to ask the module for +-- `cairo_t` and got nothing. +-- +-- cairo_t `typedef struct _cairo cairo_t;` — the typedef regex +-- needed a character between `cairo_` and `_t`, so the +-- most-used type in the library was the one it could not +-- match. +-- 192 enumerators every CAIRO_FORMAT_*, CAIRO_STATUS_*, CAIRO_OPERATOR_*. +-- The scan never entered a `typedef enum` body. +-- +-- ⚠️ WHY NOBODY NOTICED, and the lesson worth keeping: the only test that +-- imported the module ALSO wrote `#include `, so the header supplied +-- whatever the module lacked. **An import that is never asked to stand on its +-- own is not tested, it is decorated.** The fork now has two test files, one +-- per route, and exports 697 names. package = { spec = "1", namespace = "freedesktop", @@ -60,10 +90,10 @@ package = { linux = { ["1.18.2"] = { url = { - GLOBAL = "https://github.com/mcpplibs/cairo/releases/download/v1.18.2/cairo-1.18.2-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/cairo/releases/download/1.18.2/cairo-1.18.2-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/cairo/releases/download/v1.18.2/cairo-1.18.2-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/cairo/releases/download/1.18.2/cairo-1.18.2-mcpp3.tar.gz", }, - sha256 = "202352a38d0847628c55cd0f9c67e85ac78667ad76bc88691ab47606d1104ef7", + sha256 = "5a82cfb52365f40b7ecc679d7d67c3c34459aef4a69b4b75c2c0c964d74ec6e1", }, }, }, diff --git a/pkgs/f/freedesktop.libdisplay-info.lua b/pkgs/f/freedesktop.libdisplay-info.lua index 5aeb4ed..71b562e 100644 --- a/pkgs/f/freedesktop.libdisplay-info.lua +++ b/pkgs/f/freedesktop.libdisplay-info.lua @@ -45,7 +45,7 @@ -- THE MODULE IS GENERATED, not hand-written: 206 names read out of the public -- headers, so a version bump cannot silently drop one. -- --- import freedesktop.displayinfo; +-- import displayinfo; -- -- and that also removes a real burden — not one of the seven public headers has -- an `extern "C"` block, so a C++ TU that #includes them mangles every @@ -53,6 +53,27 @@ -- di_info_get_make(di_info const*)`. The module does that wrapping once, inside -- the module purview, so a consumer does not. (compat.libseat has the same -- upstream problem and no module, so there the consumer still wraps.) +-- +-- ───────────────────────────────────────────────────────────────────────── +-- ⚠️ THE MODULE IS `displayinfo`, NOT `freedesktop.displayinfo` +-- +-- import displayinfo; +-- +-- freedesktop.org HOSTS this library; the interfaces it parses are **VESA's** +-- (EDID, DisplayID). `freedesktop.*` in a module name is for freedesktop's own +-- specifications, the way `freedesktop.wayland.client` is. The index namespace +-- is a shelf label and never enters the module name. +-- +-- This changed in the mcpp3 asset; a consumer on the old name gets a compile +-- error naming the module. +-- +-- ⭐ AND THE WRAPPER EXPORTED 206 NAMES AND NOT ONE ENUMERATOR — the module +-- carried `enum di_edid_…` but nothing inside it. 295 of them. +-- +-- ⚠️ It went unnoticed because no test named an enumerator, and adding one on +-- GCC alone would not have noticed either: exporting an enum makes its +-- enumerators visible there, and clang rejects the same file. The same gap was +-- in `freedesktop.cairo`. 501 names now, and the test compares two enumerators. package = { spec = "1", namespace = "freedesktop", @@ -66,10 +87,10 @@ package = { linux = { ["0.2.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/libdisplay-info/releases/download/v0.2.0/libdisplay-info-0.2.0-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/libdisplay-info/releases/download/0.2.0/libdisplay-info-0.2.0-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/libdisplay-info/releases/download/v0.2.0/libdisplay-info-0.2.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/libdisplay-info/releases/download/0.2.0/libdisplay-info-0.2.0-mcpp3.tar.gz", }, - sha256 = "8df9a8064146b2b38378bd8b146894d084ffda43b88b43c485b954da073a7617", + sha256 = "8b2238a7b275e7da831d96c4f73b607a522d03620e6d5e0f12c605ef04659c8b", }, }, }, diff --git a/pkgs/p/gnome.pango.lua b/pkgs/p/gnome.pango.lua index bd53a03..be83a69 100644 --- a/pkgs/p/gnome.pango.lua +++ b/pkgs/p/gnome.pango.lua @@ -84,14 +84,14 @@ package = { ["1.56.1"] = { url = { GLOBAL = "https://github.com/mcpplibs/pango/archive/refs/tags/1.56.1.tar.gz", - -- ⚠️ The container tag is `1.56.1-4`, not `1.56.1`. gitcode + -- ⚠️ The container tag is `1.56.1-5`, not `1.56.1`. gitcode -- refuses to REPLACE an asset of the same name in an -- existing release, so each corrected tarball needs a new -- container tag while the PACKAGE version stays upstream's. -- Verified byte-identical to the GLOBAL tag archive. - CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-4/pango-1.56.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-5/pango-1.56.1.tar.gz", }, - sha256 = "b470a658e05ef0e14d779bc852371ce52dd11efa76d10782347be40f1d63476b", + sha256 = "0882a260346cbdb6b553177269a8acc083a2985bbe929b458406af98e90a03ad", }, }, }, diff --git a/pkgs/p/gnome.pangocairo.lua b/pkgs/p/gnome.pangocairo.lua index d018fc3..c3190f5 100644 --- a/pkgs/p/gnome.pangocairo.lua +++ b/pkgs/p/gnome.pangocairo.lua @@ -22,12 +22,20 @@ -- then segfaulted. The fork's test now reads the font map's FONT TYPE and -- requires `CAIRO_FONT_TYPE_FT`, which catches it immediately. -- --- ⚠️ AND freedesktop.cairo's MODULE IS NOT SUFFICIENT ON ITS OWN. Measured on --- 1.18.2: 470 names, ZERO enumerators (no `CAIRO_FORMAT_ARGB32`, no --- `CAIRO_FONT_TYPE_FT`) and no `cairo_t`. The index's own cairo example does --- not notice, because it writes BOTH `#include ` and --- `import freedesktop.cairo;`. pangocairo cannot mix the routes, so its module --- scans cairo.h itself; that can go when cairo's wrapper is fixed.-- +-- ✅ AND THIS PACKAGE IS WHERE cairo's MODULE GAP WAS FOUND. On the +-- `1.18.2-mcpp2` asset `freedesktop.cairo` exported 470 names, ZERO +-- enumerators and no `cairo_t`. The index's own cairo example did not notice, +-- because it wrote BOTH `#include ` and `import freedesktop.cairo;` +-- — the header supplied what the module lacked, so that import had never been +-- asked to stand on its own. +-- +-- It surfaced here because pangocairo CANNOT mix the two routes +-- (`pangocairo.h` reaches glib and therefore ), so it had to ask the +-- module for `cairo_t` and got nothing. This package carried a workaround for +-- one release — scanning cairo.h itself — and that workaround is GONE: +-- `1.18.2-mcpp3` exports 697 names and the plain re-export is enough. +-- +-- The module it re-exports is now `cairo`, not `freedesktop.cairo`.-- -- ───────────────────────────────────────────────────────────────────────── -- ⭐ TWO WAYS TO CONSUME IT, AND YOU PICK ONE -- @@ -90,14 +98,14 @@ package = { ["1.56.1"] = { url = { GLOBAL = "https://github.com/mcpplibs/pango/archive/refs/tags/1.56.1.tar.gz", - -- ⚠️ The container tag is `1.56.1-4`, not `1.56.1`. gitcode + -- ⚠️ The container tag is `1.56.1-5`, not `1.56.1`. gitcode -- refuses to REPLACE an asset of the same name in an -- existing release, so each corrected tarball needs a new -- container tag while the PACKAGE version stays upstream's. -- Verified byte-identical to the GLOBAL tag archive. - CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-4/pango-1.56.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-5/pango-1.56.1.tar.gz", }, - sha256 = "b470a658e05ef0e14d779bc852371ce52dd11efa76d10782347be40f1d63476b", + sha256 = "0882a260346cbdb6b553177269a8acc083a2985bbe929b458406af98e90a03ad", }, }, }, diff --git a/pkgs/p/gnome.pangoft2.lua b/pkgs/p/gnome.pangoft2.lua index e0fccf2..876fddf 100644 --- a/pkgs/p/gnome.pangoft2.lua +++ b/pkgs/p/gnome.pangoft2.lua @@ -68,14 +68,14 @@ package = { ["1.56.1"] = { url = { GLOBAL = "https://github.com/mcpplibs/pango/archive/refs/tags/1.56.1.tar.gz", - -- ⚠️ The container tag is `1.56.1-4`, not `1.56.1`. gitcode + -- ⚠️ The container tag is `1.56.1-5`, not `1.56.1`. gitcode -- refuses to REPLACE an asset of the same name in an -- existing release, so each corrected tarball needs a new -- container tag while the PACKAGE version stays upstream's. -- Verified byte-identical to the GLOBAL tag archive. - CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-4/pango-1.56.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-5/pango-1.56.1.tar.gz", }, - sha256 = "b470a658e05ef0e14d779bc852371ce52dd11efa76d10782347be40f1d63476b", + sha256 = "0882a260346cbdb6b553177269a8acc083a2985bbe929b458406af98e90a03ad", }, }, }, diff --git a/tests/examples/cairo/tests/cairo.cpp b/tests/examples/cairo/tests/cairo.cpp index 2aa0c94..38f5c13 100644 --- a/tests/examples/cairo/tests/cairo.cpp +++ b/tests/examples/cairo/tests/cairo.cpp @@ -1,4 +1,4 @@ -// freedesktop.cairo — draw something, read the pixels back, and check that the +// cairo, via its HEADERS — draw something, read the pixels back, and check that the // feature split actually took effect. // // Cairo needs no display and no fonts on disk to draw: an image surface is @@ -14,11 +14,26 @@ #ifdef __linux__ #include +// ⚠️ cairo.h does NOT reach the feature headers. This name used to arrive from +// the module — which is precisely the confusion this split removes: on the +// header route you include what you use. +#include #include #include -import freedesktop.cairo; +// ⚠️ THIS FILE IS THE HEADER ROUTE, AND IT NO LONGER IMPORTS. +// +// It used to do BOTH — `#include ` and `import freedesktop.cairo;` — +// and that is why nobody noticed the module was incomplete: the header +// supplied every name the module was missing, including `cairo_t` and all 192 +// enumerators. An import that is never asked to stand on its own is not +// tested, it is decorated. +// +// `tests/module.cpp` next door takes the module route alone. Two files, +// because on GCC the two routes cannot be combined in one TU anyway: reaching +// a system header both textually and through a module's global fragment makes +// the same type two entities. namespace { diff --git a/tests/examples/cairo/tests/module.cpp b/tests/examples/cairo/tests/module.cpp new file mode 100644 index 0000000..43eda2a --- /dev/null +++ b/tests/examples/cairo/tests/module.cpp @@ -0,0 +1,114 @@ +// cairo, via its MODULE — and this is the route that had never been checked. +// +// ⭐ WHY THIS FILE EXISTS +// +// The package exposed `import freedesktop.cairo;` and the only test that named +// it also wrote `#include `. So the header supplied whatever the +// module lacked, and what it lacked was substantial: +// +// 470 names exported, and among the missing: +// cairo_t the single most-used type in the library +// 192 enumerators every CAIRO_FORMAT_*, CAIRO_STATUS_*, CAIRO_OPERATOR_* +// +// It was found from OUTSIDE, by gnome.pangocairo — which cannot mix the two +// routes (pangocairo.h reaches glib and therefore , so a textual +// include alongside the import makes `struct _IO_FILE` two entities) and +// therefore had to ask the module for `cairo_t` and got nothing. +// +// This file includes NOTHING. That is the whole point: if a name is missing +// from the module, this fails to compile. + +#ifdef __linux__ + +import cairo; + +#include + +namespace { + +int failures = 0; + +void check(bool ok, const char *what) +{ + std::printf("%-58s %s\n", what, ok ? "ok" : "FAILED"); + if (!ok) { + ++failures; + } +} + +} // namespace + +int main() +{ + std::printf("import cairo — %s\n\n", cairo_version_string()); + + // ── the types, including the one that used to be missing ───────────── + cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 64, 64); + check(cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS, + "cairo_image_surface_create — CAIRO_FORMAT_ARGB32 is an ENUMERATOR"); + check(cairo_image_surface_get_format(surf) == CAIRO_FORMAT_ARGB32, + "…and the surface reports the format that was asked for"); + + // ⭐ `cairo_t` — `typedef struct _cairo cairo_t;`. The old regex required at + // least one character between `cairo_` and `_t`, so this one name, the one + // every cairo program starts with, was the one it could not match. + cairo_t *cr = cairo_create(surf); + check(cairo_status(cr) == CAIRO_STATUS_SUCCESS, "cairo_create returns a live cairo_t"); + + // ── draw, and read the pixels back ─────────────────────────────────── + cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); + cairo_rectangle(cr, 8, 8, 48, 48); + cairo_fill(cr); + cairo_surface_flush(surf); + + const unsigned char *d = cairo_image_surface_get_data(surf); + const int stride = cairo_image_surface_get_stride(surf); + check(d != nullptr, "the image surface has readable pixel data"); + + long ink = 0; + for (int y = 0; d && y < 64; ++y) { + for (int x = 0; x < 64; ++x) { + if (d[static_cast(y) * stride + 4 * x + 3] != 0) { + ++ink; + } + } + } + std::printf(" filled pixels: %ld (expected 48*48 = 2304)\n", ink); + check(ink == 48 * 48, "the rectangle is exactly the pixels it asked for"); + + // ── more enumerators, from three different enums ───────────────────── + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + check(cairo_get_operator(cr) == CAIRO_OPERATOR_OVER, "CAIRO_OPERATOR_OVER"); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + check(cairo_get_line_cap(cr) == CAIRO_LINE_CAP_ROUND, "CAIRO_LINE_CAP_ROUND"); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); + check(cairo_get_fill_rule(cr) == CAIRO_FILL_RULE_EVEN_ODD, "CAIRO_FILL_RULE_EVEN_ODD"); + + // ── the matrix, which is a struct type rather than an opaque handle ── + cairo_matrix_t m; + cairo_matrix_init_identity(&m); + cairo_matrix_translate(&m, 3.0, 4.0); + check(m.x0 == 3.0 && m.y0 == 4.0, "cairo_matrix_t is a usable value type"); + + // ── the FreeType font backend, which is a default feature ──────────── + // ⚠️ The point is the ENUMERATOR: CAIRO_FONT_TYPE_FT is what a consumer + // compares against, and it was among the 192 that never made it out. + cairo_font_face_t *ff = cairo_get_font_face(cr); + std::printf(" default font backend: %d (CAIRO_FONT_TYPE_FT=%d)\n", + (int) cairo_font_face_get_type(ff), (int) CAIRO_FONT_TYPE_FT); + check(cairo_font_face_status(ff) == CAIRO_STATUS_SUCCESS, + "the default font face is live"); + + cairo_destroy(cr); + cairo_surface_destroy(surf); + + std::printf("\n%s\n", failures == 0 ? "all ok" : "FAILURES"); + return failures == 0 ? 0 : 1; +} + +#else + +#include +int main() { std::printf("cairo: Linux only here.\n"); return 0; } + +#endif diff --git a/tests/examples/libdisplay-info/tests/libdisplay-info.cpp b/tests/examples/libdisplay-info/tests/libdisplay-info.cpp index d7022a0..dd4443e 100644 --- a/tests/examples/libdisplay-info/tests/libdisplay-info.cpp +++ b/tests/examples/libdisplay-info/tests/libdisplay-info.cpp @@ -33,10 +33,10 @@ // that is right there in the library. This file did exactly that before the // module existed. // -// `import freedesktop.displayinfo;` does the wrapping once, inside the module +// `import displayinfo;` does the wrapping once, inside the module // purview. The module is GENERATED by the package's build.mcpp from the public // headers — 206 names — so it cannot fall behind them. -import freedesktop.displayinfo; +import displayinfo; #include #include @@ -129,6 +129,17 @@ int main() std::free(make); std::free(model); di_info_destroy(info); + // ⭐ AN ENUMERATOR, WHICH THIS EXAMPLE NEVER ASKED FOR BEFORE. + // + // The module exported 206 names and NOT ONE of the 295 `DI_*` enumerators. + // Nothing noticed, because no test named one — and naming one on GCC alone + // would not have noticed either: exporting the enum makes its enumerators + // visible there and clang rejects the same file. So this is the check, and + // it is a compile error if the export list collapses again. + const enum di_edid_cvt_aspect_ratio ar = DI_EDID_CVT_ASPECT_RATIO_16_9; + check(ar != DI_EDID_CVT_ASPECT_RATIO_4_3, + "an enumerator comes through the module by name"); + check(true, "di_info_destroy"); std::printf("\n%d check(s) failed\n", failures); From 3c3451ec3ecf7cc50c5419495fe8028ed511810a Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 31 Aug 2026 22:16:45 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E6=AC=A1=E5=BA=8F=20=E2=80=94?= =?UTF-8?q?=E2=80=94=20cairo=20=E5=85=88=E8=90=BD=E5=9C=B0,pango=20?= =?UTF-8?q?=E9=9A=8F=E5=90=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gnome.pangocairo 转出 cairo 的模块,所以改名要么两个包同时到达消费者,要么中间 有一段是坏的。一个 PR 做不到,原因是硬的:一个成员只能声明一个项目级 index repo (xlings#374 / mcpp#238),于是 tests/examples/pangocairo 不可能同时用本 PR 的 gnome.pango* 描述符和本 PR 的 freedesktop.cairo 描述符。 所以本 PR 只做 cairo / libdisplay-info;pango 保持 import freedesktop.cairo 和 cairo.h 变通不动(tarball 回滚重发,sha 19507c67…,CN 容器 tag 1.56.1-6), 合并之后再切。 我一度反过来做了 —— 代码是对的,次序是错的。理由已写进 gnome.pangocairo 的描述符 和设计文档 §23.5。 五个示例全绿。 --- ...26-08-30-graphics-stack-coverage-design.md | 28 ++++++++++++++++++- pkgs/p/gnome.pango.lua | 6 ++-- pkgs/p/gnome.pangocairo.lua | 24 ++++++++++------ pkgs/p/gnome.pangoft2.lua | 6 ++-- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/.agents/docs/2026-08-30-graphics-stack-coverage-design.md b/.agents/docs/2026-08-30-graphics-stack-coverage-design.md index 14e25f5..56ce83e 100644 --- a/.agents/docs/2026-08-30-graphics-stack-coverage-design.md +++ b/.agents/docs/2026-08-30-graphics-stack-coverage-design.md @@ -2235,7 +2235,33 @@ typedef struct _cairo cairo_t; /* cairo.h:135 */ 新资产** —— 本次验证时就撞上了(`failed to read compiled module`),清掉 store 条目才对。这是「包版本与上游对齐」这条约定的固定代价,值得写在这里。 -### 23.5 结果 +### 23.5 ⭐ 改名的次序是被**索引机制**逼出来的,不是偏好 + +`gnome.pangocairo` 转出 cairo 的模块。所以改名要么两个包同时到达消费者,要么 +中间有一段是坏的 —— 而**一个 PR 做不到**,原因是硬的: + +``` +error: xlings install_packages failed … with 2 index repos configured + [freedesktop -> …, gnome -> …]; ≥2 project-level index repos is a known + xlings resolution gap +``` + +**一个成员只能声明一个项目级 index repo**(xlings#374 / mcpp#238,今天仍是硬 +错误)。于是 `tests/examples/pangocairo` 不可能同时用**本 PR 的 `gnome.pango*` +描述符**和**本 PR 的 `freedesktop.cairo` 描述符** —— 它只能重定向一个 namespace, +另一个必然来自**已发布的**索引。 + +所以次序只能是: + +1. **本 PR**:cairo / libdisplay-info 改名 + 补齐。pango **不动**,继续 + `import freedesktop.cairo;`,`cairo.h` 变通**保留**。 +2. **合并之后**:pango 改成 `import cairo;` 并删掉变通 —— 那时新 cairo 已在 + 已发布索引里,示例才有得测。 + +⚠️ 我一度反过来做了(先把 pango 的变通删掉),两边都红。**代码是对的,次序是 +错的**;已经回滚并把这段理由写进了 `gnome.pangocairo` 的描述符,免得下次再犯。 + +### 23.6 结果 | | | |---|---| diff --git a/pkgs/p/gnome.pango.lua b/pkgs/p/gnome.pango.lua index be83a69..7f66672 100644 --- a/pkgs/p/gnome.pango.lua +++ b/pkgs/p/gnome.pango.lua @@ -84,14 +84,14 @@ package = { ["1.56.1"] = { url = { GLOBAL = "https://github.com/mcpplibs/pango/archive/refs/tags/1.56.1.tar.gz", - -- ⚠️ The container tag is `1.56.1-5`, not `1.56.1`. gitcode + -- ⚠️ The container tag is `1.56.1-6`, not `1.56.1`. gitcode -- refuses to REPLACE an asset of the same name in an -- existing release, so each corrected tarball needs a new -- container tag while the PACKAGE version stays upstream's. -- Verified byte-identical to the GLOBAL tag archive. - CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-5/pango-1.56.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-6/pango-1.56.1.tar.gz", }, - sha256 = "0882a260346cbdb6b553177269a8acc083a2985bbe929b458406af98e90a03ad", + sha256 = "19507c6712304750a0e9cc282135dda1919a1b89c0c4ce61e22411df0532a980", }, }, }, diff --git a/pkgs/p/gnome.pangocairo.lua b/pkgs/p/gnome.pangocairo.lua index c3190f5..5f4d576 100644 --- a/pkgs/p/gnome.pangocairo.lua +++ b/pkgs/p/gnome.pangocairo.lua @@ -22,7 +22,7 @@ -- then segfaulted. The fork's test now reads the font map's FONT TYPE and -- requires `CAIRO_FONT_TYPE_FT`, which catches it immediately. -- --- ✅ AND THIS PACKAGE IS WHERE cairo's MODULE GAP WAS FOUND. On the +-- ⭐ AND THIS PACKAGE IS WHERE cairo's MODULE GAP WAS FOUND. On the -- `1.18.2-mcpp2` asset `freedesktop.cairo` exported 470 names, ZERO -- enumerators and no `cairo_t`. The index's own cairo example did not notice, -- because it wrote BOTH `#include ` and `import freedesktop.cairo;` @@ -31,11 +31,19 @@ -- -- It surfaced here because pangocairo CANNOT mix the two routes -- (`pangocairo.h` reaches glib and therefore ), so it had to ask the --- module for `cairo_t` and got nothing. This package carried a workaround for --- one release — scanning cairo.h itself — and that workaround is GONE: --- `1.18.2-mcpp3` exports 697 names and the plain re-export is enough. +-- module for `cairo_t` and got nothing. This package therefore scans cairo.h +-- itself. -- --- The module it re-exports is now `cairo`, not `freedesktop.cairo`.-- +-- ⚠️ THAT WORKAROUND STAYS UNTIL cairo's FIX IS PUBLISHED, not merely written. +-- It is fixed in `1.18.2-mcpp3` (697 names), but this package resolves cairo +-- from the PUBLISHED index — and the change cannot even be TESTED in one index +-- PR, because a member may declare only ONE project-level index repo: +-- +-- error: ≥2 project-level index repos is a known xlings resolution gap +-- +-- so an example cannot use a local `gnome.*` descriptor and a local +-- `freedesktop.*` descriptor at the same time. cairo lands first; this +-- follows.-- -- ───────────────────────────────────────────────────────────────────────── -- ⭐ TWO WAYS TO CONSUME IT, AND YOU PICK ONE -- @@ -98,14 +106,14 @@ package = { ["1.56.1"] = { url = { GLOBAL = "https://github.com/mcpplibs/pango/archive/refs/tags/1.56.1.tar.gz", - -- ⚠️ The container tag is `1.56.1-5`, not `1.56.1`. gitcode + -- ⚠️ The container tag is `1.56.1-6`, not `1.56.1`. gitcode -- refuses to REPLACE an asset of the same name in an -- existing release, so each corrected tarball needs a new -- container tag while the PACKAGE version stays upstream's. -- Verified byte-identical to the GLOBAL tag archive. - CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-5/pango-1.56.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-6/pango-1.56.1.tar.gz", }, - sha256 = "0882a260346cbdb6b553177269a8acc083a2985bbe929b458406af98e90a03ad", + sha256 = "19507c6712304750a0e9cc282135dda1919a1b89c0c4ce61e22411df0532a980", }, }, }, diff --git a/pkgs/p/gnome.pangoft2.lua b/pkgs/p/gnome.pangoft2.lua index 876fddf..d984db7 100644 --- a/pkgs/p/gnome.pangoft2.lua +++ b/pkgs/p/gnome.pangoft2.lua @@ -68,14 +68,14 @@ package = { ["1.56.1"] = { url = { GLOBAL = "https://github.com/mcpplibs/pango/archive/refs/tags/1.56.1.tar.gz", - -- ⚠️ The container tag is `1.56.1-5`, not `1.56.1`. gitcode + -- ⚠️ The container tag is `1.56.1-6`, not `1.56.1`. gitcode -- refuses to REPLACE an asset of the same name in an -- existing release, so each corrected tarball needs a new -- container tag while the PACKAGE version stays upstream's. -- Verified byte-identical to the GLOBAL tag archive. - CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-5/pango-1.56.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/pango/releases/download/1.56.1-6/pango-1.56.1.tar.gz", }, - sha256 = "0882a260346cbdb6b553177269a8acc083a2985bbe929b458406af98e90a03ad", + sha256 = "19507c6712304750a0e9cc282135dda1919a1b89c0c4ce61e22411df0532a980", }, }, },