From 03ec0e6827fe967025ca52f730c5f6252435e327 Mon Sep 17 00:00:00 2001 From: sergio Date: Tue, 4 Aug 2026 04:47:55 -0400 Subject: [PATCH] =?UTF-8?q?cosmic:=20libqalculate=20+=20gmp=20+=20mpfr=20?= =?UTF-8?q?=E2=80=94=20qalc=20anda,=20y=20el=20plugin=20del=20lanzador=20i?= =?UTF-8?q?gual=20no=20lo=20puede=20usar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Las tres selladas a la primera (gmp b3:d99a0a5d, mpfr b3:13982d20, libqalculate b3:6bb435a7). La cadena entera desde una tecla está en el catálogo: Super → cosmic-launcher → pop-launcher → plugin calc → qalc → mpfr → gmp. Cola 30/30, escritorio-cosmic 68/68. qalc FUNCIONA: -t '2+2' da 4, -f fichero anda, e interactivo sobre terminal calcula. Pero el plugin lo invoca SIN expresión, con stdin en tubería, le escribe la cuenta y cierra — y en ese modo nuestro binario no lee nada. Acotado a que, con el EOF ya pendiente en un stdin que no es terminal, readline() devuelve NULL antes de entregar la línea que sí está en el búfer. Descartado midiendo, para no repetirlo: no es el toolchain (una sonda estática musl del sandbox lee la tubería en C y en C++); no es readline vs no-readline (sin ella es peor: no lee nunca); no es la versión de readline (una 8.3 sombra no movió el síntoma, y se descartó en vez de dejar una receta duplicada inútil); y no es el descriptor 0, porque Could not open "/dev/stdin". —el mismo pipe reabierto por ruta— sí funciona. El que no sirve es el FILE* stdin, y ahí queda la pista. Dos gotchas del empaquetado: gmp va con --disable-assembly (elige rutinas mirando la CPU de quien compila, o sea hornearía la ISA del laptop en el hash), y el configure de libqalculate no encuentra readline porque prueba -lncurses/-lcurses/-ltinfo y el corpus sólo empaqueta libncursesw.a — los alias van en un directorio local, no ensuciando /usr/lib, que es la evidencia de qué se declaró. Co-Authored-By: Claude Opus 5 (1M context) --- docs/runbooks/cosmic-desktop.md | 37 ++++++++++- docs/state/build-state-cosmic.json | 29 ++++----- docs/state/targets.toml | 3 + recipes/incoming-cosmic/gmp.toml | 45 ++++++++++++++ recipes/incoming-cosmic/libqalculate.toml | 76 +++++++++++++++++++++++ recipes/incoming-cosmic/mpfr.toml | 37 +++++++++++ scripts/cosmic/hydrate-cosmic.sh | 3 + 7 files changed, 213 insertions(+), 17 deletions(-) create mode 100644 recipes/incoming-cosmic/gmp.toml create mode 100644 recipes/incoming-cosmic/libqalculate.toml create mode 100644 recipes/incoming-cosmic/mpfr.toml diff --git a/docs/runbooks/cosmic-desktop.md b/docs/runbooks/cosmic-desktop.md index c315a3ce..08efe24b 100644 --- a/docs/runbooks/cosmic-desktop.md +++ b/docs/runbooks/cosmic-desktop.md @@ -107,8 +107,10 @@ Más `cosmic-settings-daemon` y `xdg-desktop-portal-cosmic` fuera de esa lista. | `cosmic-workspaces-epoch` | ✅ sellado (necesitó `mesa`, ver gotcha 11) | `b3:a031249e` | | **`cosmic-applets`** (19 binarios en uno) | ✅ **sellado** (necesitó `dbus-shared`, ver gotcha 10) | `b3:3e53a325` | | `pop-launcher` 1.2.7 (otro repo, ver abajo) | ✅ sellado a la primera | `b3:64a5cbaa` | +| `gmp` 6.3.0 · `mpfr` 4.2.2 (cadena de la calculadora) | ✅ sellados a la primera | `b3:d99a0a5d` · `b3:13982d20` | +| `libqalculate` 5.12.0 (`qalc`) | ✅ sellado — **pero el plugin del lanzador no lo puede usar, ver abajo** | `b3:6bb435a7` | -**La cola está COMPLETA: 27 recetas, `escritorio-cosmic` 63/63.** +**La cola está COMPLETA: 30 recetas, `escritorio-cosmic` 68/68.** **El mínimo viable de una sesión son cuatro**: `cosmic-comp` + `cosmic-settings-daemon` + `cosmic-notifications` + `cosmic-panel` (los tres `.expect` de la tabla de arriba). @@ -479,6 +481,39 @@ plugin, que contesta «qalc command is not installed»**. Esa respuesta es la me disponible: el proceso hijo corrió, evaluó la consulta y devolvió una fila. Lo que falta ahora es `libqalculate` (el binario `qalc`), que es un paquete, no un problema. +### 🧮 `libqalculate`: empaquetado y andando, e inservible para el plugin + +`qalc` está sellado y **funciona**: `qalc -t '2+2'` → `4`, `-f fichero`, y el modo interactivo sobre +una terminal calcula perfecto (`123 × 456 = 56088`). La cadena que baja desde una tecla —`Super` → +cosmic-launcher → pop-launcher → plugin `calc` → `qalc` → **mpfr → gmp**— está entera en el catálogo. + +**Y aun así el lanzador sigue mostrando una fila vacía.** El plugin no le pasa la cuenta por argv: +arranca `qalc` SIN expresión, con stdin y stdout en tubería, le ESCRIBE la expresión y cierra. Ese +modo, en nuestro binario, no lee nada. Acotado con dos invocaciones que sólo difieren en cuándo llega +el EOF: + +```sh +{ printf '123*456\n'; sleep 3; } | qalc # LEE (aparece la pregunta de primer arranque) + printf '123*456\n' | qalc # NO lee NADA: imprime el prompt y sale con rc=0 +``` + +O sea: **con el EOF ya pendiente en un stdin que no es terminal, `readline()` devuelve NULL antes de +entregar la línea que sí está en el búfer** — y así es exactamente como lo invoca el plugin. + +Lo que ya se DESCARTÓ midiendo, para que nadie lo repita: +- **No es el toolchain.** Una sonda estática musl del propio sandbox (C y C++) lee la tubería sin + problema. El binario no tiene nada raro: un solo `NEEDED`, `libc.so`. +- **No es readline vs no-readline.** Sin readline es peor (no lee nunca, ni con la tubería abierta); + con readline al menos lee cuando el escritor no ha cerrado. +- **No es la versión de readline.** Se construyó una 8.3 como sombra de cola y el síntoma no se + movió; la sombra se descartó en vez de dejar una receta duplicada que no arregla nada. +- **No es el descriptor 0.** `qalc -f /dev/stdin` —el MISMO pipe, reabierto por ruta— sí funciona. + El que no sirve es el `FILE*` `stdin`. + +La pista que queda es justamente esa última asimetría. Y el aviso de método: **empaquetar el binario +que falta no garantiza que el consumidor lo pueda usar** — «qalc no está instalado» y «qalc está y no +contesta» son dos estados distintos, y el segundo se ve peor porque la fila sale en blanco. + ### ⏱ El panel tiene un DEADLINE de arranque: con 4 vCPU sale mutilado Medido: con `-smp 4` el panel arranca **sin ala izquierda, sin reloj y sin dock** —sólo la bandeja diff --git a/docs/state/build-state-cosmic.json b/docs/state/build-state-cosmic.json index 556898d4..9c5bfd95 100644 --- a/docs/state/build-state-cosmic.json +++ b/docs/state/build-state-cosmic.json @@ -3,9 +3,8 @@ "totals": { "recipes": 800, "nodes": 800, - "sealed": 797, - "never": 2, - "debt": 1 + "sealed": 798, + "never": 2 }, "by_class": { "c": 160, @@ -15,7 +14,7 @@ "rust": 241 }, "debt_by_class": { - "c": 3 + "c": 2 }, "by_queue": { "corpus": { @@ -23,8 +22,7 @@ "never": 2 }, "incoming-cosmic": { - "sealed": 29, - "debt": 1 + "sealed": 30 } }, "by_profile": { @@ -41,8 +39,7 @@ "escritorio-cosmic": { "raices": 20, "clausura": 68, - "sealed": 67, - "debt": 1 + "sealed": 68 }, "escritorio-mirada": { "raices": 14, @@ -4898,7 +4895,7 @@ "hash": "b3:d99a0a5d6fb0e301523c3797d899e7c1002c58f282865df87e221b4e47fd0642", "state": "sealed", "blocked_by": [], - "unblocks": 1, + "unblocks": 0, "perfiles": [ "escritorio-cosmic" ], @@ -8229,7 +8226,7 @@ "cls": "c", "queue": "incoming-cosmic", "hash": "b3:6bb435a7e54f9b83eb44133b75cd684161da7a03135a44ee22063bdba687b88c", - "state": "debt", + "state": "sealed", "blocked_by": [], "unblocks": 0, "perfiles": [ @@ -8452,7 +8449,7 @@ "hash": "b3:c4c65c4b17b86211c4a7f076e6693aedb9168d72cc8e45a4a9022fdd75f79494", "state": "sealed", "blocked_by": [], - "unblocks": 1, + "unblocks": 0, "perfiles": [ "escritorio-cosmic", "escritorio-gnome" @@ -8804,7 +8801,7 @@ "hash": "b3:fbad44acdc76df4b081cccd3cc02666664b6d1a03cd6fa482f85791fa533a602", "state": "sealed", "blocked_by": [], - "unblocks": 2, + "unblocks": 1, "perfiles": [ "base", "cli", @@ -9471,7 +9468,7 @@ "hash": "b3:13982d20e404affb9ab825c157a50efc2f0370e1befe75cb7ce93b914d55d497", "state": "sealed", "blocked_by": [], - "unblocks": 1, + "unblocks": 0, "perfiles": [ "escritorio-cosmic" ], @@ -9695,7 +9692,7 @@ "hash": "b3:a3be97622d20cd8d831a25403e00eccb23f2585ed78670a4e12d6cbe519fb8ec", "state": "sealed", "blocked_by": [], - "unblocks": 1, + "unblocks": 0, "perfiles": [ "base", "cli", @@ -10617,7 +10614,7 @@ "hash": "b3:f0b5d0b1cb4321b3c61f24a871b9a58da3ebb7eb903af4015e35bd41a6465437", "state": "sealed", "blocked_by": [], - "unblocks": 1, + "unblocks": 0, "perfiles": [ "base", "cli", @@ -11053,7 +11050,7 @@ "hash": "b3:03c0eb9ccaf42a65d876c0778b7ee53695f6389bbe5c4c9bcf606f8aa328ec36", "state": "sealed", "blocked_by": [], - "unblocks": 1, + "unblocks": 0, "perfiles": [ "base", "cli", diff --git a/docs/state/targets.toml b/docs/state/targets.toml index 956f1728..e27675c8 100644 --- a/docs/state/targets.toml +++ b/docs/state/targets.toml @@ -104,6 +104,9 @@ paquetes = [ # El MOTOR del lanzador, de otro repo y fuera del pin `epoch-N`: `cosmic-launcher` sólo dibuja y # lanza a `pop-launcher` como proceso hijo por PATH. Sin él, `Super` no abre nada. "pop-launcher", + # Y el `qalc` al que el plugin `calc` le pasa la expresión: tercer nivel de hijo por PATH. Es la + # primera raíz del perfil que existe para que una TECLA dé un resultado, no para que algo enlace. + "libqalculate", # Datos, no binarios: el tema de iconos (y el hicolor del que hereda), las tablas de xkb y una # tipografía. Ninguno es dep de build de nadie y sin ellos el escritorio arranca roto — sin puntero, # sin teclado o con tofu. diff --git a/recipes/incoming-cosmic/gmp.toml b/recipes/incoming-cosmic/gmp.toml new file mode 100644 index 00000000..ce7b7b0c --- /dev/null +++ b/recipes/incoming-cosmic/gmp.toml @@ -0,0 +1,45 @@ +# gmp — aritmética de precisión arbitraria. Entra al corpus por abajo del lanzador: el plugin de la +# calculadora de pop-launcher llama al binario `qalc`, `qalc` es libqalculate, y libqalculate corta el +# configure con «libgmp not found» si no está. O sea que la cadena de una tecla del escritorio baja +# hasta la aritmética de enteros grandes — y ese es justo el tipo de dep que no se ve venir mirando +# el paquete de arriba. +# +# ── `--disable-assembly` NO ES PEREZA ─────────────────────────────────────────────────────────── +# GMP trae rutinas en ensamblador por microarquitectura y su `configfsf.guess` elige una mirando la +# CPU de QUIEN COMPILA. Eso es exactamente lo que este lab no puede permitir: hornearía la ISA del +# laptop en el artefacto y el hash dejaría de significar lo mismo en la granja (el mismo modo de +# falla que `-mcpu=baseline` corrige en el wrapper de zig). Con `--disable-assembly` GMP usa su +# implementación en C portable: más lenta, y a nadie le importa — acá la usa una calculadora de +# escritorio, no un criptosistema. +# Además zig/clang no digiere varias de esas rutinas (`.align` y sintaxis de gas antigua), así que +# la alternativa realista tampoco era «assembly sí». +name = "gmp" +version = "6.3.0" + +[source] +tarball = "https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz" +sha256 = "a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# `--enable-cxx` va APAGADO: libqalculate usa la API C de GMP (`__gmpz_init` es lo que su configure +# busca), y `libgmpxx` arrastraría el runtime de C++ a un paquete que no lo necesita — justo la deuda +# que la campaña «matar gcc» viene pagando en el resto del árbol. +configure = ''' +./configure \ + --build=$CBUILD --host=$CHOST \ + --prefix=/usr \ + --disable-shared --enable-static \ + --disable-assembly \ + --enable-fat=no +''' +compile = 'make -j"$(nproc)"' +install = 'make DESTDIR=/out install' + +[deps] +build = ["make", "m4"] diff --git a/recipes/incoming-cosmic/libqalculate.toml b/recipes/incoming-cosmic/libqalculate.toml new file mode 100644 index 00000000..621a1dff --- /dev/null +++ b/recipes/incoming-cosmic/libqalculate.toml @@ -0,0 +1,76 @@ +# libqalculate — la calculadora que le falta al lanzador. El plugin `calc` de pop-launcher no calcula +# nada por su cuenta: arma una línea y se la pasa al binario **`qalc`**, que es de este paquete. Sin +# él, tecleando `=2+2` el lanzador contesta —correctamente y sin fallar— «qalc command is not +# installed». Es el primer paquete del catálogo que existe para que una TECLA del escritorio dé un +# resultado, no para que otra librería enlace. +# +# ── LA CADENA QUE NADIE ADIVINA MIRANDO ARRIBA ────────────────────────────────────────────────── +# `Super` → cosmic-launcher → pop-launcher → plugin calc → qalc → libqalculate → **mpfr → gmp**. +# Seis saltos desde una tecla hasta la aritmética de precisión arbitraria, y los dos últimos son +# obligatorios: el configure corta con «libgmp not found» y «libmpfr not found or uses a different +# ABI». Por eso [[gmp]] y [[mpfr]] entraron primero. +# +# ── LO QUE SE APAGA, Y POR QUÉ CADA UNO ───────────────────────────────────────────────────────── +# · `--without-libcurl`: es para BAJARSE tipos de cambio de internet. Un paquete de una distro +# reproducible no tiene por qué traer un cliente HTTP para sumar; y la clausura se ahorra curl +# entero. Las tasas quedan las del fichero de definiciones, que es lo honesto: un dato con fecha. +# · `--without-icu`: sólo aporta comparación de cadenas Unicode insensible a mayúsculas. Traería +# ICU —decenas de MB de tablas— para que `SIN` funcione igual que `sin`. +# · `--disable-nls`: no hay locales en la imagen (los clientes ya avisan «failed to get system +# locale, falling back to en-US»), así que gettext sólo agregaría dependencia sin traducir nada. +# +# ── ⚠ READLINE NO ES OPCIONAL ACÁ, Y ESO SE MIDIÓ ─────────────────────────────────────────────── +# Yo había razonado que era indiferente «porque el plugin usa qalc de forma no interactiva». **Es +# falso, y el código del plugin lo dice**: `Command::new("qalc")` sin expresión, con `stdin` en pipe, +# le ESCRIBE la expresión por stdin y descarta las dos primeras líneas de stdout. O sea que lo usa en +# modo INTERACTIVO con stdin redirigido — el modo en que `-t ` no participa. +# Sin readline ese modo no funciona: el binario imprime el prompt `>` y no evalúa nada, así que la +# fila del lanzador sale VACÍA (no «no está instalado»: está, y no contesta). Con readline el bucle +# de entrada es el que espera el plugin. +# La lección es la de siempre en esta campaña, otra vez: **la dep no se deduce de para qué sirve el +# paquete, se lee de cómo lo INVOCA quien lo usa.** «Es una calculadora, la llamarán con la cuenta +# en argv» era una suposición, no una medición. +name = "libqalculate" +version = "5.12.0" + +[source] +tarball = "https://github.com/Qalculate/libqalculate/releases/download/v5.12.0/libqalculate-5.12.0.tar.gz" +sha256 = "f3dfdcf97d5a9e3a5bc0ebe66afd184721b606276eee3ba0ada8c92a5e71d44c" + +[build] +# C++ de verdad (la librería entera lo es), y con `compiler = "zig-cc"` el sandbox pone +# CXX=hammer-zig-cxx, o sea `zig c++` con la libc++ de LLVM enlazada ESTÁTICA. Eso es lo que evita +# que el artefacto pida `libstdc++.so.6`/`libgcc_s.so.1` de Alpine — la deuda que la campaña +# «matar gcc» viene pagando en el resto del árbol. +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +configure = ''' +# ── ALIAS DE CURSES, PORQUE EL CORPUS SÓLO EMPAQUETA LA VARIANTE WIDE ─────────────────────────── +# El configure de libqalculate busca readline probando `-lreadline` junto a `-lncurses`, `-lcurses`, +# `-ltermcap` y `-ltinfo`, en ese orden. Nuestro `ncurses` instala **sólo `libncursesw.a`** (la +# variante de caracteres anchos, que es la correcta y la única que hace falta), así que las cuatro +# pruebas fallan con «unable to find static system library» y el configure concluye —sin error— que +# no hay readline. Que el paquete se llame `ncurses` no significa que provea `-lncurses`. +# Los alias van en un directorio LOCAL y no tocando `/usr/lib`: la vista de deps del sandbox es la +# evidencia de qué se declaró, y ensuciarla para que un test pase deja mintiendo justo al mecanismo +# que existe para no mentir. +mkdir -p .curses-compat +for a in libncurses libcurses libtinfo libtermcap; do ln -sf /usr/lib/libncursesw.a ".curses-compat/$a.a"; done +./configure \ + --build=$CBUILD --host=$CHOST \ + --prefix=/usr \ + --disable-shared --enable-static \ + --without-libcurl \ + --without-icu \ + --disable-nls \ + LDFLAGS="$LDFLAGS -L$PWD/.curses-compat" +''' +compile = 'make -j"$(nproc)"' +install = 'make DESTDIR=/out install' + +[deps] +build = ["make", "gmp", "mpfr", "libxml2", "pkgconf", "readline", "ncurses"] diff --git a/recipes/incoming-cosmic/mpfr.toml b/recipes/incoming-cosmic/mpfr.toml new file mode 100644 index 00000000..bd98eca0 --- /dev/null +++ b/recipes/incoming-cosmic/mpfr.toml @@ -0,0 +1,37 @@ +# mpfr — coma flotante de precisión arbitraria con redondeo correcto, sobre gmp. El segundo eslabón +# de la cadena que baja desde una tecla del escritorio hasta la aritmética: libqalculate corta el +# configure con «libmpfr not found or uses a different ABI» si no está. +# +# «or uses a different ABI» en ese mensaje no es adorno: el test de upstream enlaza contra `mpfr_get_version`, +# así que un mpfr construido contra OTRO gmp falla igual que uno ausente. Por eso [[gmp]] se declara +# acá y no se da por sentado: los dos artefactos tienen que venir del mismo grafo. +name = "mpfr" +version = "4.2.2" + +[source] +tarball = "https://www.mpfr.org/mpfr-4.2.2/mpfr-4.2.2.tar.xz" +sha256 = "b67ba0383ef7e8a8563734e2e889ef5ec3c3b898a01d00fa0a6869ad81c6ce01" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# `--with-gmp=/usr` apunta al gmp del sandbox —el que proyecta la dep, no uno del sistema—, que es +# donde `hammer` monta los artefactos declarados. Sin el flag, el configure de mpfr busca en rutas +# del host y el error que da («gmp.h can't be found») manda a instalar algo, que es justo lo que en +# este lab no hay que hacer. +configure = ''' +./configure \ + --build=$CBUILD --host=$CHOST \ + --prefix=/usr \ + --disable-shared --enable-static \ + --with-gmp=/usr +''' +compile = 'make -j"$(nproc)"' +install = 'make DESTDIR=/out install' + +[deps] +build = ["make", "gmp"] diff --git a/scripts/cosmic/hydrate-cosmic.sh b/scripts/cosmic/hydrate-cosmic.sh index 377530a2..7b442faa 100755 --- a/scripts/cosmic/hydrate-cosmic.sh +++ b/scripts/cosmic/hydrate-cosmic.sh @@ -52,6 +52,9 @@ else # …y su MOTOR, que es otro binario y de otro repo: cosmic-launcher sólo dibuja, `pop-launcher` # busca. Lo lanza como proceso hijo por PATH, así que —otra vez— ningún grafo de deps lo ve. recipes/incoming-cosmic/pop-launcher.toml + # …y `qalc`, que es a lo que el plugin `calc` del lanzador le pasa la expresión. Tercer nivel de + # hijo invisible para el grafo: sesión → lanzador → pop-launcher → qalc, todos por PATH. + recipes/incoming-cosmic/libqalculate.toml recipes/incoming-cosmic/cosmic-app-library.toml recipes/incoming-cosmic/cosmic-workspaces-epoch.toml # Ni binario ni librería: DATOS. El tema de iconos de COSMIC — sin él el escritorio no se ve