Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/descriptor-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ in the [root README](../README.md#reference-examples).
| 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) |
| 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` |
| 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) |
| 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, and the distinction is the whole design. **Why not a source build**: libgbm is a build target *inside* Mesa, not a project — `src/gbm/meson.build` is `link_with: [libloader]`, and `libloader` wants `idep_mesautil`, the whole of Mesa's internal util library (~120 TUs plus Python-generated tables) for exactly one function, `loader_open_driver_lib`. Building it would make this index re-import libdrm + expat + xcb + a Mesa-util carve-out to duplicate what `xim:mesa` has already resolved hermetically. Contrast `compat.vulkan`, which *does* build the Khronos loader from source — Khronos releases that as a standalone project, Mesa releases no such thing for GBM. **Zero host, with no escape hatch**: unlike its two neighbours it has no `/usr/lib*` path and no `MCPP_HOST_*` override, because host libgbm is a leak the ecosystem already closed — `xim:nvidia-gl-host-link` names it directly ("the table … was missing libm, libdrm, **libgbm**, libgcc_s … all of which were therefore coming from the HOST, silently, which is the leak this package exists to close"). NVIDIA's own GBM backend, if ever needed, belongs in that host-link layer rather than here. The measured surface is 1 ecosystem package (`xim:mesa`, not `xim:graphics`'s twenty-two), zero index deps, and zero transitive burden — libgbm.so.1's own RUNPATH resolves entirely inside `xim-x-*`. **What it deliberately does NOT do**: set the backend search path. libgbm is a loader — `gbm_create_device()` dlopens `<path>/<driver>_gbm.so`, and Mesa's compiled-in `/usr/lib/gbm` is right on a distro and wrong the moment the payload is relocated. The mechanism to fix that is Mesa's own (`GBM_BACKENDS_PATH`) and the job belongs to the ENVIRONMENT, which is where every other relocated stack puts it — Valve's pressure-vessel answers the identical breakage with `GBM_BACKENDS_PATH=/run/host/usr/lib64/gbm` (steam-runtime#797), Nix and Conda set it at activation. In this ecosystem `xim:mesa` now declares it through the graphics discovery layer (openxlings/xim-pkgindex#713), so this package sets nothing, generates no TU and ships no header of its own. It briefly did carry a constructor that set the variable itself; that was a workaround for the missing declaration, and deleting it took the descriptor from 598 lines to 303. **Two directory keys, not one**: `library_dirs` renders as `-Wl,-rpath` and `link_library_dirs` as `-L`, so a package that is *linked against* (unlike glx-runtime/vulkan-runtime, whose farms are only dlopen'd) needs both — with `library_dirs` alone the farm is complete, the rpath correct, and the build still dies at `ld: cannot find -lgbm`. It ships **two** test binaries: `stock_usage.cpp` includes stock `<gbm.h>` and nothing else, which is the minimal consumer and the tripwire on the two things outside this repo the package now depends on — xim-pkgindex's DISCOVERY row and mcpp's subos-env injection · [`compat.libdrm`](../pkgs/c/compat.libdrm.lua) (the layer under GBM — `drmModeAddFB2`/`drmModeSetCrtc` turn an allocated buffer into a scanout. Passes the separable-unit test that libgbm fails — libdrm is an independent freedesktop project and Conan carries a real recipe — but is still a binding for the second reason: `xim:libdrm` exists, Mesa's own payload has DT_NEEDED on it, and two `libdrm.so.2` in one process means two DRM handle tables. **Two include roots**, which is the thing that bites: the public headers sit at the include root and the uapi headers they include sit under `libdrm/`, and `xf86drm.h` line 40 is a bare `#include <drm.h>` — expose one root and nothing compiles at all) · [`compat.egl`](../pkgs/c/compat.egl.lua) (what makes libgbm useful for RENDERING rather than only allocation: `eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, gbm_device, NULL)`. Provider is libglvnd, not Mesa — EGL is a spec and the thing you link is a vendor-neutral dispatch library that must be the only one in the process. 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` instead — load-bearing, since `eglplatform.h` opens with `#include <KHR/khrplatform.h>`. X11 is deliberately NOT a dependency: that include is `USE_X11`-gated, and forcing Xorg on headless GBM users would be exactly wrong) · [`compat.wayland`](../pkgs/c/compat.wayland.lua) (client, server, cursor and EGL shim all harvested, but only `-lwayland-client` on `ldflags`. A dependency's ldflags reach the consumer's link line with no way to opt out, so forcing the server library on every client would be unfixable downstream; a compositor author adds `-lwayland-server` themselves and it resolves out of the farm. The test member does exactly that, so the documented escape hatch has a regression guarding it. `wayland-scanner` and the protocol XML are NOT here — that is a code generator plus a data package, the `compat.protobuf` `protoc` shape, and a separate package))
| 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) · [`compat.wayland`](../pkgs/c/compat.wayland.lua) (client, server, cursor and EGL shim harvested from `xim:wayland`, with only `-lwayland-client` on `ldflags` — a dependency's ldflags reach the consumer's link line with no way to opt out, so forcing the server library on every client would be unfixable downstream. Wayland IS a separable project, so this row is where it does not belong: it moves to a source build in mcpplibs/wayland, which needs `build.mcpp` because wayland-scanner has to be compiled before it can generate the ~13,000 lines the two libraries are mostly made of) |
| 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) |
| 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 |
| 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/`) |
| External build system (`install()` builds from source) | [`compat.openblas`](../pkgs/c/compat.openblas.lua) (Make) · [`compat.openssl`](../pkgs/c/compat.openssl.lua) (Perl Configure + Make, static libssl/libcrypto) |
Expand Down
38 changes: 35 additions & 3 deletions docs/package-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,44 @@ combined as needed.
| **A. C-source compat** | plain C or a handful of sources; the user writes `#include <foo.h>` | `pkgs/c/compat.cjson.lua`, `compat.zlib.lua`, `compat.gtest.lua` | `sources` and `c_standard` |
| **B. header-only** | headers only, nothing to compile | `pkgs/c/compat.eigen.lua`, `compat.opengl.lua`, `compat.khrplatform.lua` | `include_dirs` and an anchor source |
| **C. C++23 module** | exposes `import x.y;` | `pkgs/n/nlohmann.json.lua` | `modules` plus `generated_files` or a source `.cppm` |
| **D. External Form-A module repo** | upstream ships its own mcpp descriptor in a separate repository | `pkgs/i/imgui.lua`, `pkgs/m/mcpplibs.*` | `mcpp = "<repo path>"` (Form A) |
| **D. External Form-A module repo** | upstream ships its own mcpp descriptor in a separate repository — or the build needs something an inline descriptor cannot express (`build.mcpp`, a workspace, a code generator that must be compiled first) | `pkgs/i/imgui.lua`, `pkgs/m/mcpplibs.*`, `pkgs/g/grpc.lua` + `grpcgen.lua` + `grpc-plugin.lua` (three entries out of one fork) | `mcpp = "<repo path>"` (Form A) |
| **E. Whole-source direct build with a generated config** | upstream generates its config header through configure/CMake; here a snapshot of it lands in `generated_files` | `pkgs/c/compat.libpng.lua`, `compat.curl.lua`, `compat.sdl2.lua`, `compat.ffmpeg.lua` | `generated_files` + `include_dirs` |
| **F. Shared-library compat** | has to be the **only** copy of that `.so` in the process (third parties `dlopen` it) | the X11 family such as `pkgs/c/compat.x11.lua`, and `compat.vulkan.lua` (linux) | `targets = { kind = "shared", soname = … }` |
| **F. Shared-library compat** | has to be the **only** copy of that `.so` in the process — either because third parties `dlopen` it, or because the ecosystem payload links the same soname | the X11 family such as `pkgs/c/compat.x11.lua`, `compat.vulkan.lua`, `compat.libdrm.lua`, `compat.libffi.lua`, `compat.expat.lua` | `targets = { kind = "shared", soname = … }` |
| **G. Host runtime adaptation** | things that cannot be vendored, such as drivers — only a symlink farm plus metadata | `pkgs/c/compat.glx-runtime.lua`, `compat.vulkan-runtime.lua` | `runtime.library_dirs` / `capabilities` |
| **H. Host tool provider** | the upstream tarball also holds a **code generator** consumers run at build time | `pkgs/c/compat.protobuf.lua` (`protoc`) | a `targets` entry with `kind = "bin"` + `main`, plus `required_features` |
| **I. Ecosystem-stack binding** | the library is an internal build target of a project the **ecosystem already owns**, so vendoring it would fork that project | `pkgs/c/compat.libgbm.lua` (Mesa's GBM, via `xim:mesa`) | `xpm.<plat>.deps.runtime = { "xim:<pkg>" }` + a farm from `system.subos_sysrootdir()`, with `runtime.library_dirs` **and** `link_library_dirs` |
| **I. Ecosystem-stack binding** | the library is an internal build target of a project the **ecosystem already owns** and upstream ships no separable unit, so vendoring it would fork that project. NOT for libraries that merely coexist with a payload — see below | `pkgs/c/compat.libgbm.lua` (Mesa's GBM, via `xim:mesa`) | `xpm.<plat>.deps.runtime = { "xim:<pkg>" }` + a farm from `system.subos_sysrootdir()`, with `runtime.library_dirs` **and** `link_library_dirs` |

### Source build or binding: the criterion is separability, and only that

The index's default is to build from source. The only question is whether
upstream ships the thing as a **separable unit** — its own releases, buildable
without forking the project it lives in.

`compat.libgbm` fails that test and is shape I: GBM is a target inside Mesa
(`src/gbm/meson.build` is `link_with: [libloader]`, and `libloader` pulls
`idep_mesautil` — ~120 TUs of Mesa's internal util library for one function),
and it is a loader whose backends are Mesa's own. `compat.libdrm` PASSES it and
is shape F, even though the ecosystem's Mesa payload also carries a
`libdrm.so.2`.

**"The payload already has one" is not a reason to bind.** That was believed
here and is wrong, measured on mcpp 2026.8.29.1:

- Examined alone, Mesa's `libgbm.so.1` resolves `libdrm.so.2` through its own
ABSOLUTE RUNPATH into `xim-x-libdrm/<ver>/lib`.
- In a real consumer process that links `compat.libdrm`, the same `libgbm.so.1`
binds to the CONSUMER's copy instead, and exactly one `libdrm.so.2` is
mapped. Mesa's GBM then allocated a buffer through it.

A DT_NEEDED soname already present in the link map is REUSED — ld.so never
searches again, so it never consults the payload's RUNPATH. The consumer links
the library directly, so it is mapped first and everything else follows it.

This only holds for `kind = "shared"` with the canonical soname. Built as this
index's default `kind = "lib"` (objects merged into the consumer) there is no
`.so` to reuse: the payload's copy loads for Mesa and the consumer keeps its
own merged one, so the library's file-static state exists twice over one set of
handles. That is the failure the soname prevents, and why these packages set it.

For the complete sample index, see [Descriptor examples by shape](descriptor-examples.md).

Expand Down
Loading
Loading