Skip to content

Commit 2f4458d

Browse files
committed
docs: 模块命名规则写进两份 descriptor-examples,并修掉描述符首行的旧模块名
规则:模块名跟拥有**接口**的组织。索引早先的惯例是把 namespace 整个丢掉 (chriskohlhoff.asio → import asio),这一条是把那个惯例说具体 —— 模块名全局且长期, 包名不是,所以它该说清楚谁拥有这份接口。EGL 那一行顺带解释了包名与模块名为什么故意 不一致。
1 parent cf6023b commit 2f4458d

4 files changed

Lines changed: 4 additions & 4 deletions

File tree

‎docs/descriptor-examples.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ in the [root README](../README.md#reference-examples).
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) |
3535
| 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`) |
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. **A module is named for the organization that owns the INTERFACE**, which is why these are `freedesktop.wayland.{client,server,util}`: freedesktop owns the wayland protocol. The index's older convention was to drop the namespace entirely (`chriskohlhoff.asio` → `import asio`, `compat.opencv` → `import opencv.cv`); this is that rule made specific, because a module name is global and permanent in a way a package name is not. `mcpplibs.openkal` paid for the lesson — its 0.1.0 was withdrawn rather than kept because it "placed the module a consumer imports under the control of the implementation, which contradicts what the specification is for". **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()`. **The module is `khronos.egl`, not `freedesktop.egl`**, and the package/module mismatch is the point: freedesktop ships this code, but EGL is a *Khronos* specification with several implementations. Naming the module after the implementation would force every consumer to edit its imports the day a different libEGL is used — defeating a wrapper whose whole promise is that it changes nothing but the include line. It also makes two EGL providers in one build a hard conflict rather than silent coexistence, which is what GLVND wants anyway. **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)