250 líneas de comentario en 151 scripts. Control verificado: el diff no toca NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa. El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces: las tres eran el MOTD que el script escribe DENTRO de la imagen construida — texto del producto, no comentario del script. Se cambiaron aparte y a propósito, que es rebranding, no limpieza. Y el hallazgo caro: casaba contra , que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate. La etapa 4 lo movió a y el script quedó casando NADA. No fallaba: imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja. Además 14 rutas de módulo en docs, que el barrido anterior no tocó porque no es frontera de palabra.
194 lines
11 KiB
Bash
Executable File
194 lines
11 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 (`takana 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 takana) y el
|
|
# artefacto se elige por `takana 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="${TAKANA:-${HAMMER:-./target/release/takana}}"; 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
|
|
# ── LA CADENA DEL PORTAL, y son DOS raíces porque son DOS procesos ───────────────────────────
|
|
# El frontend registra `org.freedesktop.portal.Desktop` (lo que los clientes buscan) y enruta al
|
|
# backend por `org.freedesktop.impl.portal.*`. Ninguno es dep de build del otro: se encuentran
|
|
# por D-Bus en runtime, así que el grafo no los ve — igual que los componentes de la sesión.
|
|
recipes/incoming-cosmic/xdg-desktop-portal.toml
|
|
recipes/incoming-cosmic/xdg-desktop-portal-cosmic.toml
|
|
# …y la SONDA con la que se mide esa cadena. No es parte del escritorio: es el instrumento.
|
|
# Entra a la imagen a propósito —un handshake de portal sólo se puede ejercer DESDE la sesión,
|
|
# con el bus y el compositor vivos— y sale estática, sin un NEEDED, justamente para que nunca
|
|
# falle por algo del sistema que está midiendo.
|
|
recipes/incoming-cosmic/portal-probe.toml
|
|
# El GESTOR DE SESIÓN de pipewire. Ningún `[deps]` lo alcanza —es un cliente del demonio, no una
|
|
# librería de nadie— así que es raíz a mano como todo lo que se lanza por PATH. Trae consigo la
|
|
# glib COMPARTIDA (`glib-shared`), que no es la estática que usa el resto de la cola: wireplumber
|
|
# produce `.so` y la estática del corpus no es PIC.
|
|
recipes/incoming-cosmic/wireplumber.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 ]
|