diff --git a/docs/evidencia/sway-headless-soberano-2026-08-26.png b/docs/evidencia/sway-headless-soberano-2026-08-26.png new file mode 100644 index 00000000..1fbab3c9 Binary files /dev/null and b/docs/evidencia/sway-headless-soberano-2026-08-26.png differ diff --git a/docs/state/build-state-wlr.json b/docs/state/build-state-wlr.json index b4c315f4..20c0099c 100644 --- a/docs/state/build-state-wlr.json +++ b/docs/state/build-state-wlr.json @@ -1,14 +1,14 @@ { "schema": "hammer-build-state/1", "totals": { - "recipes": 794, - "nodes": 794, - "sealed": 440, + "recipes": 796, + "nodes": 796, + "sealed": 442, "debt": 352, "never": 2 }, "by_class": { - "c": 166, + "c": 168, "go": 362, "gui": 32, "kernel": 6, @@ -23,7 +23,7 @@ }, "by_queue": { "corpus": { - "sealed": 425, + "sealed": 427, "debt": 352, "never": 2 }, @@ -51,13 +51,13 @@ "debt": 3 }, "escritorio-sway": { - "raices": 58, - "clausura": 125, - "sealed": 123, + "raices": 61, + "clausura": 128, + "sealed": 126, "debt": 2 } }, - "sin_perfil": 654, + "sin_perfil": 653, "orphan_deps": [], "topo_ok": true, "nodes": { @@ -826,7 +826,7 @@ "escritorio-mirada", "escritorio-sway" ], - "dependientes_total": 37 + "dependientes_total": 38 }, "biome": { "iname": "biome", @@ -3495,6 +3495,26 @@ ], "dependientes_total": 70 }, + "expat-shared": { + "iname": "expat-shared", + "version": "2.8.1", + "link": "dynamic", + "compiler": "zig-cc", + "deps": [ + "binutils", + "make" + ], + "cls": "c", + "queue": "corpus", + "hash": "b3:f0f0391200508c52df612dcc85ece1bc0fbddccc16990f61bedb4ee8dd012d20", + "state": "sealed", + "blocked_by": [], + "unblocks": 0, + "perfiles": [ + "escritorio-sway" + ], + "dependientes_total": 0 + }, "eza": { "iname": "eza", "version": "0.23.4", @@ -7884,6 +7904,25 @@ ], "dependientes_total": 115 }, + "libffi-shared": { + "iname": "libffi-shared", + "version": "3.5.2", + "link": "dynamic", + "compiler": "zig-cc", + "deps": [ + "make" + ], + "cls": "c", + "queue": "corpus", + "hash": "b3:2e5ce246a17d19b1d45629a9a485d556db5be40068ae3f618f16fe0ccdc58620", + "state": "sealed", + "blocked_by": [], + "unblocks": 0, + "perfiles": [ + "escritorio-sway" + ], + "dependientes_total": 0 + }, "libgcrypt": { "iname": "libgcrypt", "version": "1.12.2", @@ -8722,7 +8761,7 @@ "escritorio-mirada", "escritorio-sway" ], - "dependientes_total": 111 + "dependientes_total": 113 }, "mako": { "iname": "mako", @@ -14410,7 +14449,9 @@ "state": "sealed", "blocked_by": [], "unblocks": 0, - "perfiles": [], + "perfiles": [ + "escritorio-sway" + ], "dependientes_total": 4 }, "zola": { diff --git a/docs/state/targets.toml b/docs/state/targets.toml index 9efc8656..5dcdec91 100644 --- a/docs/state/targets.toml +++ b/docs/state/targets.toml @@ -185,6 +185,17 @@ paquetes = [ "dejavu-fonts", # sin UNA fuente, foot muere con «monospace::size=8: failed to match font» "xkeyboard-config", # sin datos XKB: «xkbcommon: ERROR: failed to add default include path # /usr/share/X11/xkb», y sway no completa el seat + # ── LAS TRES `.so` QUE sway PIDE Y EL CORPUS SÓLO PRODUCE COMO `.a` ───────────────────────── + # `sway` es `link = "dynamic"` a propósito (dlopea los drivers DRI de mesa) y sale con + # `NEEDED libz.so.1 / libexpat.so.1 / libffi.so.8`. Las canónicas son `--disable-shared`, así que + # ningún artefacto sellado producía esos `.so` y el rootfs hidratado los resolvía contra el + # sysroot Alpine DEL LAB. Peor que una dep faltante: el lab NO entra en `hash_inputs`, así que la + # fuga era invisible para el store — el artefacto se daba por bueno y sólo arrancaba en una + # máquina con Alpine debajo. Los otros nueve NEEDED de sway (pixman, drm, evdev, input, udev, + # wayland-server, wlroots, xkbcommon, libc) sí los cubre la clausura, porque esas recetas ya son + # `link = dynamic`. Como las tres son data de runtime y nadie las declara como dep de BUILD, van + # de raíces por la misma razón que las fuentes y el XKB de arriba. + "zlib-shared", "expat-shared", "libffi-shared", "swaybg", # fondo de escritorio "swaylock", # bloqueo de pantalla (con PAM: linux-pam SÍ está en el corpus) "swayidle", # apagado por inactividad diff --git a/recipes/expat-shared.toml b/recipes/expat-shared.toml new file mode 100644 index 00000000..44fbff94 --- /dev/null +++ b/recipes/expat-shared.toml @@ -0,0 +1,51 @@ +# expat-shared 2.8.1 — variante DINÁMICA de expat. +# +# ── POR QUÉ EXISTE ───────────────────────────────────────────────────────────────────────────── +# El expat canónico (recipes/expat.toml) es `--disable-shared`: sólo `libexpat.a`, que es lo que +# quiere el enlace estático del corpus. Pero `sway` es `link = "dynamic"` A PROPÓSITO —un compositor +# dlopea los drivers DRI de mesa en runtime, así que estático no sirve— y sale con +# `NEEDED libexpat.so.1`. Ese `.so` no lo producía NINGÚN artefacto sellado ⇒ el rootfs hidratado +# caía al `libexpat.so.1` del sysroot Alpine del lab. Es la misma fuga de soberanía que documenta +# `zlib-shared`, y con el agravante de [[lab-fuera-de-hash-inputs]]: el lab NO entra en `hash_inputs`, +# así que la dependencia era invisible para el store — el artefacto se daba por bueno y sólo +# arrancaba en una máquina que tuviera Alpine debajo. +# +# ── POR QUÉ UNA VARIANTE Y NO `link = "dynamic"` EN LA CANÓNICA ──────────────────────────────── +# `link` entra en `hash_inputs`. Tocar `recipes/expat.toml` re-hashearía expat y, en cascada, todo +# lo que lo lista en deps — fontconfig, dbus, mesa, los crates `-sys`… cientos de recetas selladas +# a reconstruir por una lib que ya está bien. El patrón `*-shared` deja intacta la canónica: el +# nombre es DISTINTO (`expat-shared` no pisa a `expat`), así que conviven en el mismo store y cada +# consumidor toma la que necesita. Mismo criterio que zlib-shared / freetype-shared / libpng-shared. +# +# ── EN EL CORPUS, NO EN UNA COLA ─────────────────────────────────────────────────────────────── +# La resolución de deps es hermano→padre: una receta del corpus NO ve las de `incoming-*`. Las +# variantes compartidas genéricas (zlib-shared, fontconfig-shared, freetype-shared, libpng-shared…) +# ya viven acá por esa razón. Ponerla en `incoming-wlr/` la haría invisible para todo lo demás y +# obligaría a una cuarta copia en cuanto otro escritorio la necesite. +# +# ── EL BUILD ES EL DE AUTOTOOLS, SIN EL TRUCO DE zlib ────────────────────────────────────────── +# zlib-shared fabrica su `.so` a mano con `--whole-archive` porque el configure de zlib aborta bajo +# zig cc. Acá no hace falta: el configure de expat acepta `--enable-shared` y libtool emite el +# `libexpat.so.1` con su SONAME correcto. Se cae el `-all-static -no-pie` de la canónica, que es +# justamente lo que impide producir una compartida. + +name = "expat-shared" +version = "2.8.1" +license = "MIT" + +[source] +tarball = "https://github.com/libexpat/libexpat/releases/download/R_2_8_1/expat-2.8.1.tar.xz" +sha256 = "10b195ee78160a908388180a8fe3603d4e9a12f4755fbf5f3816b23a9d750da0" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" + +[deps] +build = ["binutils", "make"] + +[build.phases] +configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --enable-shared --disable-static --with-pic --without-docbook' +compile = 'make -j"$(nproc)"' +install = 'make DESTDIR=/out install' diff --git a/recipes/libffi-shared.toml b/recipes/libffi-shared.toml new file mode 100644 index 00000000..1c029c93 --- /dev/null +++ b/recipes/libffi-shared.toml @@ -0,0 +1,39 @@ +# libffi-shared 3.5.2 — variante DINÁMICA de libffi. +# +# Misma razón que `expat-shared` y `zlib-shared`, y el mismo trío: `sway` es `link = "dynamic"` por +# diseño y sale con `NEEDED libffi.so.8`, que ningún artefacto sellado producía. El canónico +# (recipes/libffi.toml) es `--disable-shared` porque su consumidor principal es el enlace estático +# (python3, los crates `-sys`). Sin esta variante el rootfs hidratado resolvía `libffi.so.8` contra +# el sysroot Alpine del lab — fuga de soberanía invisible para el store, porque el lab no entra en +# `hash_inputs`. +# +# NO se toca `link` en la canónica: entra en `hash_inputs` y re-hashearía libffi y todo lo que lo +# lista en deps (glib, python3, mesa, wayland…). El nombre `libffi-shared` es distinto del canónico +# ⇒ conviven en el store sin pisarse. Va al CORPUS, no a `incoming-wlr/`, porque la resolución de +# deps es hermano→padre y una receta del corpus no vería una cola. +# +# `--disable-exec-static-tramp` se conserva de la canónica: los trampolines estáticos ejecutables de +# libffi 3.5 asumen un layout de memoria que musl no garantiza. Se cae `--disable-shared` y con él el +# motivo por el que no había `.so`; el resto del configure es idéntico, a propósito, para que las dos +# variantes sigan siendo comparables de un vistazo. + +name = "libffi-shared" +version = "3.5.2" +license = "MIT" + +[source] +tarball = "https://github.com/libffi/libffi/releases/download/v3.5.2/libffi-3.5.2.tar.gz" +sha256 = "f3a3082a23b37c293a4fcd1053147b371f2ff91fa7ea1b2a52e335676bac82dc" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" + +[deps] +build = ["make"] + +[build.phases] +configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --enable-shared --disable-static --with-pic --disable-exec-static-tramp' +compile = 'make -j"$(nproc)"' +install = 'make DESTDIR=/out install'