Commit6aa0059aRecorded24 Aug 2026Repositorycore-channel

Package foot and its Wayland/font chain, and the channel's first meson build

Message

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.

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