Commit828e9705Recorded24 Aug 2026Repositorycore-channel

Add swayimg, unbuilt, and record the GitHub two-endpoint finding

Message

swayimg costs exactly ONE new library and that one is LuaJIT. Every other dependency -- libxkbcommon, fontconfig, freetype, wayland, wayland-protocols, libpng -- already existed because foot needed them, and all nine wayland-protocols XML files it names are present in the 1.45 packaged here. The Wayland half of this channel is reusable rather than foot-shaped, which is the same thing core/grim demonstrated.

NOT BUILT. Publishing is required to build a new package, and publishing is blocked by the channel evaluation CPU budget (t-cc5d27). The definition is committed so the work is not lost and so the provenance reasoning is recorded where it belongs.

Two findings at the site:

  • luajit is mandatory and it is LuaJIT, not Lua: meson.build:38, under a comment reading # mandatory dependencies, no required: kwarg, no option. An earlier estimate of this package's cost read dependency(get_option('liblua')) from upstream MASTER and concluded plain Lua would do. Read the version you are packaging, not the branch.
  • THE HASH IS A PROPERTY OF THE ENDPOINT, NOT OF THE TAG. The first build refused on a source hash mismatch -- my error, one URL hashed and another pinned -- but the two differ because GitHub serves the same tag as two different artifacts: api.github.com/.../tarball/TAG roots the tree at artemsen-swayimg-682fecf/, codeload.../tar.gz/TAG at swayimg-5.5/. Identical content once the prefix is stripped, verified by extracting both and diffing; different bytes; and each endpoint individually stable, so this is not regeneration. There is simply no canonical byte stream for a tag that ships no release artifact. That is the argument against synthetic archives stated more precisely than "they can change".

Both this and core/luajit carry the same scoped-exception note: they are a discovery exercise whose product is knowledge about the packaging model, not a published dependency, and NEITHER SHOULD BE PUBLISHED OR SIGNED ON THIS PROVENANCE without revisiting the question.

Changed
 channel.sgl                  |   6 ++++--
 src/core/packages/viewer.sgl | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+), 2 deletions(-)
Diff
channel.sglmodified
@@ -17,7 +17,8 @@
17
(core packages wayland)
18
(core packages input)
19
(core packages capture)
−20
(core packages luajit))
+20
(core packages luajit)
+21
(core packages viewer))
22
23
(channel
24
stream: "core-stable"
@@ -53,4 +54,5 @@
54
(channel-binding name: "core/vim" value: vim)
55
(channel-binding name: "core/emacs" value: emacs)
56
(channel-binding name: "core/python" value: python)
−56
(channel-binding name: "core/luajit" value: luajit)))
+57
(channel-binding name: "core/luajit" value: luajit)
+58
(channel-binding name: "core/swayimg" value: swayimg)))
src/core/packages/viewer.sgladded
@@ -0,0 +1,156 @@
+1
;;; Image viewing.
+2
;;;
+3
;;; PROVENANCE: THE SAME SCOPED EXCEPTION AS `core/luajit`, AND FOR THE SAME
+4
;;; REASON. swayimg publishes NO release assets -- only GitHub's
+5
;;; auto-generated source archive, which is what every distribution pins. A
+6
;;; synthetic archive whose bytes can be regenerated does not belong on the
+7
;;; critical path of a chain intended to SHIP.
+8
;;;
+9
;;; This is not that. `core/swayimg` and `core/luajit` exist as a DISCOVERY
+10
;;; EXERCISE whose product is knowledge about what this packaging model can
+11
;;; host -- where the blind spots are -- rather than a published dependency.
+12
;;; Two auto-generated archives are acceptable for that purpose and would not
+13
;;; be acceptable for a shipped chain. NEITHER PACKAGE SHOULD BE PUBLISHED OR
+14
;;; SIGNED ON THIS PROVENANCE WITHOUT REVISITING THE QUESTION.
+15
;;;
+16
;;; AND A CONCRETE DEMONSTRATION OF WHY, MET WHILE WRITING THIS PACKAGE.
+17
;;; The first build refused:
+18
;;;
+19
;;; REFUSING source sha256 mismatch; expected 64ae8713... actual f84fec72...
+20
;;;
+21
;;; That was my error -- I hashed one URL and pinned another -- but the reason
+22
;;; the two hashes differ is the point. GitHub serves the SAME TAG from two
+23
;;; endpoints and they are not the same artifact:
+24
;;;
+25
;;; api.github.com/repos/OWNER/REPO/tarball/TAG -> artemsen-swayimg-682fecf/
+26
;;; codeload.github.com/OWNER/REPO/tar.gz/TAG -> swayimg-5.5/
+27
;;;
+28
;;; Identical CONTENT once the prefix is stripped -- verified by extracting
+29
;;; both and diffing -- but a different embedded top-level directory name, so
+30
;;; different bytes and different hashes. Each endpoint is individually
+31
;;; stable (the api one was fetched twice and matched), so this is not
+32
;;; regeneration. It is that THERE IS NO SINGLE CANONICAL BYTE STREAM for a
+33
;;; tag that ships no release artifact.
+34
;;;
+35
;;; A real dist tarball has one answer to "what are the bytes". This has two,
+36
;;; and nothing upstream declares which is meant. That is the argument
+37
;;; against synthetic archives stated more precisely than "they can change".
+38
;;;
+39
;;; WHAT IT COST, which is the interesting number: ONE new library, and that
+40
;;; one is LuaJIT. Every other dependency swayimg has -- libxkbcommon,
+41
;;; fontconfig, freetype, wayland, wayland-protocols, libpng -- already existed
+42
;;; because foot needed them. The Wayland half of this channel turns out to be
+43
;;; reusable rather than foot-shaped, which is the same thing `core/grim`
+44
;;; demonstrated and is worth more than either program.
+45
(define-library (core packages viewer)
+46
(import (sigil core)
+47
(core build-environments)
+48
(core packages compression)
+49
(core packages xml)
+50
(core packages ffi)
+51
(core packages graphics)
+52
(core packages fonts)
+53
(core packages wayland)
+54
(core packages input)
+55
(core packages luajit)
+56
(core packages build-tools)
+57
(core packages python)
+58
(sigil env package)
+59
(sigil build external))
+60
(export swayimg)
+61
(begin
+62
;; LUAJIT IS NOT OPTIONAL, and it is LuaJIT rather than Lua.
+63
;; meson.build:38 is `dependency('luajit')` under a comment reading
+64
;; `# mandatory dependencies`, with no `required:` kwarg and no
+65
;; corresponding entry in meson.options. An earlier estimate of this
+66
;; package's cost read `dependency(get_option('liblua'))` from upstream
+67
;; MASTER and concluded plain Lua would do; the RELEASE hardcodes luajit.
+68
;; Read the version you are packaging, not the branch.
+69
;;
+70
;; expat, zlib, libffi and freetype are declared although swayimg names
+71
;; none of them: they are reached through the `Requires:` and
+72
;; `Requires.private:` chains of fontconfig.pc, freetype2.pc, libpng.pc and
+73
;; wayland-client.pc, and this channel composes PKG_CONFIG_PATH from DIRECT
+74
;; dependencies only. The union was printed from the store before writing
+75
;; this line rather than discovered one build failure at a time.
+76
;; [[topics/core-channel-pkgconfig-is-not-transitive]]
+77
(define swayimg
+78
(package
+79
name: "swayimg"
+80
version: "5.5"
+81
source: (external-source
+82
location: "https://codeload.github.com/artemsen/swayimg/tar.gz/v5.5"
+83
sha256: "f84fec722cd0b4cfd86d0a8a820b15a95bbdb1d2cfcded7f67446a7a0c1d1c09")
+84
buildenv: core-buildenv
+85
dependencies: (list libxkbcommon fontconfig freetype wayland
+86
wayland-protocols libpng luajit
+87
expat zlib libffi)
+88
build-inputs: (list meson samurai python)
+89
phases: (list
+90
(external-phase
+91
kind: 'run
+92
args: (list "sh" "-c"
+93
(string-append
+94
"set -eu\n"
+95
"meson setup build"
+96
" --prefix=${out}"
+97
" --buildtype=plain"
+98
" --default-library=static"
+99
;; fontconfig.pc carries `Requires.private: expat`,
+100
;; and swayimg links an executable, so the static
+101
;; pkg-config query is required or the link fails
+102
;; on XML_* symbols. Same lesson as foot.
+103
" -Dprefer_static=true"
+104
;; PNG only. Every other decoder is a feature
+105
;; option defaulting to `auto`, which would make
+106
;; the output depend on what happened to be
+107
;; visible -- the thing this channel exists to
+108
;; prevent. Turning them off explicitly is what
+109
;; makes the result a function of the declared
+110
;; inputs.
+111
" -Dpng=enabled"
+112
" -Dexr=disabled -Dgif=disabled -Dheif=disabled"
+113
" -Davif=disabled -Djpeg=disabled -Djp2=disabled"
+114
" -Djxl=disabled -Dsvg=disabled -Dtiff=disabled"
+115
" -Dsixel=disabled -Draw=disabled -Dwebp=disabled"
+116
" -Dexif=disabled"
+117
;; Wayland yes, DRM no: the DRM backend wants
+118
;; libdrm and a console, and the acceptance case
+119
;; is a Wayland compositor.
+120
" -Dwayland=enabled -Ddrm=disabled"
+121
;; Completions want bash-completion and zsh; the
+122
;; man page wants scdoc. None is packaged and
+123
;; none is read by anything here.
+124
" -Dbash=disabled -Dzsh=disabled"
+125
" -Dman=false -Ddoc=false -Ddesktop=false"
+126
" -Dluameta=false"
+127
;; The version option exists because swayimg
+128
;; otherwise derives it from `git`, which is not
+129
;; in the seed and would leave it at 0.0.0.
+130
" -Dversion=5.5\n"
+131
"echo SWAYIMG-CONFIGURED\n")))
+132
(external-phase
+133
kind: 'run
+134
args: (list "sh" "-c"
+135
(string-append
+136
"set -eu\n"
+137
"meson compile -C build\n"
+138
"echo SWAYIMG-COMPILED\n")))
+139
(external-phase
+140
kind: 'run
+141
args: (list "sh" "-c"
+142
(string-append
+143
"set -eu\n"
+144
"d=\"$(dirname \"$PWD\")/install-root\"\n"
+145
"DESTDIR=\"$d\" meson install -C build --no-rebuild\n"
+146
"test -x \"$d${out}/bin/swayimg\"\n"
+147
;; ASSERT PNG SUPPORT IS ACTUALLY IN, not merely
+148
;; requested. `-Dpng=enabled` makes meson FAIL if
+149
;; libpng is missing, so this is belt-and-braces
+150
;; rather than the only guard -- but a viewer that
+151
;; installs and cannot decode the one format it
+152
;; was built for is exactly the silent
+153
;; feature-loss this channel keeps finding, and
+154
;; the binary is the only thing that can answer it.
+155
"\"$d${out}/bin/swayimg\" --version\n"
+156
"echo SWAYIMG-INSTALLED\n"))))))))