From b2ff678301a4bcebc7693b5ae6702d5fee3d0ccc Mon Sep 17 00:00:00 2001 From: Sergio Date: Sun, 6 Sep 2026 22:27:59 +0000 Subject: [PATCH] readline-shared: la .so que python3 y sqlite piden (b3:79355aad) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libreadline.so.8, 970.144 bytes. Hermana de ncurses-shared y por la misma fuga: python3 y sqlite-shared salen con NEEDED libreadline.so.8 y la readline del catálogo es --disable-shared, así que sólo produce libreadline.a. En el rootfs hidratado eso se resolvía contra el sysroot Alpine DEL LAB. Versión 8.2 y no la 8.3.3 de Alpine, a propósito: es la que ya usa recipes/readline.toml y tener las dos variantes en versiones distintas es pedir que diverjan sin que nadie lo note. TRES GUARDIANES, y el segundo es de Alpine casi literal. readline sale MAL ENLAZADA por diseño de upstream —libreadline.so no enlaza contra ncurses salvo que se lo fuerces, y queda con tgetent y compañía sin resolver—; Alpine lo arregla con un parche y con un check que falla si el NEEDED de ncurses no aparece. Acá se consigue sin parche con SHLIB_LIBS=-lncursesw, que es la perilla que el propio Makefile expone. El guardián verifica que quedó enlazada, porque si no el fallo reaparece en el consumidor, a un eslabón de distancia y sin nombrar a esta receta. La dep es ncurses-shared y no ncurses: con la estática, libreadline.so.8 se llevaría los objetos de libncursesw.a dentro en vez de declarar el NEEDED — la misma lección que el cairo estático de waterfox. Se promueve además sqlite-shared de incoming-gnome al catálogo canónico: python3 la necesita como dep de ejecución y una receta de recipes/ no ve las de una cola. Sin variantes en conflicto y ya sellada, así que la promoción no cuesta un build. --- recipes/readline-shared.toml | 85 ++++++++++++++++++++++++++++++++++++ recipes/sqlite-shared.toml | 55 +++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 recipes/readline-shared.toml create mode 100644 recipes/sqlite-shared.toml diff --git a/recipes/readline-shared.toml b/recipes/readline-shared.toml new file mode 100644 index 00000000..10cd2afa --- /dev/null +++ b/recipes/readline-shared.toml @@ -0,0 +1,85 @@ +# readline-shared 8.2 — la MISMA readline de `recipes/readline.toml` pero `.so`. Hermana de +# `ncurses-shared`, y existe por la misma clase de fuga. +# +# ══ POR QUÉ HACE FALTA ═════════════════════════════════════════════════════════════════════════ +# `python3` y `sqlite-shared` salen con `NEEDED libreadline.so.8` y NINGÚN artefacto del cierre +# publica ese soname: la readline del catálogo es `--disable-shared`, así que sólo produce +# `libreadline.a`. En el rootfs hidratado eso se resuelve contra el sysroot Alpine DEL LAB, que en +# la máquina de desarrollo está y en la distro NO. Lo venía reportando `scripts/vigia-sonames.py` +# en los CUATRO perfiles, junto a `libstdc++.so.6` — que se pagó el 2026-09-06 con `gcc-libs`. +# +# No sustituye a `recipes/readline.toml`: quien quiera enlazar estático sigue usando aquélla. Es el +# mismo reparto que `ncurses` / `ncurses-shared`. +# +# ⚠ VERSIÓN 8.2 Y NO LA 8.3.3 DE ALPINE, A PROPÓSITO. El soname que piden los consumidores YA +# SELLADOS es `libreadline.so.8`, y las dos lo dan; pero la 8.2 es la que ya usa `readline.toml`, y +# tener las dos variantes en versiones distintas es pedir que diverjan sin que nadie lo note. Si se +# sube una, se suben las dos. +# +# ⚠ EL SUBRAYADO: READLINE SALE MAL ENLAZADA POR DISEÑO DE UPSTREAM. `libreadline.so` no enlaza +# contra ncurses salvo que se lo fuerces, y queda con `tgetent`, `tputs` y compañía SIN RESOLVER. +# Alpine lo arregla con un parche y —esto es lo que se copia— con un guardián que FALLA si el +# `NEEDED` de ncurses no aparece. Acá se consigue lo mismo sin parche, pasándole `SHLIB_LIBS` al +# make, que es la perilla que el propio Makefile de readline expone para esto. +name = "readline-shared" +version = "8.2" +license = "GPL-3.0-or-later" + +[source] +tarball = "https://ftp.gnu.org/gnu/readline/readline-8.2.tar.gz" +sha256 = "3feb7171f16a84ee82ca18a36d7b9be109a52c04f492a053331d7d1095007c35" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +# El punto de la receta: hammer inyecta `LDFLAGS=-static` en modo estático y acá el producto ES la +# librería compartida. Igual que en `gcc-libs` y `ncurses-shared`. +link = "dynamic" + +[deps] +# `ncurses-shared` y no `ncurses`: si acá entrara la estática, `libreadline.so.8` se llevaría los +# objetos de `libncursesw.a` dentro en vez de declarar el `NEEDED`, y el guardián de abajo —que es +# el de Alpine— lo cazaría. Es la misma lección que el cairo estático de waterfox. +build = ["ncurses-shared", "make"] + +[build.phases] +configure = "./configure --prefix=/usr --enable-shared --disable-static --with-curses" +# `SHLIB_LIBS` es la perilla del Makefile de readline para lo que Alpine resuelve con un parche. +compile = 'make SHLIB_LIBS=-lncursesw' +install = ''' +set -e +make install DESTDIR=/out SHLIB_LIBS=-lncursesw + +# La readline trae `rlconf.h` y binarios de ejemplo que no hacen falta para satisfacer un NEEDED. +rm -rf /out/usr/bin /out/usr/share + +# ── GUARDIÁN 1: el soname que los consumidores piden ───────────────────────────────────────── +F=$(find /out/usr/lib -name 'libreadline.so.8*' | head -1) +test -n "$F" || { echo "!! no se generó libreadline.so.8 — ¿--enable-shared no tomó?" >&2; ls -l /out/usr/lib >&2; exit 1; } +SO=$(readelf -d "$F" 2>/dev/null | sed -n 's/.*SONAME.*\[\(.*\)\].*/\1/p') +test "$SO" = "libreadline.so.8" || { echo "!! SONAME es '$SO' y esperaba 'libreadline.so.8'" >&2; exit 1; } +echo "guardián: libreadline.so.8 ok (SONAME correcto, $(stat -Lc%s "$F") bytes)" + +# ── GUARDIÁN 2: EL DE ALPINE — que no esté SUBENLAZADA ─────────────────────────────────────── +# Copiado de su APKBUILD casi literal («verify that it is not underlinked as upstream designed +# it»). Sin esto la librería existe, tiene el soname correcto y sus símbolos de terminal quedan +# colgando: el fallo reaparecería en el consumidor, a un eslabón de distancia y sin nombrar a esta +# receta. Es exactamente la forma de fallo que esta receta viene a cerrar, así que dejarla sin +# comprobar sería no haber aprendido nada. +readelf -d "$F" 2>/dev/null | grep -q 'NEEDED.*ncurses' || { + echo "!! libreadline.so.8 NO está enlazada contra ncurses (subenlazada)" >&2 + echo " readline hace esto por diseño de upstream; revisá SHLIB_LIBS=-lncursesw." >&2 + readelf -d "$F" 2>/dev/null | grep NEEDED >&2 + exit 1 +} +echo "guardián: enlazada contra ncurses ($(readelf -d "$F" | sed -n 's/.*NEEDED.*\[\(.*ncurses.*\)\].*/\1/p'))" + +# ── GUARDIÁN 3: la prueba del consumidor ───────────────────────────────────────────────────── +# Los símbolos que python3 y sqlite buscan de verdad. «Existe» no es «sirve». +falta="" +for s in readline add_history rl_completion_matches; do + nm -D --defined-only "$F" 2>/dev/null | grep -qE " $s(@|\$)" || falta="$falta $s" +done +test -z "$falta" || { echo "!! la .so no define:$falta" >&2; exit 1; } +echo "guardián: define los símbolos que piden python3 y sqlite" +''' diff --git a/recipes/sqlite-shared.toml b/recipes/sqlite-shared.toml new file mode 100644 index 00000000..40c3e6a3 --- /dev/null +++ b/recipes/sqlite-shared.toml @@ -0,0 +1,55 @@ +# ── ⚠ VERRUGA MEDIDA (2026-09-05), sin decidir ───────────────────────────────────────────────── +# `usr/bin/sqlite3` —el shell de la CLI, NO `libsqlite3.so.0`— sale con `NEEDED libreadline.so.8`, y +# el `readline` canónico del corpus es `.a`. O sea que en la imagen GNOME ese comando no carga: lo +# resolvía contra el sysroot Alpine DEL LAB. Lo cazó `scripts/vigia-sonames.py`; las APPS están bien, +# porque la librería no lo lleva. +# +# Las dos salidas cuestan cosas distintas y por eso no se decide de paso: +# · construir `readline-shared` y sumarla de raíz a `escritorio-gnome` — 1 build nuevo, pero la +# imagen carga una librería más sólo para que ande un comando. (`ncurses-shared` ya es raíz del +# perfil, así que la cadena cierra.) +# · configurar acá `--disable-readline` y no instalar una CLI que nadie pidió — más limpio, pero +# re-hashea: `yupana radio sqlite-shared` da **6 dependientes en incoming-gnome** que caen a +# deuda. +# Se paga barato el día que sqlite-shared se re-selle por otro motivo. + +# sqlite-shared 3.46.1 — variante DINÁMICA de sqlite para la isla dinámica de GNOME. +# +# Por qué existe: el sqlite canónico (recipes/sqlite.toml) es `--disable-shared --enable-static` y su +# libsqlite3.a NO es PIC (verificado: trae reubicaciones R_X86_64_32). libsoup 3.6 lo pide DURO +# (meson.build:123-136 cae a `dependency('sqlite3')` sin `required:false`) y libsoup es un .so ⇒ +# meter el .a no-PIC adentro corta con el mismo `relocation R_X86_64_32 ... recompile with -fPIC` +# que frenó a mutter contra freetype. Mismo patrón y misma solución que zlib-shared/cairo-shared: +# una variante hermana, sin tocar la canónica (su radio son cientos de sellados). +# +# El único cambio real frente a la canónica es --enable-shared --disable-static y sacar el +# `-all-static -no-pie` de LDFLAGS, que es justo lo que impide producir un .so. Misma versión y mismo +# tarball a propósito: el sqlite3.pc que consume libsoup declara la misma versión que el resto del +# corpus, así que no se abre un skew de versiones entre el mundo estático y el dinámico. +name = "sqlite-shared" +version = "3.46.1" + +[source] +tarball = "https://sqlite.org/2024/sqlite-autoconf-3460100.tar.gz" +sha256 = "67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" + +[build.phases] +configure = "./configure --prefix=/usr --enable-shared --disable-static" +compile = 'make -j"$(nproc)"' +install = "make install DESTDIR=/out" + +[deps] +# ✅ DECIDIDA 2026-09-06, y la decisión cambió porque cambió el cálculo. La verruga de arriba dejaba +# dos salidas sin elegir, y el argumento contra la primera era «la imagen carga una librería más sólo +# para que ande un comando». Ya no es cierto: `python3` sale con el MISMO `NEEDED libreadline.so.8` +# y es raíz de los cuatro perfiles, así que `readline-shared` entra en la clausura igual. El coste +# marginal de arreglar además el `sqlite3` de la CLI es CERO. +# `runtime` no entra en el ArtifactHash ⇒ no re-hashea, y los 6 dependientes que menciona la nota de +# arriba no se tocan. Esa era la otra mitad del precio y tampoco se paga. +runtime = ["readline-shared"] +build = ["make", "pkgconf"]