Skip to content

Commit cc94a86

Browse files
committed
freedesktop.egl 1.7.0:libglvnd 源码构建 + C++23 模块层,替掉 compat.egl 绑定
compat.egl 绑的是 xim:libglvnd,而它自己的注释就写着这是错的: libglvnd **is** a separable project, so by the criterion this should be a source build; it is still a binding for effort alone 判据里没有「工作量」这一项。工作量的部分是生成的 dispatch(约 1000 行 Python 处理 2.7MB gl.xml)加上整个 libGLdispatch.so —— mcpplibs/libglvnd 这个 fork 把生成物签进仓、两个库都建出来,于是构建期不跑 Python、不跑第二套构建系统, mcpp build 就是全部工具链。upstream/ 是 freedesktop 发布物原样,fork 加的东西 全在 mcpp/ 下,CI 两边都 diff。 一个索引条目,两个库。libGLdispatch.so.0 由同 workspace 的兄弟成员构建,用 path 依赖引入而不是走索引:「进程里只有一个 dispatch 点」是 GLVND 存在的全部 理由,两个索引条目会让消费者把两个都写上、解析出两个包实例,而 soname 复用 只映射其中一个、另一个被静默丢弃。 按架构选文件放在 build.mcpp 里。GLdispatch 的 entry stub 既分架构又分线程存储 模型,而且和 libffi 的不同,它们自身没有任何门控 —— 在 sources 里写死 x86_64 会让包只能在 x86_64 上用而且哪儿都不写明;build.mcpp 用 mcpp::target_arch() 做的正是上游 gl_dispatch_type 的那个选择。 编译进去的 vendor 搜索路径刻意留空。上游烤进 <prefix>/share/glvnd/egl_vendor.d, 重定位之后那就是 host 的目录。留空能让「生态没声明」表现为「找不到 vendor」, 而不是悄悄把宿主驱动装进沙箱进程 —— 与 compat.libgbm 对 GBM_BACKENDS_PATH 的 立场一致,真正的路径由 xim:mesa 通过 discovery 层声明(xim-pkgindex#713)。 测试成员的每条断言在零 vendor 的机器上都成立,这是被 runner 教的:原来那条 「扩展串里有 EGL_EXT_」在我这台过、在干净 runner 挂,因为上游在 vendor 列表 为空时直接返回空串(libegl.c:928)—— 那条断言测的是机器有没有 GPU 驱动。 换成两条更好的:EGL_VERSION 在任何 vendor 之前由 libglvnd 自己答成字面量 "1.5 libglvnd"(既是活性检查也是身份检查),以及 dladdr 确认加载的确实是本包 构建的 libEGL.so.1 而不是 xim:libglvnd payload 的同 soname 副本 —— 后者在装了 图形栈的机器上是真实可达的,和 compat.libdrm 测试里那条同理。
1 parent 83ddd4a commit cc94a86

6 files changed

Lines changed: 232 additions & 296 deletions

File tree

docs/descriptor-examples.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ in the [root README](../README.md#reference-examples).
3232
| C++ application framework compat (dependencies reuse packages already in the index) | [`compat.eui-neo`](../pkgs/e/compat.eui-neo.lua) (upstream's `3rd/` ships 8 vendored dependencies; none of them is compiled here — all are redirected to the same-version `compat.*` packages in this index) |
3333
| Mutually exclusive backends (one of several inside one package) | [`compat.eui-neo`](../pkgs/e/compat.eui-neo.lua): `vulkan` / `sdl2` each **replace** the default OpenGL / GLFW, and the default backend is expressed by *naming no feature at all* — there is no `opengl`/`glfw` feature. A `default` feature cannot express exclusivity: its own `defines`/`sources`/`deps` have no effect whatsoever, while its `implies` always applies and cannot be overridden by a named feature (which is, conversely, exactly the solution for the "always-on interface define" row below). The workable answer is to read the `-DMCPP_FEATURE_<NAME>` mcpp passes anyway and decide up front in a force-included header. Note also that `cflags` only reaches C TUs — C++ needs `cxxflags`, so a backend define written only into `cflags` never reaches any `.cpp` |
3434
| Host runtime adaptation (drivers are not vendored) | [`compat.glx-runtime`](../pkgs/c/compat.glx-runtime.lua) · [`compat.vulkan-runtime`](../pkgs/c/compat.vulkan-runtime.lua) (mcpp binaries run against a bundled glibc, so a bare-soname `dlopen` never reaches the host drivers; a symlink farm plus `runtime.library_dirs` bridges that. The farm holds only versioned sonames, so nothing there can shadow an index package. Note that `runtime.library_dirs` renders as `-Wl,-rpath` and **not** as `-L` — the `-L` key is `runtime.link_library_dirs`, which these two do not need because nothing links against their farms; see the row below for one that does) |
35-
| Ecosystem-stack binding (zero host) | [`compat.libgbm`](../pkgs/c/compat.libgbm.lua) (Mesa's GBM — buffer allocation out of a DRM device. The row above reaches the HOST; this one reaches the ECOSYSTEM and nothing else. **Why not a source build**: libgbm is a build target *inside* Mesa, not a separable project — `src/gbm/meson.build` is `link_with: [libloader]`, and `libloader` wants `idep_mesautil`, ~120 TUs of Mesa's internal util library for exactly one function, `loader_open_driver_lib`; and it is a **loader**, whose `gbm_create_device()` dlopens Mesa's own `dri_gbm.so`, so built apart from Mesa it would have nothing to load. **The test on this row is separability, NOT whether the payload also carries a copy** — `compat.libdrm`, from the same stack, passes it and is a source build (see the row below); the two coexist, measured. **What it deliberately does NOT do**: set the backend search path. `GBM_BACKENDS_PATH` is Mesa's own mechanism and the ENVIRONMENT's job — `xim:mesa` declares it through the graphics discovery layer (openxlings/xim-pkgindex#713), so this package sets nothing. **Two directory keys, not one**: `library_dirs` renders as `-Wl,-rpath` and `link_library_dirs` as `-L`, so a package that is *linked against* needs both — with the first alone the build still dies at `ld: cannot find -lgbm`) · [`compat.egl`](../pkgs/c/compat.egl.lua) (the EGL dispatch, provided by libglvnd rather than Mesa. libglvnd **is** a separable project, so by the criterion this should be a source build; it is still a binding for effort alone: `libEGL.so` also needs `egl_dispatch_stubs` (Python-generated `g_egldispatchstubs.c/.h`), `winsys_dispatch` and the whole of `libGLdispatch.so`, plus the vendor discovery path `__EGL_VENDOR_LIBRARY_DIRS` — a fork of the same size as wayland's. Ships **only** `EGL/` out of a payload that also carries `GL/`, `GLES2/`, `KHR/`: a third provider of `GL/` would make compat.glx-headers' documented two-provider race a three-way one, and `KHR/` comes from the index's existing `compat.khrplatform` — load-bearing, since `eglplatform.h` opens with `#include <KHR/khrplatform.h>`. X11 is deliberately NOT a dependency: that include is `USE_X11`-gated) |
36-
| Source build that needs a real project (Form A fork) | [`freedesktop.wayland`](../pkgs/f/freedesktop.wayland.lua) · [`freedesktop.wayland-server`](../pkgs/f/freedesktop.wayland-server.lua) · [`freedesktop.wayland-scanner`](../pkgs/f/freedesktop.wayland-scanner.lua) · [`freedesktop.wayland-util`](../pkgs/f/freedesktop.wayland-util.lua) (wayland 1.26.0, four entries out of one tarball — [mcpplibs/wayland](https://github.com/mcpplibs/wayland), a fork that patches no upstream file. **Why a fork and not an inline descriptor**: wayland is mostly GENERATED — `protocol/wayland.xml` describes every interface and wayland-scanner emits ~13,000 lines from it — and the generator is a C program in the same tree, so it must be COMPILED before it can run. An inline descriptor has no build step, and `install()` cannot do it either: mcpp compiles a package's sources at CONSUMER-BUILD time, so no package binary exists while another package installs. `build.mcpp` is the mechanism, and it only exists for a real mcpp project. **Why four packages**: `libwayland-client.so.0` and `libwayland-server.so.0` are distinct SONAMEs that Mesa's libEGL_mesa needs BOTH of, and mcpp links every library target against ALL of a package's sources — measured: a feature-gated second target still receives the feature's objects — so one package cannot emit two libraries with disjoint contents. mcpp's own diagnostic names the remedy: "split into a workspace member". **The module layer ships with the library, not beside it**: each package carries its C library AND its `.cppm`, so there is one package per library rather than a C one and a module one. The wrappers add no API — every exported name is upstream's, and the lists are generated from the public headers so a version bump cannot silently drop one. **Macros are the one thing that cannot cross**: `export` names entities and a macro is not one, so `wayland-util` maps each to what it actually is — `WL_MARSHAL_FLAG_DESTROY` to a constexpr, `wl_container_of` to a function template, the six `*_for_each` loops to ranges — and its test instantiates all of them including the `_safe` removal guarantee) |
35+
| Ecosystem-stack binding (zero host) | [`compat.libgbm`](../pkgs/c/compat.libgbm.lua) (Mesa's GBM — buffer allocation out of a DRM device. The row above reaches the HOST; this one reaches the ECOSYSTEM and nothing else. **Why not a source build**: libgbm is a build target *inside* Mesa, not a separable project — `src/gbm/meson.build` is `link_with: [libloader]`, and `libloader` wants `idep_mesautil`, ~120 TUs of Mesa's internal util library for exactly one function, `loader_open_driver_lib`; and it is a **loader**, whose `gbm_create_device()` dlopens Mesa's own `dri_gbm.so`, so built apart from Mesa it would have nothing to load. **The test on this row is separability, NOT whether the payload also carries a copy** — `compat.libdrm`, from the same stack, passes it and is a source build (see the row below); the two coexist, measured. **What it deliberately does NOT do**: set the backend search path. `GBM_BACKENDS_PATH` is Mesa's own mechanism and the ENVIRONMENT's job — `xim:mesa` declares it through the graphics discovery layer (openxlings/xim-pkgindex#713), so this package sets nothing. **Two directory keys, not one**: `library_dirs` renders as `-Wl,-rpath` and `link_library_dirs` as `-L`, so a package that is *linked against* needs both — with the first alone the build still dies at `ld: cannot find -lgbm`) (EGL used to sit on this row as `compat.egl`, bound to `xim:libglvnd`. Its own comment recorded why that was wrong — "libglvnd **is** a separable project, so by the criterion this should be a source build; it is still a binding for effort alone" — and effort is not the criterion, so it moved to the row below as [`freedesktop.egl`](../pkgs/f/freedesktop.egl.lua)) |
36+
| Source build that needs a real project (Form A fork) | [`freedesktop.wayland`](../pkgs/f/freedesktop.wayland.lua) · [`freedesktop.wayland-server`](../pkgs/f/freedesktop.wayland-server.lua) · [`freedesktop.wayland-scanner`](../pkgs/f/freedesktop.wayland-scanner.lua) · [`freedesktop.wayland-util`](../pkgs/f/freedesktop.wayland-util.lua) (wayland 1.26.0, four entries out of one tarball — [mcpplibs/wayland](https://github.com/mcpplibs/wayland), a fork that patches no upstream file. **Why a fork and not an inline descriptor**: wayland is mostly GENERATED — `protocol/wayland.xml` describes every interface and wayland-scanner emits ~13,000 lines from it — and the generator is a C program in the same tree, so it must be COMPILED before it can run. An inline descriptor has no build step, and `install()` cannot do it either: mcpp compiles a package's sources at CONSUMER-BUILD time, so no package binary exists while another package installs. `build.mcpp` is the mechanism, and it only exists for a real mcpp project. **Why four packages**: `libwayland-client.so.0` and `libwayland-server.so.0` are distinct SONAMEs that Mesa's libEGL_mesa needs BOTH of, and mcpp links every library target against ALL of a package's sources — measured: a feature-gated second target still receives the feature's objects — so one package cannot emit two libraries with disjoint contents. mcpp's own diagnostic names the remedy: "split into a workspace member". **The module layer ships with the library, not beside it**: each package carries its C library AND its `.cppm`, so there is one package per library rather than a C one and a module one. The wrappers add no API — every exported name is upstream's, and the lists are generated from the public headers so a version bump cannot silently drop one. **Macros are the one thing that cannot cross**: `export` names entities and a macro is not one, so `wayland-util` maps each to what it actually is — `WL_MARSHAL_FLAG_DESTROY` to a constexpr, `wl_container_of` to a function template, the six `*_for_each` loops to ranges — and its test instantiates all of them including the `_safe` removal guarantee) · [`freedesktop.egl`](../pkgs/f/freedesktop.egl.lua) (libglvnd 1.7.0 — [mcpplibs/libglvnd](https://github.com/mcpplibs/libglvnd), and the entry that replaced the `compat.egl` binding on the row above. **The generated code is checked in rather than generated**: upstream drives ~1000 lines of Python over a 2.7 MB `gl.xml` to emit the dispatch tables, and that output is deterministic from a pinned registry — so it lives in `mcpp/generated/` and NO generator runs at build time, with CI regenerating and diffing it instead. `mcpp build` is the whole toolchain. **The per-architecture choice lives in `build.mcpp`, not in the manifest**: GLdispatch's entry stubs are per-arch *and* per-thread-storage-model, and unlike libffi's they carry no guards of their own, so hard-coding x86_64 in `sources` would make the package x86_64-only and say so nowhere; `build.mcpp` makes upstream's own `gl_dispatch_type` choice from `mcpp::target_arch()`. **One index entry, two libraries**: `libGLdispatch.so.0` is a sibling workspace member reached by a PATH dependency — being the one dispatch point in a process is GLVND's whole purpose, and a second index entry would let a consumer name both and resolve two instances, with soname reuse mapping one and silently discarding the other. **The compiled-in vendor path is deliberately EMPTY**: upstream bakes in `<prefix>/share/glvnd/egl_vendor.d`, which after relocation means the HOST's directory, so an empty fallback makes a missing ecosystem declaration surface as "no vendor found" rather than as silently loading the host's driver — the same stance compat.libgbm takes with `GBM_BACKENDS_PATH`) |
3737
| Source-built library sharing a soname with the payload | [`compat.libdrm`](../pkgs/c/compat.libdrm.lua) (libdrm 2.4.134 — five TUs plus an inlined `generated_static_table_fourcc.h`, and zero dependencies. It carries the same `libdrm.so.2` the ecosystem's Mesa payload does, and that is **not** a reason to bind: a DT_NEEDED soname already in the link map is REUSED, ld.so never searches again, so Mesa's `libgbm.so.1` binds to the copy the consumer linked — measured: one mapping in the process, and Mesa's GBM allocated a buffer through it. It holds only for `kind = "shared"` with the right soname: as the default `kind = "lib"` there is no `.so` to reuse, the payload's loads for Mesa, the consumer keeps its own merged one, and libdrm's file-static state (`drmHashTable`, `nr_fds`, `connection`) exists twice over one set of fds. The test asserts with `dladdr` that the loaded library is this package's. **Two include roots**: the public headers sit at the source root and the uapi headers under `include/drm/`, and `xf86drm.h` line 40 is a bare `#include <drm.h>`) · [`compat.libffi`](../pkgs/c/compat.libffi.lua) (3.4.8. The x86 assembly **gates itself** — `ffi.c`/`ffiw64.c`/`unix64.S`/`sysv.S`/`win64.S` each open with `#ifdef __x86_64__` or `__i386__` — so all of them are listed and the preprocessor selects. `fficonfig.h` is configure's record of what it probed and cannot be derived, so it is inlined; `ffi.h` CAN be derived, so install() performs configure's four substitutions on `ffi.h.in` and stays honest across a version bump) · [`compat.expat`](../pkgs/c/compat.expat.lua) (2.7.1, three TUs — `xmltok_impl.c` and `xmltok_ns.c` are `#include`d BY `xmltok.c`, five times between them, so listing them would produce duplicate symbols) |
3838
| Always-on interface define | `CURL_STATICLIB` in [`compat.curl`](../pkgs/c/compat.curl.lua): `cflags` is always on but package-private, while a feature's `defines` reaches consumers yet has to be named — `default = { implies = … }` applies unconditionally and happens to give both |
3939
| Multiple majors in one package (shape switches with the version) | [`compat.catch2`](../pkgs/c/compat.catch2.lua) (3.x compiles `src/catch2/` into a static library; 2.x goes header-only through `single_include/`) |

0 commit comments

Comments
 (0)