# 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: takana 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" '''