Package foot and its Wayland/font chain, and the channel's first meson build
foot is a Wayland terminal emulator with a meson build, so this adds two things the channel did not have: a build system it had never driven, and the first packages whose dependency graph is deep enough to expose how the builder composes a build environment.
Fifteen new packages. The build tools -- python, meson, samurai, gperf -- are here because the seed has none of them, and CPython is unavoidable: foot's own meson.build runs python3 in three unconditional custom_targets, so no choice of build system removes it. The rest is foot's actual chain: libffi, expat, pixman, freetype, fontconfig, tllist, wayland, wayland-protocols, libxkbcommon and fcft, plus libpng and grim as a second Wayland client.
THREE PROPERTIES OF THIS CHANNEL WERE FOUND, not of any package in it, and each is documented at the site that meets it:
- The builder compiles everything with -O2, and zig cc enables -Werror=date-time when it optimizes, so any package expanding DATE or TIME cannot compile. core/python suppresses it AND ships a phase that asserts the toolchain still pins those macros, because a suppression whose justification is a toolchain property needs something that notices when the property stops holding.
- PKG_CONFIG_PATH is composed from DIRECT dependencies only, so a .pc file's Requires: chain is not satisfied transitively -- and the diagnostic names the wrong package ("checking for FREETYPE... no" when zlib is missing). fontconfig, fcft, foot and grim therefore declare grand-dependencies they never mention themselves. Each is genuinely on their static link line.
- Requires.private: bites a second time at LINK time, which -Dprefer_static fixes for the meson consumers.
byacc changes twice: it installs under both yacc and byacc, because libxkbcommon looks for the latter, and backtracking is re-enabled because libxkbcommon's grammar uses %destructor. Measured both ways with a control for the plain-grammar case the earlier decision protected; tmux is unaffected and in fact never runs the program at all.
samurai carries a one-line patch. This toolchain's libc returns NULL from realloc(NULL, 0) while returning non-NULL from malloc(0), and samurai treats that as fatal on every fresh build directory. That is a latent portability bug in third-party source, which is what a distribution patch is for -- distinct from the file case in the same module, where a tool was MISSING and the right answer was to supply the tool.
Deliberate scope reductions, recorded so they do not read as oversights: text shaping is off (drops harfbuzz and utf8proc, costs combining sequences), tllist and wayland-protocols are run-phase installs rather than meson builds, and xkeyboard-config is not needed because a Wayland client is handed its keymap as text.
channel.sgl | 29 ++++++++++++++--
src/core/packages/build-tools.sgl | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
src/core/packages/capture.sgl | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/datastructures.sgl | 83 ++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/ffi.sgl | 41 +++++++++++++++++++++++
src/core/packages/fonts.sgl | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/graphics.sgl | 83 ++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/input.sgl | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/python.sgl | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/terminal.sgl | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/core/packages/wayland.sgl | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/core/packages/xml.sgl | 55 ++++++++++++++++++++++++++++--
src/patches/samurai-realloc-zero-length.patch | 18 ++++++++++
13 files changed, 1517 insertions(+), 12 deletions(-)channel.sglmodified
(core packages xml) (core packages databases) (core packages parsing) (core packages editors)) (core packages editors) (core packages python) (core packages ffi) (core packages graphics) (core packages fonts) (core packages datastructures) (core packages wayland) (core packages input) (core packages capture))(channel stream: "core-stable" bindings: (list (channel-binding name: "core/binaryen" value: binaryen) (channel-binding name: "core/byacc" value: byacc) (channel-binding name: "core/gperf" value: gperf) (channel-binding name: "core/meson" value: meson) (channel-binding name: "core/samurai" value: samurai) (channel-binding name: "core/file" value: file) (channel-binding name: "core/hello" value: hello) (channel-binding name: "core/zlib" value: zlib) (channel-binding name: "core/musl" value: musl) (channel-binding name: "core/ncurses" value: ncurses) (channel-binding name: "core/libxml2" value: libxml2) (channel-binding name: "core/expat" value: expat) (channel-binding name: "core/libffi" value: libffi) (channel-binding name: "core/pixman" value: pixman) (channel-binding name: "core/libpng" value: libpng) (channel-binding name: "core/grim" value: grim) (channel-binding name: "core/freetype" value: freetype) (channel-binding name: "core/fontconfig" value: fontconfig) (channel-binding name: "core/fcft" value: fcft) (channel-binding name: "core/libxkbcommon" value: libxkbcommon) (channel-binding name: "core/tllist" value: tllist) (channel-binding name: "core/wayland" value: wayland) (channel-binding name: "core/wayland-protocols" value: wayland-protocols) (channel-binding name: "core/sqlite" value: sqlite) (channel-binding name: "core/tree-sitter" value: tree-sitter) (channel-binding name: "core/tmux" value: tmux) (channel-binding name: "core/foot" value: foot) (channel-binding name: "core/vim" value: vim) (channel-binding name: "core/emacs" value: emacs))) (channel-binding name: "core/emacs" value: emacs) (channel-binding name: "core/python" value: python)))src/core/packages/build-tools.sglmodified
(core build-environments) (sigil env package) (sigil build external)) (export byacc file) (export byacc file gperf meson samurai) (begin ;; WHY THE CHANNEL NEEDS A YACC AT ALL, since nothing here has a grammar of ;; its own: tmux's configure REFUSES to proceed without one. (external-phase kind: 'configure args: (list "--prefix=${out}" ;; btyacc's backtracking extension is not ;; wanted; tmux uses plain yacc input "--disable-btyacc")) ;; BACKTRACKING IS ENABLED, AND THIS REVERSES ;; AN EARLIER DECISION IN THIS PACKAGE. It was ;; `--disable-btyacc` when tmux was the only ;; consumer, on the grounds that tmux uses plain ;; yacc input and does not want the extension. ;; ;; libxkbcommon does want it. Its grammar uses ;; `%destructor`, which is a btyacc extension, ;; and its own meson.build says so outright: ;; "we use some yacc extensions, which work with ;; either GNU bison (preferred) or byacc (with ;; backtracking enabled)". Without it: ;; ;; byacc: e - line 230 of "parser.y", syntax ;; error %destructor { FreeStmt(...); } ;; ;; MEASURED BOTH WAYS on this toolchain before ;; changing it, with a control for the case the ;; earlier decision was protecting: ;; ;; no btyacc + libxkbcommon grammar -> syntax error ;; btyacc + libxkbcommon grammar -> parser.c, 3393 lines ;; btyacc + a PLAIN grammar -> parser.c, 1177 lines ;; btyacc, installed name -> still bin/yacc ;; ;; So the extension is a superset and tmux loses ;; nothing. In fact tmux never runs this program ;; at all -- its release tarball ships ;; `cmd-parse.c` pre-generated and its configure ;; only does `AC_CHECK_PROG` -- so the flag was ;; never observable there. "--enable-btyacc")) (external-phase kind: 'make) (external-phase kind: 'install)))) (external-phase kind: 'install) ;; INSTALL THE PROGRAM UNDER BOTH NAMES IT IS KNOWN BY, AND ;; THIS IS DELIBERATE -- do not "tidy" it away. ;; ;; byacc's own makefile installs exactly one binary, named ;; `yacc` (makefile.in:5, `THIS = yacc`), because that is the ;; POSIX name tmux's configure looks for. But `byacc` is the ;; program's ACTUAL name, and it is the name a second consumer ;; asks for: libxkbcommon 1.7.0's meson.build:166 is ;; `find_program('byacc', required: false)`, reached only after ;; bison is not found. A `yacc` on PATH does not satisfy it. ;; ;; Debian and Fedora both ship both names. A package that ;; installs only the POSIX alias is the odd one out, and the ;; alternative -- patching libxkbcommon to look for `yacc` -- ;; would write this channel's deficiency into a third party's ;; source, which is the trade `file` in this same module ;; already refused to make. ;; ;; Choosing byacc over bison is what keeps GNU m4 out of this ;; channel: bison needs m4 to build, byacc needs nothing. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "test -x \"$d${out}/bin/yacc\"\n" "cp \"$d${out}/bin/yacc\" \"$d${out}/bin/byacc\"\n" "test -x \"$d${out}/bin/byacc\"\n" "echo BYACC-DUAL-NAME-INSTALLED\n")))))) ;; WHY A `file` PACKAGE EXISTS IN A CHANNEL THAT NEVER RUNS `file`. ;; "--disable-libseccomp" "--disable-landlock")) (external-phase kind: 'make) (external-phase kind: 'install)))))) (external-phase kind: 'install)))) ;; ;; SAMURAI CARRIES A PATCH, AND THE REASON IS NARROWER THAN "PATCHES ARE ;; FINE HERE". This module's `file` package exists because a TOOL WAS ;; MISSING, and the right fix for a missing tool is to supply the tool -- ;; a patch there would have written this channel's deficiency into a third ;; party's source. This is the other case: a LATENT PORTABILITY BUG in ;; third-party source, which is exactly what a distribution patch is for. ;; ;; WHAT BREAKS WITHOUT IT. Every fresh build directory: ;; ;; samu: reallocarray: No such file or directory ;; ;; deps.c:262 saves the old dependency-log entries with ;; `xreallocarray(NULL, entrieslen, sizeof(...))`, and on a build directory ;; with no `.ninja_deps` yet `entrieslen` is 0. util.c:61 forwards that to ;; `realloc(p, 0)`, and xreallocarray treats a null result as fatal. The ;; errno in the message is stale from an unrelated open(), which is why it ;; reads like a missing file. ;; ;; THIS TOOLCHAIN'S LIBC RETURNS NULL FROM realloc(NULL, 0) WHILE RETURNING ;; NON-NULL FROM malloc(0). Upstream musl's realloc begins ;; `if (!p) return malloc(n);` and would return non-null, so this is a ;; property of the zig-bundled libc rather than of musl generally, and ;; anything in this channel calling `realloc(p, 0)` inherits it. Measured ;; through a volatile function pointer, because the optimizer rewrites a ;; visible `realloc(NULL, n)` into `malloc(n)` and hides the real answer. ;; ;; REVISIT ON EVERY VERSION BUMP. Upstream master still has the same line, ;; so the patch is expected to keep applying -- but if it stops, the right ;; response is to check whether upstream fixed it, not to force it. ;; samurai is ninja, reimplemented in C99 by Michael Forney. ;; ;; WHY NOT NINJA. ninja is C++, and this channel has no C++ compiler on ;; PATH: `builder-base-assignments` emits no `CXX` and `toolchain/setup.sh` ;; writes wrappers for cc/ar/ranlib/ld only. A package can supply its own ;; `CXX` (see `gperf` below) but a build backend is on the critical path of ;; every meson package here, and a C99 leaf with no dependencies is a ;; better thing to put there than a C++ program with a Python configure ;; script. ;; ;; This is not a substitution meson has to be told about: it searches ;; `['ninja', 'ninja-build', 'samu']` itself ;; (mesonbuild/environment.py:182) and renames what it finds to `samurai` ;; for its own logging. Verified by reading meson 1.8.2's source, not ;; assumed from its documentation. ;; ;; `PREFIX` is passed as an install argument because samurai's Makefile ;; assigns `PREFIX=/usr/local` unconditionally rather than with `?=`, so ;; only a command-line override beats it. Its install target already ;; honours `DESTDIR`, which the builder supplies. (define samurai (package name: "samurai" version: "1.2" source: (external-source location: "https://github.com/michaelforney/samurai/releases/download/1.2/samurai-1.2.tar.gz" sha256: "3b8cf51548dfc49b7efe035e191ff5e1963ebc4fe8f6064a5eefc5343eaf78a5") buildenv: core-buildenv phases: (list (external-phase kind: 'patch patches: (list (channel-patch path: "patches/samurai-realloc-zero-length.patch" sha256: "f160b3d9265a68b88cbcc5e7b04ec5e65f1ef89289dac14919e3702c80e025b3"))) (external-phase kind: 'make) (external-phase kind: 'install args: (list "PREFIX=${out}"))))) ;; gperf, a perfect-hash generator. Nothing in this channel wants one; it ;; is here because fontconfig's configure RUNS it, at configure time: ;; ;; configure.ac:216 fc_gperf_test="$(echo 'foo' | gperf -L ANSI-C)" ;; ;; and the shape of that line is why the tool is packaged rather than left ;; absent. With gperf missing the command substitution yields the empty ;; string, the two AC_COMPILE_IFELSE arms then compile an empty snippet, ;; the first succeeds, and configure concludes `FC_GPERF_SIZE_T=size_t` ;; FROM A PROBE THAT TESTED NOTHING. Same family as the `file` case in ;; this module and as the ncurses `-dynamic` probe: an absent tool does not ;; announce itself, it produces a confident answer about something else. ;; ;; THIS IS THE CHANNEL'S FIRST C++ PACKAGE, and the `env:` line is what ;; makes it possible. `CXX` is not a builder-owned variable -- the ;; refusal list in `build-environment-assignments` is DERIVED from the ;; assignments the builder emits, and it emits none for C++ -- so a package ;; may declare one. `zig c++` is refused by the toolchain guard without an ;; explicit `-target`, hence the full triple here rather than a bare ;; `zig c++`. ;; ;; THE FALSIFIER, stated so this is a known dependency and not a surprise: ;; if the builder ever starts emitting its own `CXX`, the derived refusal ;; will cover this and the package breaks at once. That is the right ;; trade -- deriving the list is what keeps it honest -- but whoever adds a ;; builder `CXX` inherits converting this. (define gperf (package name: "gperf" version: "3.3" source: (external-source location: "https://ftp.gnu.org/gnu/gperf/gperf-3.3.tar.gz" sha256: "fd87e0aba7e43ae054837afd6cd4db03a3f2693deb3619085e6ed9d8d9604ad8") buildenv: core-buildenv env: (list (cons "CXX" "zig c++ -target x86_64-linux-musl")) phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}")) (external-phase kind: 'make) (external-phase kind: 'install)))) ;; meson. Pure Python, so there is nothing to compile and the whole ;; package is one `run` phase that copies a tree and writes a launcher. ;; ;; WHY THE LAUNCHER EXECS `python3` FROM PATH RATHER THAN AN ABSOLUTE PATH. ;; The honest way to write this would be `${python}/bin/python3`, but ;; `${name}` interpolation of a BUILD INPUT is refused at the identity ;; door -- a baked build-input path would be an undeclared runtime ;; reference -- and python is not a runtime dependency of the meson ;; *package*, it is a tool meson needs when it runs. ;; ;; Resolving from PATH is safe here in a way it would not be on a host: ;; inside the build sandbox PATH is exactly the declared build inputs, ;; then the seed, then the toolchain. There is no host filesystem to fall ;; through to. So a consumer that declares `meson` without also declaring ;; `python` gets a loud "python3: not found", not a silent host binary -- ;; which is the failure direction to be wrong in. Every meson consumer in ;; this channel declares both, and foot needs python3 directly anyway. (define meson (package name: "meson" version: "1.8.2" source: (external-source location: "https://github.com/mesonbuild/meson/releases/download/1.8.2/meson-1.8.2.tar.gz" sha256: "c105816d8158c76b72adcb9ff60297719096da7d07f6b1f000fd8c013cd387af") buildenv: core-buildenv phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "mkdir -p \"$d${out}/lib/meson\" \"$d${out}/bin\"\n" "cp -a mesonbuild \"$d${out}/lib/meson/\"\n" "cp meson.py \"$d${out}/lib/meson/\"\n" ;; THE LAUNCHER LOCATES ITSELF rather than ;; baking `${out}`. `${out}` expands to the ;; SANDBOX view -- /sigil/store/<item> -- which ;; exists inside any build that binds meson and ;; nowhere else. A launcher carrying that path ;; works where meson is actually used and dies ;; under `sigil env shell`, which is a real ;; failure mode this package hit on its first ;; build and which the same shape gives ncurses' ;; compiled-in terminfo root. Deriving the ;; prefix from `$0` costs one line, works in both ;; places, and leaves no absolute path in the ;; artifact at all. ;; ;; A quoted heredoc delimiter keeps the shell out ;; ;; `-B` IS LOAD-BEARING AND THIS PACKAGE PROVED ;; IT THE EXPENSIVE WAY. Without it, the FIRST ;; `meson --version` wrote 99 `.pyc` files into ;; `__pycache__` directories INSIDE meson's own ;; store item. The item then failed content ;; verification on its next use and sigil ;; quarantined it: ;; ;; sigil env: REFUSING CORRUPT <hash>: output ;; candidates failed content verification ;; ;; Measured, not inferred -- the quarantined ;; directory was inspected and the 99 files ;; counted. The refusal is correct behaviour and ;; the package was wrong. ;; ;; GENERALISE THIS: any interpreted-language ;; package in a content-addressed store must be ;; stopped from writing beside its own sources at ;; run time. Python is merely the first one here. ;; The failure is also delayed by one invocation, ;; so it does not look like a packaging bug -- ;; the run that breaks the item succeeds. ;; of `$0` and `$@`; neither uses braces, so ;; sigil's own `${...}` interpolation does not ;; see them either. "cat > \"$d${out}/bin/meson\" <<'WRAP'\n" "#!/bin/sh\n" "here=$(CDPATH= cd -- \"$(dirname -- \"$0\")\" && pwd)\n" "exec python3 -B \"$here/../lib/meson/meson.py\" \"$@\"\n" "WRAP\n" "chmod 755 \"$d${out}/bin/meson\"\n" "test -d \"$d${out}/lib/meson/mesonbuild\"\n" "echo MESON-INSTALLED\n"))))))))src/core/packages/capture.sgladded
;;; Screen capture.;;;;;; grim is a Wayland screenshot client. It is in this channel for two;;; reasons, and the second is the one worth reading.;;;;;; 1. IT IS A SECOND WAYLAND CLIENT. Everything else in the Wayland half of;;; this channel exists to serve foot, which makes it hard to tell whether;;; `wayland`, `pixman` and `wayland-protocols` are packaged correctly or;;; merely packaged the way foot happens to need. A second, unrelated;;; consumer answers that.;;;;;; 2. IT MAKES A DIFFERENTIAL TEST POSSIBLE -- AND IT MUST NOT BE MISTAKEN;;; FOR AN INSTRUMENT. foot's acceptance evidence is a screenshot of a;;; nested compositor, and that screenshot is deliberately taken with the;;; HOST's grim, not this one. The observer has to be independent of the;;; observed: a grim built from the SAME pixman and the SAME wayland-client;;; as foot could be wrong in a correlated way, agree with foot, and produce;;; a convincing green. What this package is good for is the other;;; direction -- run both grims on the same frame and compare. Agreement is;;; then evidence that the channel's Wayland and pixman stack behaves like;;; the host's, which is a measurement rather than a caveat.;;;;;; grim vendors the `wlr-screencopy` protocol XML it needs under `protocol/`,;;; so it does not drag wlroots into this channel.(define-library (core packages capture) (import (sigil core) (core build-environments) (core packages compression) (core packages graphics) (core packages wayland) (core packages ffi) (core packages build-tools) (core packages python) (sigil env package) (sigil build external)) (export grim) (begin (define grim (package name: "grim" version: "1.4.0" source: (external-source location: "https://github.com/emersion/grim/releases/download/v1.4.0/grim-1.4.0.tar.gz" sha256: "b6786e889ed93568634189a59f6b6429c041bf74883c10e8b6e2ed6772ae7ffb") buildenv: core-buildenv ;; zlib is reached through libpng.pc's `Requires:` chain -- see the ;; note on `fontconfig` in (core packages fonts) for why a ;; grand-dependency has to be named here. dependencies: (list libpng pixman wayland wayland-protocols libffi zlib) ;; `wayland` APPEARS IN BOTH LISTS, AND THAT IS NOT A MISTAKE. ;; ;; grim needs two different things from it. The LIBRARY ;; (wayland-client) is linked into the result, which is a ;; `dependencies:` relationship. The PROGRAM `wayland-scanner` runs ;; during the build to turn protocol XML into C, which is a ;; `build-inputs:` relationship -- and only build inputs contribute ;; their `bin/` to PATH. A dependency's `bin/` is not on PATH at all. ;; ;; foot does not need the second declaration, because foot asks ;; pkg-config for the scanner's absolute path ;; (`wscanner.get_variable('wayland_scanner')`) rather than searching ;; PATH. grim's `protocol/meson.build:3` does a plain ;; `find_program('wayland-scanner')`, so without this it fails: ;; ;; Run-time dependency wayland-client found: YES 1.24.0 ;; Program wayland-scanner found: NO ;; ;; Note the shape of that pair -- the library IS found and the program ;; is not, from the same package, one line apart. build-inputs: (list meson samurai python wayland) phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson setup build" " --prefix=${out}" " --buildtype=plain" " --default-library=static" ;; `-Dprefer_static=true` MAKES MESON ASK ;; PKG-CONFIG THE STATIC QUESTION, and without it ;; a fully static link fails at the last step. ;; ;; A `.pc` file splits its link line: `Libs:` is ;; what a SHARED consumer needs, `Libs.private:` ;; and `Requires.private:` are the extra ;; transitive libraries a STATIC consumer needs. ;; fontconfig lists expat under ;; `Requires.private:`, so a default ;; `dependency('fontconfig')` emits no `-lexpat` ;; -- correct for a shared link, fatal here: ;; ;; ld.lld: error: undefined symbol: XML_ErrorString ;; >>> referenced by fcxml.c ;; >>> fcxml.o in archive libfontconfig.a ;; ;; The symbol is missing from the LINK, not from ;; the closure: expat is declared, built, and ;; present. Only the query was the wrong one. ;; This is the same `Requires.private:` line that ;; had to be satisfied at CONFIGURE time; it bites ;; twice, in two different ways, and fixing the ;; first does not fix the second. " -Dprefer_static=true" ;; JPEG output would add libjpeg for a format ;; nothing here reads. " -Djpeg=disabled" ;; man pages want scdoc. " -Dman-pages=disabled\n" "echo GRIM-CONFIGURED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson compile -C build\n" "echo GRIM-COMPILED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "DESTDIR=\"$d\" meson install -C build --no-rebuild\n" "test -x \"$d${out}/bin/grim\"\n" "echo GRIM-INSTALLED\n"))))))))src/core/packages/datastructures.sgladded
;;; Generic data-structure libraries.;;;;;; tllist is a header-only typed linked list. It is in this channel because;;; fcft and foot both `dependency('tllist')`, and foot's fallback for it is a;;; `[wrap-git]` subproject pointing at a live git URL -- which cannot resolve;;; inside a build sandbox, so a real package is not optional.(define-library (core packages datastructures) (import (sigil core) (core build-environments) (sigil env package) (sigil build external)) (export tllist) (begin ;; TLLIST IS HEADER-ONLY AND IS STILL DECLARED AS A `dependencies:` ENTRY ;; BY ITS CONSUMERS, WHICH PUTS A BUILD-TIME-ONLY LIBRARY IN THEIR RUNTIME ;; CLOSURE. That is not sloppiness; the channel has no other category for ;; it, and the reason is worth knowing before someone "fixes" it. ;; ;; `build-inputs:` contributes the input's `bin/` to PATH and nothing else ;; -- no `-I`, no `-L`, no `PKG_CONFIG_PATH`. It can additionally carry a ;; package's own `search-paths:` exports, so a header-only library CAN ;; reach a build as a build input by exporting `CPATH`. ;; ;; What it cannot do is be found by PKG-CONFIG. `PKG_CONFIG_PATH` is ;; composed by the builder from DEPENDENCIES only, and it is builder-owned, ;; so a package may not export it -- that is refused by name. fcft and ;; foot both locate this library with `dependency('tllist')`, which is a ;; pkg-config lookup, so it has to be a dependency. ;; ;; So: THERE IS NO "BUILD-TIME LIBRARY" CATEGORY HERE. A package that is ;; discovered through pkg-config is a runtime dependency by construction, ;; whether or not anything of it survives into the binary. For a ;; header-only library nothing does. ;; INSTALLED BY HAND RATHER THAN BY MESON, DELIBERATELY. ;; ;; tllist is one header and a `.pc` file. Its meson.build does exactly ;; two things when it is not a subproject: `install_headers('tllist.h')` ;; and `pkg.generate(...)`. Running a full meson configure to copy one ;; header would put meson, samurai and python on the critical path of a ;; package that needs none of them -- and this package was written before ;; meson had been proven in this channel at all, which is the actual ;; reason: it removes a dependency from the ordering, not just from the ;; build. ;; ;; The `.pc` is hand-written to match what meson's pkgconfig module would ;; have emitted. NOTE THE ABSENCE OF `${prefix}`-STYLE VARIABLES: a ;; pkg-config file would normally define `prefix=` and refer to it, but ;; `${...}` inside a run-phase argument is intercepted by SIGIL's ;; interpolation, not by the shell or by pkg-config, and any name that is ;; not `out` or a declared input is an error at evaluation time. Absolute ;; paths are what the rest of this channel bakes anyway. (define tllist (package name: "tllist" version: "1.1.0" source: (external-source location: "https://codeberg.org/dnkl/tllist/releases/download/1.1.0/tllist-1.1.0.tar.gz" sha256: "a1f0ce0dc4fcb4ce59c37202a1d914c853a8bd2d7c5d57d623b15a8dd9778767") buildenv: core-buildenv phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "mkdir -p \"$d${out}/include\" \"$d${out}/lib/pkgconfig\"\n" "cp tllist.h \"$d${out}/include/\"\n" "cat > \"$d${out}/lib/pkgconfig/tllist.pc\" <<'PC'\n" "Name: tllist\n" "Description: A C header file only implementation of a typed linked list\n" "Version: 1.1.0\n" "Cflags: -I${out}/include\n" "PC\n" ;; Prove the artifact rather than trust the copy: ;; the header must be there AND pkg-config must ;; be able to answer a query about it, which is ;; what every consumer will actually do. "test -f \"$d${out}/include/tllist.h\"\n" "PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --exists tllist\n" "PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --modversion tllist\n" "echo TLLIST-INSTALLED\n")))))))) src/core/packages/ffi.sgladded
;;; Foreign function interface.;;;;;; libffi is here for exactly one consumer -- libwayland's connection layer;;; dispatches protocol messages through `ffi_call`, in the CLIENT as well as;;; the server -- but it is filed under its own domain rather than beside;;; wayland, on the same reasoning that put libevent in `event.sgl` instead of;;; `terminal.sgl`: a module named for the consumer writes "this exists to;;; serve wayland" into the layout, and it does not.(define-library (core packages ffi) (import (sigil core) (core build-environments) (sigil env package) (sigil build external)) (export libffi) (begin (define libffi (package name: "libffi" version: "3.4.8" source: (external-source location: "https://github.com/libffi/libffi/releases/download/v3.4.8/libffi-3.4.8.tar.gz" sha256: "bc9842a18898bfacb0ed1252c4febcc7e78fa139fd27fdc7a3e30d9d9356119b") buildenv: core-buildenv phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--enable-static" ;; No docs: they want makeinfo, which is not in ;; the seed, and libffi's build does not fail ;; without it -- it silently ships no info ;; pages, which is a smaller and quieter ;; version of the same absent-tool problem. "--disable-docs" ;; The multi-OS directory layout installs ;; headers under lib/libffi-<ver>/include, ;; which no consumer's -I would find. "--disable-multi-os-directory")) (external-phase kind: 'make) (external-phase kind: 'install))))))src/core/packages/fonts.sgladded
;;; Font rasterisation and font configuration.;;;;;; This is the half of foot's closure that has to reach OUTSIDE the store to;;; be useful, and it is the one place in this chain where "hermetic" and;;; "works on the user's desktop" pull against each other. Wayland does not:;;; a client and a compositor exchange a wire protocol over a socket and never;;; share a library, so version skew there is a non-issue. Fonts are;;; different -- a fontconfig built into the store looks for font FILES, and;;; the font files a user has are wherever their system put them.;;;;;; Nothing here solves that; it is recorded so the next reader does not;;; mistake a font-path gap for a foot defect. See the task note for the;;; measured runtime behaviour.(define-library (core packages fonts) (import (sigil core) (core build-environments) (core packages compression) (core packages xml) (core packages graphics) (core packages datastructures) (core packages build-tools) (core packages python) (sigil env package) (sigil build external)) (export freetype fontconfig fcft) (begin ;; freetype, with every optional backend OFF. ;; ;; The harfbuzz one is not a size decision, it is a CYCLE: harfbuzz builds ;; against freetype and freetype can build against harfbuzz for ;; auto-hinting. Distributions resolve it by building freetype twice. ;; This channel does not need to, because nothing here does complex text ;; shaping -- foot is configured without grapheme clustering, which is what ;; would have pulled harfbuzz in through fcft. (define freetype (package name: "freetype" version: "2.13.3" source: (external-source location: "https://download.savannah.gnu.org/releases/freetype/freetype-2.13.3.tar.xz" sha256: "0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289") buildenv: core-buildenv dependencies: (list zlib) phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--enable-static" "--with-zlib=yes" "--with-bzip2=no" "--with-png=no" "--with-brotli=no" "--with-harfbuzz=no")) (external-phase kind: 'make) (external-phase kind: 'install) ;; fontconfig asks for `freetype2 >= 21.0.15` through ;; PKG_CHECK_MODULES and fcft asks for it too, so the `.pc` ;; is load-bearing rather than decorative. Note the version ;; in that constraint is freetype's LIBTOOL version, not its ;; release version -- 2.13.3 publishes 26.x -- which is why ;; the check below asserts the file exists and does not try ;; to be clever about its contents. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "test -f \"$d${out}/lib/pkgconfig/freetype2.pc\"\n" "echo FREETYPE2-PC-PRESENT\n")))))) ;; VERSION AND SOURCE, because both were forced and the reasoning matters. ;; ;; `www.freedesktop.org/software/fontconfig/release/` -- the URL every ;; older recipe uses -- was unreachable from this host across three ;; attempts spanning ten minutes. Upstream has moved distribution to ;; the freedesktop GitLab generic package registry, which is where ;; Alpine now points too, and that registry does NOT carry 2.15.0. ;; ;; So the choice was between 2.15.0 from a DISTRIBUTION MIRROR and ;; 2.18.1 from the CANONICAL upstream host. Taking the canonical host ;; keeps a real upstream release artifact on the critical path, which ;; is the rule this channel is built on, and it is a current release ;; rather than a two-year-old one. ;; ;; 2.18.1 SHIPS BOTH BUILD SYSTEMS, and this build deliberately uses ;; the autotools one. Its meson.build pulls in Rust for the optional ;; `fc-fontations` backend (meson.build:92, add_languages([rust])); ;; configure.ac mentions Rust nowhere. Autotools therefore avoids ;; introducing a whole second language toolchain to this channel for a ;; feature foot does not use. ;; ;; fontconfig. Three build-time tools and two libraries, and the tool list ;; is the interesting part: ;; ;; gperf -- configure.ac:216 RUNS it: `echo 'foo' | gperf -L ANSI-C`. ;; Absent, the substitution is empty, the two compile probes ;; that follow test an empty snippet, the first succeeds, and ;; configure concludes FC_GPERF_SIZE_T=size_t from a probe ;; that measured nothing. ;; python -- configure.ac:56 is AM_PATH_PYTHON([3]). ;; ;; Both are `build-inputs:` and not `dependencies:`, because neither is ;; linked into the result and neither may contribute a store path to it. (define fontconfig (package name: "fontconfig" version: "2.18.1" source: (external-source location: "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.1/fontconfig-2.18.1.tar.xz" sha256: "2300f3dbfa7253b3a44f4feecdbc8dfa45dde5dc2cfb71fceaf31f394cb41031") buildenv: core-buildenv ;; ZLIB IS DECLARED HERE ALTHOUGH FONTCONFIG NEVER MENTIONS IT, AND THE ;; REASON IS A PROPERTY OF THIS CHANNEL RATHER THAN OF FONTCONFIG. ;; ;; `dependency-pkgconfig` in (sigil build external) composes ;; PKG_CONFIG_PATH from DIRECT dependencies only -- one ;; `<dep>/lib/pkgconfig` entry each, with no transitive walk. freetype's ;; generated `freetype2.pc` carries `Requires: zlib`, so pkg-config ;; resolving `freetype2` must also resolve `zlib`, and without this line ;; it cannot: ;; ;; checking for FREETYPE... no ;; configure: error: Package requirements (freetype2 >= 21.0.15) were ;; not met: Package 'zlib', required by 'freetype2', not found ;; ;; THE DIAGNOSTIC NAMES FREETYPE AND THE MISSING PACKAGE IS ZLIB, which ;; is the part that costs the time. ;; ;; This is not a workaround. It is the same rule the builder applies to ;; `${name}` interpolation -- a build that wants to reach a ;; grand-dependency has to declare it -- and it is TRUE here: everything ;; in this channel links statically, so zlib really is on fontconfig's ;; link line and really is in its closure. Every consumer of a `.pc` ;; with a `Requires:` chain inherits this. dependencies: (list freetype expat zlib) build-inputs: (list gperf python) phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--enable-static" ;; The docs want docbook and would otherwise be ;; a probe on what is installed. "--disable-docs" ;; Nothing in this channel reads a cache ;; fontconfig would have to build at install ;; time, and running fc-cache during a build ;; would write outside the staged prefix. "--disable-cache-build" ;; NLS wants gettext, which is not in the seed ;; and which nothing here reads. "--disable-nls")) (external-phase kind: 'make) (external-phase kind: 'install) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "test -f \"$d${out}/lib/pkgconfig/fontconfig.pc\"\n" ;; The config file is what a runtime consumer ;; needs and what the font-path problem above is ;; about; assert it landed so a missing one is ;; found here rather than at first launch. "test -f \"$d${out}/etc/fonts/fonts.conf\"\n" "echo FONTCONFIG-PC-AND-CONF-PRESENT\n")))))) ;; fcft: the glyph rasterisation and caching layer between foot and ;; freetype/fontconfig. By the same author as foot, and foot's meson.build ;; carries a `[wrap-git]` fallback for it -- which cannot resolve inside a ;; sandbox, so a real package is required rather than optional. ;; ;; TEXT SHAPING IS OFF, AND THAT IS THE LARGEST SCOPE DECISION IN THIS ;; CHAIN. `grapheme-shaping` and `run-shaping` each pull in harfbuzz, and ;; run-shaping additionally pulls utf8proc. Turning both off removes two ;; packages, and harfbuzz would additionally have re-opened the ;; freetype/harfbuzz build cycle that `freetype` above is configured to ;; avoid. ;; ;; WHAT IS LOST, said plainly rather than buried: without shaping, foot ;; renders each grapheme cluster from a single font glyph. Combining ;; sequences -- emoji built from ZWJ sequences, some scripts with complex ;; joining behaviour -- will not compose. Latin, Cyrillic, Greek, CJK and ;; box drawing are unaffected. Turn it back on by packaging harfbuzz and ;; utf8proc and flipping these two options here and in `foot`. ;; ;; The SVG backend is left at its default, which uses the bundled nanosvg ;; and therefore costs no package. (define fcft (package name: "fcft" version: "3.3.3" source: (external-source location: "https://codeberg.org/dnkl/fcft/releases/download/3.3.3/fcft-3.3.3.tar.gz" sha256: "bb298772d625e7d917373d541456f34f1957a9e22b16f17f64158b2c3816563c") buildenv: core-buildenv ;; zlib is here for the reason spelled out on fontconfig above: it is ;; reached through freetype2.pc's `Requires:` line, and this channel ;; composes PKG_CONFIG_PATH from direct dependencies only. dependencies: (list fontconfig freetype pixman tllist expat zlib) build-inputs: (list meson samurai python) phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson setup build" " --prefix=${out}" " --buildtype=plain" " --default-library=static" ;; `-Dprefer_static=true` MAKES MESON ASK ;; PKG-CONFIG THE STATIC QUESTION, and without it ;; a fully static link fails at the last step. ;; ;; A `.pc` file splits its link line: `Libs:` is ;; what a SHARED consumer needs, `Libs.private:` ;; and `Requires.private:` are the extra ;; transitive libraries a STATIC consumer needs. ;; fontconfig lists expat under ;; `Requires.private:`, so a default ;; `dependency('fontconfig')` emits no `-lexpat` ;; -- correct for a shared link, fatal here: ;; ;; ld.lld: error: undefined symbol: XML_ErrorString ;; >>> referenced by fcxml.c ;; >>> fcxml.o in archive libfontconfig.a ;; ;; The symbol is missing from the LINK, not from ;; the closure: expat is declared, built, and ;; present. Only the query was the wrong one. ;; This is the same `Requires.private:` line that ;; had to be satisfied at CONFIGURE time; it bites ;; twice, in two different ways, and fixing the ;; first does not fix the second. " -Dprefer_static=true" " -Dgrapheme-shaping=disabled" " -Drun-shaping=disabled" " -Ddocs=disabled" " -Dexamples=false" " -Dtest-text-shaping=false\n" "echo FCFT-CONFIGURED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson compile -C build\n" "echo FCFT-COMPILED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "DESTDIR=\"$d\" meson install -C build --no-rebuild\n" "test -f \"$d${out}/lib/pkgconfig/fcft.pc\"\n" ;; foot pins fcft to >=3.3.1 <4.0.0, so the ;; version pkg-config reports is load-bearing and ;; is asserted rather than assumed. "PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --modversion fcft\n" "echo FCFT-INSTALLED\n"))))))))src/core/packages/graphics.sgladded
;;; Software rasterisation.;;;;;; pixman is foot's renderer: foot draws its cells into pixman images in;;; shared memory and hands the buffer to the compositor through `wl_shm`.;;; There is no GPU path in foot at all, which is why nothing in this channel;;; needs Mesa, a DRM driver, or anything else that would have to match the;;; host's graphics stack.;;;;;; VERSION, and this is a deliberate choice rather than staleness: 0.42.2 is;;; the last pixman release with an autotools build. 0.43 and later are;;; meson-only. foot's meson.build asks only for `pixman-1` with no version;;; constraint -- it tests `pixman.version().version_compare('>=0.46.0')` to;;; decide whether to define HAVE_PIXMAN_RGBA_16, and simply does not define it;;; otherwise. So taking the autotools release costs one optional pixel format;;; and removes a meson build from the chain.(define-library (core packages graphics) (import (sigil core) (core build-environments) (core packages compression) (sigil env package) (sigil build external)) (export pixman libpng) (begin (define pixman (package name: "pixman" version: "0.42.2" source: (external-source location: "https://cairographics.org/releases/pixman-0.42.2.tar.gz" sha256: "ea1480efada2fd948bc75366f7c349e1c96d3297d09a3fe62626e38e234a625e") buildenv: core-buildenv phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--enable-static" ;; Every one of these is a probe that would ;; otherwise make the output depend on what ;; happens to be visible in the build ;; environment. Turning them off is what makes ;; the result a function of the declared ;; inputs rather than of the sandbox's luck. "--disable-gtk" "--disable-libpng" "--disable-openmp")) (external-phase kind: 'make) (external-phase kind: 'install)))) ;; libpng exists here for `grim`, the screenshot client, and for nothing ;; else -- foot renders through pixman and never encodes an image. ;; ;; zlib is declared for the reason set out at length on `fontconfig` in ;; (core packages fonts): libpng's generated `libpng.pc` carries a ;; `Requires:` on zlib, and this channel composes PKG_CONFIG_PATH from ;; DIRECT dependencies only. It is also simply true -- PNG's compression ;; IS zlib's deflate, so zlib is on every libpng consumer's static link ;; line. (define libpng (package name: "libpng" version: "1.6.50" source: (external-source location: "https://download.sourceforge.net/libpng/libpng-1.6.50.tar.xz" sha256: "4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307") buildenv: core-buildenv dependencies: (list zlib) phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--enable-static")) (external-phase kind: 'make) (external-phase kind: 'install) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "test -f \"$d${out}/lib/pkgconfig/libpng.pc\"\n" "echo LIBPNG-PC-PRESENT\n"))))))))src/core/packages/input.sgladded
;;; Keyboard handling.;;;;;; libxkbcommon compiles XKB keymaps and turns key codes into keysyms and;;; UTF-8. A Wayland client needs it because the compositor hands over its;;; keymap as an XKB TEXT description over a file descriptor and each client;;; compiles that itself.;;;;;; THAT IS ALSO WHY `xkeyboard-config` IS NOT IN THIS CHANNEL. The keymap;;; DATABASE -- rules, symbols, geometry -- is what an X server or a;;; compositor reads to BUILD a keymap. A Wayland client is handed the;;; finished text and never opens the database, so `xkb-config-root` is left;;; at its default and nothing resolves it at runtime. Worth stating because;;; the default points at a path that does not exist here, and a reader who;;; assumes it is load-bearing will go looking for a missing package.(define-library (core packages input) (import (sigil core) (core build-environments) (core packages build-tools) (core packages python) (sigil env package) (sigil build external)) (export libxkbcommon) (begin ;; VERSION 1.7.0, AND IT IS CHOSEN RATHER THAN INHERITED. ;; ;; Two reasons, and they point the same way: ;; ;; PROVENANCE. `xkbcommon.org/download/` stops at 1.7.0 -- every later ;; version 404s there -- and GitHub publishes no release asset for this ;; project, only auto-generated source archives. Those archives are not ;; release artifacts: their bytes can be regenerated and have changed ;; under people before. 1.7.0 is the newest version with a real upstream ;; dist tarball. ;; ;; COST. 1.7.0's meson.build:157 makes bison `required: false` and falls ;; back to `find_program('byacc')`. 1.13.2 requires `bison >= 3.6` ;; outright, and bison needs GNU m4 to build -- so the newer version ;; would have added TWO packages to this channel, in exchange for weaker ;; provenance. ;; ;; foot asks only for `xkbcommon >= 1.0.0`, so 1.7.0 is comfortably inside ;; what it needs. If a future consumer wants a newer one, that is a scoped ;; upgrade with an upstream-tarball question attached, not a reason to put ;; a synthetic archive on this path. ;; ;; The byacc fallback is why `core/byacc` installs itself under BOTH names: ;; the fallback looks for a program called `byacc`, and byacc's own ;; makefile installs only `yacc`. See the note there. (define libxkbcommon (package name: "libxkbcommon" version: "1.7.0" source: (external-source location: "https://xkbcommon.org/download/libxkbcommon-1.7.0.tar.xz" sha256: "65782f0a10a4b455af9c6baab7040e2f537520caa2ec2092805cdfd36863b247") buildenv: core-buildenv build-inputs: (list meson samurai python byacc) phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson setup build" " --prefix=${out}" " --buildtype=plain" " --default-library=static" ;; X11 support would want libxcb; the registry ;; and the tools would want more still. foot is ;; a Wayland client and links only the core ;; library. " -Denable-x11=false" " -Denable-xkbregistry=false" " -Denable-wayland=false" " -Denable-tools=false" " -Denable-docs=false" " -Denable-bash-completion=false\n" "echo XKBCOMMON-CONFIGURED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" ;; Prove the yacc fallback actually ran, rather ;; than assuming it: the parser is GENERATED, and ;; a byacc without backtracking dies on this ;; grammar's `%destructor`. ;; ;; SEARCHED RATHER THAN NAMED, because the first ;; version of this check asserted ;; `build/src/xkbcomp/parser.c` -- a path I had ;; guessed. meson actually emits it into ;; per-target directories ;; (`build/libxkbcommon.a.p/parser.c`), so the ;; check failed on a build where all 121 targets ;; had linked successfully. A gate that names a ;; path it has not observed is asserting its ;; author's expectations, not the product. "meson compile -C build\n" "n=$(find build -name parser.c | wc -l)\n" "echo \"XKBCOMMON-GENERATED-PARSERS=$n\"\n" "test \"$n\" -ge 1\n" "echo XKBCOMMON-PARSER-GENERATED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "DESTDIR=\"$d\" meson install -C build --no-rebuild\n" "test -f \"$d${out}/lib/pkgconfig/xkbcommon.pc\"\n" "echo XKBCOMMON-INSTALLED\n"))))))))src/core/packages/python.sgladded
;;; CPython, packaged as a BUILD TOOL for this channel and nothing else.;;;;;; WHY A CHANNEL WITH NO PYTHON PROGRAMS IN IT NEEDS PYTHON. Three;;; independent consumers, and no choice of build system removes the first:;;;;;; 1. foot's own `meson.build` runs `python3` in three UNCONDITIONAL;;; custom_targets -- scripts/generate-builtin-terminfo.py,;;; scripts/generate-emoji-variation-sequences.py and the srgb generator.;;; They emit headers that foot then compiles. No option turns them off.;;; 2. meson is CPython.;;; 3. fontconfig's configure.ac is `AM_PATH_PYTHON([3])`.;;;;;; That is why muon -- the C reimplementation of meson -- was considered and;;; rejected. It removes consumer 2 and leaves 1 and 3.(define-library (core packages python) (import (sigil core) (core build-environments) (core packages compression) (sigil env package) (sigil build external)) (export python) (begin (define python (package name: "python" version: "3.13.7" source: (external-source location: "https://www.python.org/ftp/python/3.13.7/Python-3.13.7.tar.xz" sha256: "5462f9099dfd30e238def83c71d91897d8caa5ff6ebc7a50f14d4802cdaaa79a") buildenv: core-buildenv ;; zlib is the one optional module worth having: `zipfile`, `shutil`'s ;; archive support and `gzip` all reach for it, and it is already in ;; the channel. Every other optional module -- ssl, readline, curses, ;; sqlite3, ctypes, bz2, lzma -- is left to fail its probe, and in this ;; sandbox that is an HONEST absence: there is no host filesystem for a ;; probe to find something in, so nothing can be silently absorbed. dependencies: (list zlib) env: (list ;; --------------------------------------------------------- ;; STATIC MODULES. Everything this channel builds is linked ;; statically and a static binary cannot dlopen. CPython's ;; default is to build each stdlib extension as a shared object ;; and load it at import, so left alone the interpreter starts ;; and then dies on `import re` -- at RUN time, inside some ;; other package's build, a very long way from here. ;; ;; Both switches are read from the ENVIRONMENT by configure, ;; which is what makes this expressible without patching ;; CPython: ;; ;; configure.ac:7684 MODULE_BUILDTYPE=${MODULE_BUILDTYPE:-shared} ;; configure.ac:5156 if test -z "$DYNLOADFILE" ... ;; ;; Neither name is builder-owned, so neither is refused. (cons "MODULE_BUILDTYPE" "static") (cons "DYNLOADFILE" "dynload_stub.o") ;; --------------------------------------------------------- ;; THE -O2 / __DATE__ COLLISION, WHICH IS A PROPERTY OF THIS ;; CHANNEL AND NOT OF CPYTHON. The builder hands every compile ;; a response file whose first line is `-O2`, and zig cc turns ;; ON -Werror=date-time whenever it optimizes. So ANY package ;; here whose sources expand __DATE__ or __TIME__ fails to ;; compile. CPython is merely the first one that does ;; (Modules/getbuildinfo.c:14,22). ;; ;; Narrowed by elimination against this toolchain rather than ;; guessed: `-g0`, `-ffile-prefix-map` and `-fdebug-prefix-map` ;; each produce 0 date-time diagnostics; `-O2` alone produces 2, ;; on the command line and inside a response file alike. ;; ;; Suppressing the guard is safe HERE, and the reason is ;; measured: zig cc expands __DATE__ to "Jan 1 1970" and ;; __TIME__ to "00:00:00" UNCONDITIONALLY. It does not honour ;; SOURCE_DATE_EPOCH -- set to 1000000000 it still emits ;; Jan 1 1970 -- and two independent compiles produced ;; byte-identical executables. The macros are already ;; deterministic and the diagnostic is a false positive. It ;; exists for compilers where they are not. ;; ;; CFLAGS is builder-owned but APPENDABLE, and the package's ;; value is emitted after the builder's, so this beats the ;; response file's -O2 rather than replacing the dependency ;; include flags. (cons "CFLAGS" "-Wno-error=date-time") ;; LDFLAGS, same append rule. Static because the sandbox binds ;; no loader for a build input. (cons "LDFLAGS" "-static")) phases: (list ;; THE SUPPRESSION ABOVE IS PAIRED WITH A CHECK THAT ASSERTS ;; THE PROPERTY IT RELIES ON. Turning off a reproducibility ;; diagnostic on the strength of "the toolchain pins those ;; macros" is only honest if something notices when it stops. ;; If a future toolchain emits the real date, this fails ;; loudly and prints what it saw, rather than quietly ;; producing an unreproducible interpreter -- which is ;; exactly the failure -Wdate-time exists to prevent. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "echo '#include <stdio.h>' > dtcheck.c\n" "echo 'int main(void){ printf(\"%s|%s\", __DATE__, __TIME__); return 0; }' >> dtcheck.c\n" "$CC -Wno-error=date-time dtcheck.c -o dtcheck\n" "v=$(./dtcheck)\n" "echo \"TOOLCHAIN-DATE-MACROS=[$v]\"\n" "test \"$v\" = \"Jan 1 1970|00:00:00\"\n" "rm -f dtcheck dtcheck.c\n" "echo DATE-MACRO-PINNING-CONFIRMED\n"))) (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--disable-test-modules" "--with-ensurepip=no")) (external-phase kind: 'make) (external-phase kind: 'install))))))src/core/packages/terminal.sglmodified
(import (sigil core) (core build-environments) (core packages event) (core packages ffi) (core packages xml) (core packages compression) (core packages graphics) (core packages datastructures) (core packages fonts) (core packages wayland) (core packages input) (core packages python) (core packages build-tools) (sigil env package) (sigil build external)) (export ncurses tmux) (export ncurses tmux foot) (begin (define ncurses (package ;; the seed's applet list. "ac_cv_prog_found_vlock=no")) (external-phase kind: 'make) (external-phase kind: 'install)))))) (external-phase kind: 'install)))) ;; foot: a Wayland terminal emulator. ;; ;; THE INTERESTING PROPERTY, and the reason this package is the point of ;; the exercise rather than another consumer: FOOT SHIPS ITS OWN TERMINFO. ;; `tic` -- from `core/ncurses`, as a BUILD INPUT -- compiles `foot.info` ;; into `${out}/share/terminfo` during the build. ;; ;; That makes foot a WEAK test of terminfo resolution and a STRONG one, in ;; two different configurations, and confusing them is how you get a green ;; that proves nothing: ;; ;; AS A ROOT, foot's own terminfo is exported by even the oldest, ;; broken search-path behaviour, because that behaviour composes paths ;; over the lock's ROOT packages. A foot that starts and draws in that ;; configuration would have started and drawn before any of the runtime ;; search-path work existed. ;; ;; THROUGH A DEPENDENCY is where it breaks. Run `tmux` inside foot and ;; tmux needs the `screen` entry, which comes from `core/ncurses` -- a ;; DEPENDENCY of tmux, not a root. That is the path that was absent. ;; ;; The acceptance evidence for this package is built around the second ;; case, deliberately. See [[tasks/foot-packaging]]. ;; ;; WHAT FOOT NEEDS FROM OUTSIDE THE STORE, stated because a reader will ;; otherwise mistake it for a defect: ;; ;; - A Wayland compositor. Supplied by the user's session over a socket; ;; no library or version has to match, because Wayland is a wire ;; protocol. See the note in `(core packages wayland)`. ;; - FONTS. fontconfig searches the directories ITS configuration names, ;; and the user's font files are wherever their system put them. This ;; is the one genuine host coupling in foot's runtime and nothing in ;; this channel closes it. ;; - A cursor theme, through wayland-cursor, with the same shape. ;; ;; `-Dcustom-terminfo-install-location` is deliberately NOT set. Setting ;; it makes foot export `TERMINFO` into its child processes, pointing at ;; its own store item -- which would be convenient and would also mask the ;; dependency-path question this package exists to exercise. If it is ever ;; set, the acceptance test above stops measuring what it claims to. (define foot (package name: "foot" version: "1.27.0" source: (external-source location: "https://codeberg.org/dnkl/foot/releases/download/1.27.0/foot-1.27.0.tar.gz" sha256: "d1e1d04cd7a3a1615b4d8dbf9645b9a35cdc3f36fba4e1c0fd252ad0bd6651c8") buildenv: core-buildenv ;; freetype and zlib are not named by foot's own meson.build. They are ;; reached through the `Requires:` chain of fcft.pc and fontconfig.pc, ;; and this channel composes PKG_CONFIG_PATH from DIRECT dependencies ;; only -- see the long note on `fontconfig`. dependencies: (list fcft tllist pixman wayland wayland-protocols libffi expat libxkbcommon fontconfig freetype zlib) ;; ncurses is a BUILD INPUT and not a dependency: `tic` runs during the ;; build and nothing from ncurses is linked into foot or referenced by ;; it at runtime. Declaring it as a dependency would put ncurses in ;; foot's runtime closure and would ALSO quietly make the terminfo ;; acceptance test pass for the wrong reason. build-inputs: (list meson samurai python ncurses) phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson setup build" " --prefix=${out}" " --buildtype=plain" " --default-library=static" ;; `-Dprefer_static=true` MAKES MESON ASK ;; PKG-CONFIG THE STATIC QUESTION, and without it ;; a fully static link fails at the last step. ;; ;; A `.pc` file splits its link line: `Libs:` is ;; what a SHARED consumer needs, `Libs.private:` ;; and `Requires.private:` are the extra ;; transitive libraries a STATIC consumer needs. ;; fontconfig lists expat under ;; `Requires.private:`, so a default ;; `dependency('fontconfig')` emits no `-lexpat` ;; -- correct for a shared link, fatal here: ;; ;; ld.lld: error: undefined symbol: XML_ErrorString ;; >>> referenced by fcxml.c ;; >>> fcxml.o in archive libfontconfig.a ;; ;; The symbol is missing from the LINK, not from ;; the closure: expat is declared, built, and ;; present. Only the query was the wrong one. ;; This is the same `Requires.private:` line that ;; had to be satisfied at CONFIGURE time; it bites ;; twice, in two different ways, and fixing the ;; first does not fix the second. " -Dprefer_static=true" " -Ddocs=disabled" ;; Matches fcft: no harfbuzz, no utf8proc. The ;; two must agree -- grapheme clustering in foot ;; without shaping in fcft is, in foot's own ;; words, not useful. " -Dgrapheme-clustering=disabled" ;; utmp/wtmp accounting would want a setuid ;; helper this channel has no way to install. " -Dutmp-backend=none" " -Dtests=false\n" "echo FOOT-CONFIGURED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson compile -C build\n" "echo FOOT-COMPILED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "DESTDIR=\"$d\" meson install -C build --no-rebuild\n" "test -x \"$d${out}/bin/foot\"\n" "test -x \"$d${out}/bin/footclient\"\n" ;; THE TERMINFO ENTRIES ARE ASSERTED, not ;; assumed. `-Dterminfo` defaults to enabled but ;; `find_program('tic')` is `required:` on that ;; option -- so if the build input ever went ;; missing, meson would DISABLE the feature and ;; still succeed, and foot would install with no ;; terminfo at all. That is a silent ;; feature-loss of exactly the kind this channel ;; keeps finding, so it fails here instead. ;; ;; `find -L` because terminfo directories are ;; symlink farms and plain `find -type f` ;; returns 0 on them. "n=$(find -L \"$d${out}/share/terminfo\" -type f | wc -l)\n" "echo \"FOOT-TERMINFO-FILES=$n\"\n" "test \"$n\" -ge 2\n" "test -f \"$d${out}/share/terminfo/f/foot\"\n" "test -f \"$d${out}/share/terminfo/f/foot-direct\"\n" "echo FOOT-INSTALLED\n"))))))))src/core/packages/wayland.sgladded
;;; The Wayland display protocol: the client library, the code generator, and;;; the protocol definitions.;;;;;; WHAT DOES AND DOES NOT HAVE TO MATCH THE USER'S COMPOSITOR, because it is;;; the first question anyone asks about a store-built Wayland client and the;;; answer is not obvious:;;;;;; Wayland is a WIRE PROTOCOL over a Unix socket, not a shared-library ABI.;;; A client links libwayland-client, a compositor links libwayland-server,;;; and the two never link against each other or share an address space. So;;; a statically linked musl client built from this channel talks to a;;; glibc compositor from any distribution, at any library version, without;;; difficulty. What must line up is the PROTOCOL VERSIONS a client binds;;; against those the compositor advertises -- and `wayland-protocols` below;;; is a BUILD-TIME source of XML for code generation, not something the;;; compositor needs to have.;;;;;; The same reasoning covers libxkbcommon: a Wayland compositor sends its;;; keymap to clients as TEXT over a file descriptor, and each client compiles;;; it with its own copy of the library. That is why nothing here needs;;; `xkeyboard-config` -- a Wayland client never reads the keymap database.(define-library (core packages wayland) (import (sigil core) (core build-environments) (core packages ffi) (core packages xml) (core packages build-tools) (core packages python) (sigil env package) (sigil build external)) (export wayland wayland-protocols) (begin ;; THE CHANNEL'S FIRST MESON PACKAGE. ;; ;; There is no `meson` phase kind and none is needed: `run` executes an ;; authored argv under `set -eu`, so the three steps are three phases. ;; Separate phases rather than one script because a failure then names ;; which step failed without anyone reading a log. ;; ;; `--buildtype=plain` is the load-bearing flag. Meson's other build ;; types inject their own optimisation and debug flags, which would sit ;; alongside the builder's `@compiler.flags` response file with no defined ;; precedence. `plain` tells meson to add nothing and use CFLAGS, ;; CPPFLAGS and LDFLAGS from the environment -- which is exactly the ;; channel's model, and is what every distribution uses for the same ;; reason. ;; ;; DESTDIR is passed to `meson install` through the environment rather ;; than as `--destdir`, so the same idiom works on any meson old enough to ;; predate that flag. `$d` is derived from the phase's cwd rather than ;; hardcoded: a run phase cannot interpolate the staged install root, but ;; it always runs in `<build-root>/source` and the staged root is always ;; `<build-root>/install-root`. (define wayland (package name: "wayland" version: "1.24.0" source: (external-source location: "https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.24.0/downloads/wayland-1.24.0.tar.xz" sha256: "82892487a01ad67b334eca83b54317a7c86a03a89cfadacfef5211f11a5d0536") buildenv: core-buildenv ;; libffi is a genuine runtime dependency: libwayland dispatches every ;; protocol message through `ffi_call`, in the client as well as the ;; server. expat is needed by wayland-scanner, which parses protocol ;; XML -- and although the scanner is a build-time tool for consumers, ;; it is a linked artifact OF THIS package, so expat is a dependency ;; and not a build input. dependencies: (list libffi expat) build-inputs: (list meson samurai python) phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson setup build" " --prefix=${out}" " --buildtype=plain" " --default-library=static" ;; documentation wants doxygen, xmlto and ;; xsltproc; dtd_validation wants libxml2. Both ;; are probes on what happens to be visible, and ;; neither produces anything foot consumes. " -Ddocumentation=false" " -Dtests=false" " -Ddtd_validation=false\n" "echo WAYLAND-CONFIGURED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "meson compile -C build\n" "echo WAYLAND-COMPILED\n"))) (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "DESTDIR=\"$d\" meson install -C build --no-rebuild\n" ;; Assert the three things consumers resolve by ;; name. foot asks for wayland-client and ;; wayland-cursor through pkg-config and then ;; reads `wayland_scanner` out of the ;; wayland-scanner `.pc` to find the generator. "test -f \"$d${out}/lib/pkgconfig/wayland-client.pc\"\n" "test -f \"$d${out}/lib/pkgconfig/wayland-cursor.pc\"\n" "test -f \"$d${out}/lib/pkgconfig/wayland-scanner.pc\"\n" "test -x \"$d${out}/bin/wayland-scanner\"\n" "echo WAYLAND-INSTALLED\n")))))) ;; wayland-protocols is DATA -- XML files and one `.pc` -- so it is ;; installed by hand for the same reason tllist is: running meson to copy ;; a directory tree would add three build inputs to a package that ;; compiles nothing. ;; ;; TWO THINGS HERE ARE NOT COSMETIC. ;; ;; 1. THE `.pc` GOES IN `lib/pkgconfig`, WHERE UPSTREAM PUTS IT IN ;; `share/pkgconfig`. `dependency-pkgconfig` in (sigil build external) ;; composes PKG_CONFIG_PATH as exactly `<dep>/lib/pkgconfig` for each ;; dependency and nothing else. An arch-independent `.pc` in ;; `share/pkgconfig` -- which is the correct place for one, and where ;; upstream's meson puts it -- WOULD BE INVISIBLE TO EVERY CONSUMER IN ;; THIS CHANNEL. A package cannot fix that by declaring a search path ;; either: `PKG_CONFIG_PATH` is builder-owned, and a `search-paths:` ;; export naming a builder variable is refused outright. ;; ;; 2. `pkgdatadir` MUST BE DEFINED IN THE `.pc`, because foot does not ;; merely link against this -- it reads the variable back out ;; (`wayland_protocols.get_variable('pkgdatadir')`, meson.build:169) and ;; then joins protocol paths onto it. Upstream's template writes it as ;; `${pc_sysrootdir}${datarootdir}/@PACKAGE@`; that spelling cannot be ;; used here because sigil interpolates `${...}` in a phase argument ;; before any consumer sees it. An absolute path is equivalent for ;; every use in this channel. (define wayland-protocols (package name: "wayland-protocols" version: "1.45" source: (external-source location: "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/1.45/downloads/wayland-protocols-1.45.tar.xz" sha256: "4d2b2a9e3e099d017dc8107bf1c334d27bb87d9e4aff19a0c8d856d17cd41ef0") buildenv: core-buildenv phases: (list (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "p=\"$d${out}/share/wayland-protocols\"\n" "mkdir -p \"$p\" \"$d${out}/lib/pkgconfig\"\n" "cp -a stable unstable staging \"$p/\"\n" "cat > \"$d${out}/lib/pkgconfig/wayland-protocols.pc\" <<'PC'\n" "prefix=${out}\n" "datarootdir=${out}/share\n" "pkgdatadir=${out}/share/wayland-protocols\n" "\n" "Name: Wayland Protocols\n" "Description: Wayland protocol files\n" "Version: 1.45\n" "PC\n" ;; foot names these three files explicitly, so ;; assert them rather than the directory: a ;; reorganised tarball would otherwise fail three ;; packages later inside wayland-scanner. "test -f \"$p/stable/xdg-shell/xdg-shell.xml\"\n" "test -f \"$p/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml\"\n" "test -f \"$p/unstable/xdg-output/xdg-output-unstable-v1.xml\"\n" ;; And prove the variable foot reads back is ;; actually readable, by the tool that will read ;; it. "PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --variable=pkgdatadir wayland-protocols\n" "echo WAYLAND-PROTOCOLS-INSTALLED\n")))))))) src/core/packages/xml.sglmodified
(core build-environments) (sigil env package) (sigil build external)) (export libxml2) (export libxml2 expat) (begin (define libxml2 (package "--without-http" "--without-modules")) (external-phase kind: 'make) (external-phase kind: 'install)))))) (external-phase kind: 'install)))) ;; expat, the OTHER XML parser in this channel, and it is here rather than ;; folded into libxml2's consumers because two different packages ask for ;; it by name through pkg-config: libwayland (for wayland-scanner, which ;; parses the protocol XML) and fontconfig (for fonts.conf). Neither can ;; use libxml2 instead. ;; ;; MUST SHIP A `.pc` FILE, and this is not incidental. Both consumers find ;; it through PKG_CHECK_MODULES, and a dependency that is found by header ;; probe instead of by pkg-config is how `core/ncurses` poisoned tmux's ;; entire configure -- a probe that tested a header and concluded about a ;; library. expat's autotools build installs `expat.pc` by default; that ;; is asserted below rather than assumed, because "it installs one by ;; default" is exactly the kind of belief that turns out to be false after ;; a build cycle. (define expat (package name: "expat" version: "2.7.1" source: (external-source location: "https://github.com/libexpat/libexpat/releases/download/R_2_7_1/expat-2.7.1.tar.xz" sha256: "354552544b8f99012e5062f7d570ec77f14b412a3ff5c7d8d0dae62c0d217c30") buildenv: core-buildenv phases: (list (external-phase kind: 'configure args: (list "--prefix=${out}" "--disable-shared" "--enable-static" ;; xmlwf is a command-line tool no consumer ;; here uses, and the examples and tests are ;; build-time only. "--without-xmlwf" "--without-examples" "--without-tests" "--without-docbook")) (external-phase kind: 'make) (external-phase kind: 'install) ;; ASSERT THE `.pc` RATHER THAN TRUST IT. If a future expat ;; stops installing one, the failure without this check is ;; not "expat is broken" -- it is fontconfig or wayland ;; quietly configuring itself differently, three packages ;; downstream, with configure still exiting 0. (external-phase kind: 'run args: (list "sh" "-c" (string-append "set -eu\n" "d=\"$(dirname \"$PWD\")/install-root\"\n" "test -f \"$d${out}/lib/pkgconfig/expat.pc\"\n" "echo EXPAT-PC-PRESENT\n"))))))))src/patches/samurai-realloc-zero-length.patchadded
--- a/util.c 2026-08-24 12:58:01.409014074 +0300+++ b/util.c 2026-08-24 12:58:01.414746803 +0300@@ -64,7 +64,14 @@ errno = ENOMEM; return NULL; }- return realloc(p, n * m);+ /* realloc(p, 0) is NOT required to return a non-null pointer, and+ xreallocarray() below treats null as failure. This toolchain's+ libc returns null for realloc(NULL, 0) while malloc(0) returns+ non-null, so deps.c's zero-length save on a fresh build+ directory aborts with a bogus "reallocarray" error. Ask for one+ byte instead; every caller of a zero-length result copies zero+ bytes into it. */+ return realloc(p, n * m ? n * m : 1); } void *