diff --git a/docs/evidencia/cosmic-calculadora-qemu-2026-08-04.png b/docs/evidencia/cosmic-calculadora-qemu-2026-08-04.png new file mode 100644 index 00000000..c45ad4ba Binary files /dev/null and b/docs/evidencia/cosmic-calculadora-qemu-2026-08-04.png differ diff --git a/docs/runbooks/cosmic-desktop.md b/docs/runbooks/cosmic-desktop.md index 08efe24b..d4d0e7fa 100644 --- a/docs/runbooks/cosmic-desktop.md +++ b/docs/runbooks/cosmic-desktop.md @@ -108,7 +108,7 @@ Más `cosmic-settings-daemon` y `xdg-desktop-portal-cosmic` fuera de esa lista. | **`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` | +| `libqalculate` 5.12.0 (`qalc`) | ✅ sellado — la calculadora del lanzador ANDA (ver abajo) | `b3:edb8d14d` | **La cola está COMPLETA: 30 recetas, `escritorio-cosmic` 68/68.** @@ -481,38 +481,66 @@ 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 +### 🧮 `libqalculate`: la calculadora del lanzador, y un `stdin` que era `stdout` -`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. +**`=123*456` → `56088` en el lanzador** (`cosmic-calculadora-qemu-2026-08-04.png`). La cadena entera +desde una tecla está en el catálogo: `Super` → cosmic-launcher → pop-launcher → plugin `calc` → +`qalc` → **mpfr → gmp**. -**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: +Pero el camino hasta ahí tuvo dos muros que vale la pena dejar escritos, porque ninguno se parecía a +lo que era. -```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 +**1) `stdin` apuntaba al descriptor 1.** El plugin no le pasa la cuenta por argv: arranca `qalc` SIN +expresión, con stdin en tubería, le escribe la expresión y cierra. En ese modo el binario **no leía +nada** — ni error ni resultado, la fila del lanzador salía en blanco. El síntoma acusaba a readline, +al toolchain y a la versión, y no era ninguno: + +``` +DIAG-main isatty=0 feof=0 ferror=0 fd=1 ← fileno(stdin) == 1 +nm -S qalc | grep -E ' (stdin|stdout|stderr)$' + 1847148 8 B stderr + 1847148 8 B stdin ← las TRES en la misma dirección + 1847148 8 B stdout ``` -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. +La causa: **el binario salía DINÁMICO aunque la receta dice `link = "static"`, porque libtool se come +el `-static` que inyecta el harness** ([[link-static-mentira-libtool]] otra vez, cuarto caso). Y en +un musl dinámico las tres variables de stdio se resolvieron por *copy relocation* a **una sola ranura +de BSS compartida**: la última que escribe el loader gana, así que el `stdin` del programa era el +`stdout`. De ahí la asimetría que parecía absurda: `qalc -f /dev/stdin` funcionaba —abre el +descriptor por RUTA, sin tocar el `FILE*` roto— y `qalc` leyendo `stdin` no. -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`. +El fix es el par que ya usa `libxml2`: `make LDFLAGS="-all-static -no-pie"`. Con él el binario queda +estático de verdad y los tres símbolos vuelven a tener direcciones distintas, en `.rodata`: -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. +``` + 11e71c8 8 R stderr + 11e71d0 8 R stdin + 11e71d8 8 R stdout +``` + +**La regla, y es de las caras: `link = "static"` en la receta no garantiza un binario estático.** +Cuando algo se comporta como si su `stdin`/`stdout` estuvieran cruzados, `readelf -d` y +`nm -S | grep ' std'` contestan en dos segundos lo que el log no dice nunca. + +**2) La pregunta de primer arranque se come la cuenta, SIEMPRE.** Antes de leer nada, `qalc` pregunta +si activar `autocalc`. El plugin le manda la expresión y cierra: la expresión se consume como +respuesta, no es un sí/no válido, y qalc **no llega a guardar config** ⇒ la próxima vez vuelve a +preguntar. No es «falla la primera vez»: no anda nunca. Se siembra +`~/.config/qalculate/qalc.cfg` con `calculate_as_you_type=0` desde `cosmic-start` — política de la +imagen, como el color de fondo; una distro de verdad lo pondría en `/etc/skel`. + +**Gotchas de empaquetado de la cadena:** +- **`gmp` va con `--disable-assembly`.** Elige rutinas en ensamblador mirando la CPU de quien + compila, o sea hornearía la ISA del laptop en el artefacto y el hash dejaría de significar lo mismo + en la granja. Es una calculadora de escritorio: la implementación en C portable sobra. +- **El configure de libqalculate no encuentra readline** porque prueba `-lreadline` junto a + `-lncurses`, `-lcurses`, `-ltermcap` y `-ltinfo`, y el corpus **sólo empaqueta `libncursesw.a`**. + Que el paquete se llame `ncurses` no significa que provea `-lncurses`. Los alias van en un + directorio LOCAL (`.curses-compat/`) y no en `/usr/lib`: esa vista es la evidencia de qué se + declaró, y ensuciarla para que pase un test deja mintiendo justo al mecanismo que existe para no + mentir. Ojo: hay que repetir el `-L` en el `make`, porque el `LDFLAGS` del `make` PISA al del + `configure`. ### ⏱ El panel tiene un DEADLINE de arranque: con 4 vCPU sale mutilado @@ -530,9 +558,9 @@ El número de procesos no distingue «arrancó» de «dibujó». El framebuffer ## Lo que falta, en orden -1. **Aplicaciones.** No hay ni una: los 20 `.desktop` de la imagen son `NoDisplay=true`. El primer - candidato barato es `libqalculate` (el binario `qalc`), que es lo único que le falta al plugin de - la calculadora para dar un resultado en vez de decir que no está. +1. **Aplicaciones con ventana.** Sigue sin haber ninguna: los 20 `.desktop` de la imagen son + `NoDisplay=true`, así que la biblioteca sale vacía y eso es correcto. La calculadora del lanzador + ya da resultados, pero es una fila de texto, no una app. `cosmic-term` sería la primera de verdad. 2. `xdg-desktop-portal-cosmic` — sin él no hay captura de pantalla, ni file-chooser, ni compartir. 3. `cosmic-settings` (el panel de control) y `cosmic-files`: la suite tiene aplicaciones y el catálogo todavía no. diff --git a/docs/state/build-state-cosmic.json b/docs/state/build-state-cosmic.json index c59ff535..6a312856 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" ], @@ -8228,8 +8225,8 @@ ], "cls": "c", "queue": "incoming-cosmic", - "hash": "b3:fcf9c6c097f9f37235319d52dbe64e42deaa012cc374aee206f9c5d2201ae3ac", - "state": "debt", + "hash": "b3:edb8d14db8e625168dea50e40073840b29a6800d7c5b027bbf01f17f15fd5b68", + "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/recipes/incoming-cosmic/libqalculate.toml b/recipes/incoming-cosmic/libqalculate.toml index 621a1dff..dd57e95c 100644 --- a/recipes/incoming-cosmic/libqalculate.toml +++ b/recipes/incoming-cosmic/libqalculate.toml @@ -69,8 +69,17 @@ for a in libncurses libcurses libtinfo libtermcap; do ln -sf /usr/lib/libncurses --disable-nls \ LDFLAGS="$LDFLAGS -L$PWD/.curses-compat" ''' -compile = 'make -j"$(nproc)"' -install = 'make DESTDIR=/out install' +# `-all-static` NO ES REDUNDANTE con `link = "static"`, y esa confusión costó todo un frente. +# El harness inyecta `LDFLAGS=-static`, pero **libtool se lo come**: la línea de enlace real de `qalc` +# salía dinámica contra `libc.so`. Y un musl DINÁMICO acá no es sólo «pesa distinto»: `stdin`, +# `stdout` y `stderr` terminaron **en la misma dirección de BSS**, las tres compartiendo una única +# ranura de copy-relocation, así que el `stdin` del programa era en realidad el `stdout` +# (`fileno(stdin)` = 1). De ahí que `qalc` no leyera NADA de una tubería mientras `-f /dev/stdin` +# —que abre el descriptor por ruta, sin tocar el FILE* roto— funcionaba perfecto. +# `-all-static` es la forma de decírselo a libtool; `-no-pie` va con él (un estático PIE no tiene +# sentido). Mismo par que usa `libxml2` en el corpus, por la misma razón. +compile = 'make LDFLAGS="-all-static -no-pie -L$PWD/.curses-compat" -j"$(nproc)"' +install = 'make DESTDIR=/out install LDFLAGS="-all-static -no-pie -L$PWD/.curses-compat"' [deps] build = ["make", "gmp", "mpfr", "libxml2", "pkgconf", "readline", "ncurses"] diff --git a/scripts/cosmic/cosmic-start-qemu.sh b/scripts/cosmic/cosmic-start-qemu.sh index def6091f..9a8815c4 100755 --- a/scripts/cosmic/cosmic-start-qemu.sh +++ b/scripts/cosmic/cosmic-start-qemu.sh @@ -157,6 +157,19 @@ RON say "fondo: color liso (el wallpaper por defecto vive en git-lfs y no se empaqueta)" fi +# ── SEMILLA DE CONFIG DE qalc, SIN LA CUAL LA CALCULADORA DEL LANZADOR NUNCA ANDA ──────────────── +# En el primer arranque `qalc` hace una PREGUNTA interactiva («¿activar autocalc?») antes de leer +# nada. El plugin `calc` de pop-launcher le escribe la expresión y cierra el pipe, así que la +# expresión se consume como respuesta, no es un sí/no válido, y qalc **no llega a guardar config** ⇒ +# la próxima vez vuelve a preguntar. O sea que sin sembrar esto no falla una vez: falla SIEMPRE. +# Es política de la imagen, no del paquete —igual que el color de fondo—: una distro de verdad lo +# pondría en el esqueleto de /etc/skel. +if [ ! -f "$HOME/.config/qalculate/qalc.cfg" ]; then + mkdir -p "$HOME/.config/qalculate" + printf '[General]\ncalculate_as_you_type=0\n' > "$HOME/.config/qalculate/qalc.cfg" + say "qalc: config sembrada (si no, la pregunta de primer arranque se come la cuenta)" +fi + # ── MODO: bare (default) o session ────────────────────────────────────────────────────────────── # `cosmic-session` es el camino de producción, pero HOY no se puede usar: llama a # `cosmic-settings-daemon` con `.expect("failed to start settings daemon")` (src/main.rs:255) y