b3:8b57ec9b, 9 minutos, NEEDED = libc.so y nada más. Un solo NEEDED, y el único `[deps] build = []` de la campaña: 130 líneas de Rust que no dibujan ni hablan wayland. Se incluye en la imagen sabiendo que falla, para que el hueco sea MEDIBLE en vez de supuesto. Error exacto capturado corriéndola desde cosmic-term dentro de la VM: panicked at src/main.rs:76:10: failed to send screenshot request: Portal(ZBus(MethodError(ServiceUnknown, "The name org.freedesktop.portal.Desktop was not provided by any .service files"))) Misma forma que la deuda de org.freedesktop.locale1: un nombre de D-Bus que nadie sirve. LA CADENA DEL PORTAL SON TRES ESLABONES, NO DOS. Medido en el fuente: xdg-desktop-portal-cosmic declara DBUS_NAME = "org.freedesktop.impl.portal.desktop.cosmic" (src/main.rs:27) — es BACKEND, no toma el nombre que los clientes buscan. Falta también el frontend xdg-desktop-portal, y ninguno de los dos está en ninguna cola. ⚠ CORRECCIÓN AL PROPIO RUNBOOK: decía que `gvfs` exigía «una campaña propia». Es falso desde que KDE selló las piezas. Verificado con `hammer hash` desde las dos colas —el método correcto para saber si una receta se comparte—: glib-shared 2.88.1 (b3:0584ce1f) y pcre2-shared (b3:f254abaa) resuelven IDÉNTICO desde incoming-cosmic ⇒ cero rebuild, los artefactos sirven tal cual. zlib-shared ya estaba en la cola. libffi entra estático dentro de la .so (su Requires.private lo ignora pkg-config fuera del modo estático). Lo que queda de `gvfs` NO es técnico sino una decisión: mete una segunda glib en la imagen —lo que GNOME midió y evitó— y re-hashea cosmic-files, que arrastra a cosmic-term y cosmic-edit porque lo usan como crate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
178 lines
9.6 KiB
Bash
Executable File
178 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# hydrate-cosmic.sh — proyecta al FHS el cierre de runtime de COSMIC desde artefactos SELLADOS del
|
|
# store, sin rebuild ni reproduce (`hammer hydrate <hash> --into`).
|
|
#
|
|
# Es el hermano de scripts/gnome/hydrate-gnome.sh y comparte con él lo que importa: el cierre sale
|
|
# del GRAFO REAL de recetas (`[deps].build`, resolución hermano→padre, la misma que usa hammer) y el
|
|
# artefacto se elige por `hammer hash` —el ArtifactHash de la receta de HOY, sin construir—, no por
|
|
# el más reciente del store. Con dos artefactos del mismo paquete conviviendo, la fecha miente.
|
|
#
|
|
# ── POR QUÉ LAS RAÍCES SE NOMBRAN A MANO ────────────────────────────────────────────────────────
|
|
# Porque el cierre de BUILD no es el cierre de RUNTIME, y en COSMIC la brecha es enorme: cosmic-session
|
|
# lanza a sus componentes por PATH, por nombre, con `start_component("cosmic-panel", …)`. Ninguno es
|
|
# dep de build de ningún otro, así que el grafo no los ve. La lista autoritativa es
|
|
# `cosmic-session/src/main.rs`, no una intuición sobre qué hace falta.
|
|
#
|
|
# Uso: scripts/cosmic/hydrate-cosmic.sh [receta.toml ...]
|
|
# Env: RFS (def work/cosmic-rootfs) destino
|
|
# STORE (def ./store)
|
|
# LINK (def static) static=hardlink | dynamic=patchelf
|
|
# KEEP (def 0) 1 = no borrar el RFS previo
|
|
set -uo pipefail
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
|
RFS="${RFS:-work/cosmic-rootfs}"; STORE="${STORE:-./store}"; LINK="${LINK:-static}"
|
|
HAMMER="${HAMMER:-./target/release/hammer}"; KEEP="${KEEP:-0}"
|
|
|
|
if [ $# -gt 0 ]; then
|
|
TARGETS=("$@")
|
|
else
|
|
TARGETS=(
|
|
# El compositor: la única raíz que el grafo sí explica sola (smithay enlaza sus backends).
|
|
recipes/incoming-cosmic/cosmic-comp.toml
|
|
# El gestor de sesión y su `start-cosmic`. Lanza al resto POR NOMBRE ⇒ nada de lo de abajo es
|
|
# alcanzable desde el grafo de deps.
|
|
recipes/incoming-cosmic/cosmic-session.toml
|
|
# Componentes que `cosmic-session/src/main.rs` levanta, en el orden en que los levanta.
|
|
recipes/incoming-cosmic/cosmic-bg.toml
|
|
recipes/incoming-cosmic/cosmic-idle.toml
|
|
recipes/incoming-cosmic/cosmic-panel.toml
|
|
recipes/incoming-cosmic/cosmic-osd.toml
|
|
# Los dos que —con el panel— hacen que `cosmic-session` no panickee. Son TRES los obligatorios;
|
|
# ver la tabla de `.expect` del runbook.
|
|
recipes/incoming-cosmic/cosmic-settings-daemon.toml
|
|
recipes/incoming-cosmic/cosmic-notifications.toml
|
|
# Los que el PANEL spawnea (no cosmic-session): un binario multiplexado en veinte symlinks, más
|
|
# los tres botones. Sin ellos el panel arranca, dice «Done spawning applets» y NO SE DIBUJA —
|
|
# nada que medir, nada que pintar. Invisibles para el grafo por la misma razón que los de arriba:
|
|
# se invocan por AppID.
|
|
recipes/incoming-cosmic/cosmic-applets.toml
|
|
# Los tres que esos botones abren al hacer clic. No matan la sesión si faltan (el botón queda
|
|
# muerto), pero un escritorio sin lanzador ni biblioteca de apps no se puede usar.
|
|
recipes/incoming-cosmic/cosmic-launcher.toml
|
|
# …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
|
|
# La PRIMERA aplicación con ventana del catálogo. A diferencia de todo lo de arriba, ésta sí es
|
|
# alcanzable «desde el escritorio»: su `.desktop` no es `NoDisplay`, así que aparece en la
|
|
# biblioteca. Sigue siendo raíz a mano porque nadie la declara como dep de nada.
|
|
recipes/incoming-cosmic/cosmic-term.toml
|
|
# …y el gestor de ficheros, la segunda. Su `.desktop` tampoco es `NoDisplay`.
|
|
recipes/incoming-cosmic/cosmic-files.toml
|
|
# …y el panel de control, la tercera. Instala 32 `.desktop`: uno por página (Pantalla, Sonido,
|
|
# Red…), así que la biblioteca lista cada sección como entrada propia.
|
|
recipes/incoming-cosmic/cosmic-settings.toml
|
|
# …y el editor de texto, la cuarta. Su `.desktop` declara `MimeType=text/plain`, así que además
|
|
# de aparecer en la biblioteca es a quien el gestor de ficheros abre un `.txt`.
|
|
recipes/incoming-cosmic/cosmic-edit.toml
|
|
# …y la tienda, la quinta. Con el backend `pkgar` NAVEGA pero no INSTALA: lee el AppStream del
|
|
# sistema, o sea los `.metainfo.xml` que instalan las recetas de acá arriba.
|
|
recipes/incoming-cosmic/cosmic-store.toml
|
|
# …y la captura, la sexta. Se incluye SABIENDO que hoy falla: pide la captura al portal XDG, que
|
|
# la imagen no tiene. Está acá para que el hueco sea visible y medible, no para que funcione.
|
|
recipes/incoming-cosmic/cosmic-screenshot.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
|
|
# «sin iconos», se ve roto, y con el cursor por software ni siquiera hay puntero.
|
|
recipes/incoming-cosmic/cosmic-icons.toml
|
|
# …y el tema del que HEREDA (`Inherits=hicolor` en su index.theme). Que un tema herede de otro es
|
|
# una relación declarada en un fichero de datos, invisible para cualquier grafo de deps.
|
|
recipes/incoming-cosmic/hicolor-icon-theme.toml
|
|
|
|
# ── LO QUE PIDE `start-cosmic`, Y NO LO PIDE NINGÚN `[deps]` ────────────────────────────────
|
|
# bash de verdad: el script usa `mapfile`, `[[ ]]` y expansión indirecta `${!var}`, así que con el
|
|
# busybox de la base metal no arranca. Y termina en `exec dbus-run-session -- cosmic-session`,
|
|
# o sea que el bus de SESIÓN es parte del arranque, no un extra.
|
|
recipes/bash.toml
|
|
recipes/dbus.toml
|
|
|
|
# ── DATOS QUE UNA LIBRERÍA DEL CIERRE NECESITA EN RUNTIME ───────────────────────────────────
|
|
# libxkbcommon entra por el grafo (la enlazan comp y clientes) pero sus TABLAS no: sin
|
|
# `/usr/share/X11/xkb` no puede compilar un keymap y el escritorio queda sin teclado. Es el mismo
|
|
# patrón que los gschemas en GNOME — la librería es la interfaz, los datos son otro paquete.
|
|
recipes/incoming-cosmic/xkeyboard-config.toml
|
|
# Idem tipografías: nada del cierre trae una sola fuente, y sin fuentes el panel sale con tofu.
|
|
recipes/incoming-cosmic/dejavu-fonts.toml
|
|
# zlib COMPARTIDA. El corpus trae `zlib` estática, que le sirve a todo el cierre de COSMIC… y no
|
|
# al driver de mesa: `kms_swrast_dri.so` es un .so que se abre con dlopen y NEEDea `libz.so.1`.
|
|
# Sin ella mesa no carga NINGÚN driver de software, GBM falla, y el compositor arranca igual y
|
|
# deja la pantalla NEGRA — el fallo aparece a tres capas de distancia de la causa.
|
|
recipes/incoming-cosmic/zlib-shared.toml
|
|
)
|
|
fi
|
|
|
|
# ── cierre transitivo por el grafo de recetas, en orden topológico ──────────────────────────────
|
|
PLAN=$(python3 - "${TARGETS[@]}" <<'PY'
|
|
import sys, tomllib
|
|
from pathlib import Path
|
|
|
|
def resolve(base_dir: Path, dep: str) -> Path | None:
|
|
"""Misma regla que hammer_build::resolve_dep_path: HERMANO primero, luego el catálogo PADRE.
|
|
Nunca de reojo a otra cola — por eso libdisplay-info y nasm hubo que copiarlas a incoming-cosmic/."""
|
|
sib = base_dir / f"{dep}.toml"
|
|
if sib.exists():
|
|
return sib
|
|
par = base_dir.parent / f"{dep}.toml"
|
|
return par if par.exists() else None
|
|
|
|
seen, order, missing = set(), [], []
|
|
|
|
def visit(path: Path):
|
|
p = path.resolve()
|
|
if p in seen:
|
|
return
|
|
seen.add(p)
|
|
try:
|
|
r = tomllib.loads(p.read_text())
|
|
except Exception as e:
|
|
print(f"!PARSE\t{p}\t{e}", file=sys.stderr)
|
|
return
|
|
for d in r.get("deps", {}).get("build", []):
|
|
q = resolve(p.parent, d)
|
|
if q is None:
|
|
missing.append((p.name, d))
|
|
else:
|
|
visit(q)
|
|
order.append((p, r.get("name", p.stem)))
|
|
|
|
for t in sys.argv[1:]:
|
|
visit(Path(t))
|
|
|
|
for recipe, dep in missing:
|
|
print(f"!NORECIPE\t{recipe}\t{dep}", file=sys.stderr)
|
|
for p, name in order:
|
|
print(f"{p}\t{name}")
|
|
PY
|
|
)
|
|
|
|
[ "$KEEP" = 1 ] || rm -rf "$RFS"
|
|
mkdir -p "$RFS"
|
|
total=$(printf '%s\n' "$PLAN" | grep -c . || true)
|
|
echo "==> cierre de ${#TARGETS[@]} raíces: $total recetas → $RFS (link=$LINK)"
|
|
|
|
ok=0; miss=0; MISSING=""
|
|
while IFS=$'\t' read -r path name; do
|
|
[ -n "$path" ] || continue
|
|
h=$("$HAMMER" --store "$STORE" hash "$path" 2>/dev/null | tail -1)
|
|
if [ -z "$h" ]; then
|
|
printf " ✗ %-28s (hash falló)\n" "$name"; miss=$((miss+1)); MISSING="$MISSING $name"; continue
|
|
fi
|
|
hex="${h#b3:}"
|
|
if [ ! -d "$STORE/$hex-$name" ]; then
|
|
printf " ✗ %-28s %s NO-SELLADO\n" "$name" "${hex:0:12}"; miss=$((miss+1)); MISSING="$MISSING $name"; continue
|
|
fi
|
|
if "$HAMMER" --store "$STORE" hydrate "$hex" --into "$RFS" --link "$LINK" >/dev/null 2>&1; then
|
|
ok=$((ok+1))
|
|
else
|
|
printf " ✗ %-28s %s (hydrate falló)\n" "$name" "${hex:0:12}"; miss=$((miss+1)); MISSING="$MISSING $name"
|
|
fi
|
|
done <<< "$PLAN"
|
|
|
|
echo "==> $ok proyectados · $miss faltantes"
|
|
[ -n "$MISSING" ] && echo " faltan:$MISSING"
|
|
echo " binarios: $(find "$RFS" -type f -perm -u+x 2>/dev/null | wc -l) · .so: $(find "$RFS" -name '*.so*' 2>/dev/null | wc -l)"
|
|
[ "$miss" = 0 ]
|