diff --git a/recipes/perl.toml b/recipes/perl.toml index 8fc13a1e..903de001 100644 --- a/recipes/perl.toml +++ b/recipes/perl.toml @@ -34,5 +34,24 @@ sh Configure -des -Dprefix=/usr -Dcc="$ZW/cc" -Dld="$ZW/cc" -Duseshrplib -Dusenm compile = 'make -j"$(nproc)"' install = 'make install DESTDIR=/out' +# ⚠ DEUDA MEDIDA Y NO PAGADA (2026-09-04): esta receta instala 945 ficheros en la RAÍZ del rootfs. +# +# Su `Configure -des` no encuentra nroff, así que elige `man1ext='0'` y —lo importante— +# `man1dir=' '` / `man3dir=' '`, la convención de perl para «no instales man». Pero `installman` +# igual GENERA las páginas preformateadas y, con el directorio vacío, `make install DESTDIR=/out` +# las deja en `/out/`: `AnyDBM_File.0`, `App::Cpan.0`, … 945 ficheros sueltos en `/`. +# Se ve hidratando un rootfs de verdad; ninguna métrica sobre recetas puede verlo. El guardián que +# lo caza y NOMBRA al culpable está en `scripts/hydrate-profile.py` (§quién ensucia la raíz). +# +# EL ARREGLO ES UNA LÍNEA —`-Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 +# -Dman1ext=1 -Dman3ext=3`— Y NO SE APLICA HOY. `yupana radio perl` dice por qué: +# +# dependientes transitivos 347 · sellados que CAEN a deuda 305 +# imágenes afectadas: base, cli, escritorio-{kde,gnome,cosmic,sway,mirada} (TODAS) +# +# 305 rebuilds para mover unas páginas de man de sitio no se paga solo. **Va con el próximo bump de +# perl**, cuando el re-hash ya esté pagado: ahí la línea cuesta cero. Esto no es un olvido, es una +# decisión con el número delante. + [deps] build = ["pkgconf"] diff --git a/scripts/hydrate-profile.py b/scripts/hydrate-profile.py index b74514b7..c344af16 100755 --- a/scripts/hydrate-profile.py +++ b/scripts/hydrate-profile.py @@ -146,6 +146,23 @@ def main(): for nombre, msg in fallidos: print(" %-28s %s" % (nombre, msg[0] if msg else "")) + # ── quién ensucia la RAÍZ del rootfs ──────────────────────────────────────────────────────── + # Se mira por ARTEFACTO y no sobre el árbol fundido, porque en el árbol fundido el nombre del + # culpable ya se perdió: 945 ficheros sueltos en `/` no se parecen a «una fase install con el + # directorio de destino vacío», que es lo que son. Cazado el 2026-09-04 mirando un rootfs de + # verdad, no una métrica: `perl` instala sus 945 páginas nroff en `/` porque su Configure eligió + # `man1dir=' '`. Ver el comentario en `recipes/perl.toml`. + sucios = [] + for nombre, _h, d in artefactos: + extra = sorted(e for e in os.listdir(d) if e not in FHS_RAIZ) + if extra: + sucios.append((nombre, extra)) + if sucios: + print("\n ⚠ entradas FUERA del FHS en la raíz del rootfs:") + for nombre, extra in sorted(sucios, key=lambda x: -len(x[1])): + muestra = ", ".join(extra[:3]) + (", …" if len(extra) > 3 else "") + print(" %-28s %4d (%s)" % (nombre, len(extra), muestra)) + n_fich = sum(len(f) for _, _, f in os.walk(into)) du = subprocess.run(["du", "-sh", into], capture_output=True, text=True).stdout.split() print("\n== %s: %d ficheros, %s" % (into, n_fich, du[0] if du else "?")) @@ -154,5 +171,13 @@ def main(): FLAGS_CON_VALOR = {"--into", "--store", "--link"} +# Lo que un artefacto PUEDE poner en la raíz. Todo lo demás es una fase `install` que se equivocó de +# destino, y en un rootfs fundido eso se lee como si fuera parte del sistema. +FHS_RAIZ = {"bin", "sbin", "lib", "lib64", "usr", "etc", "var", "run", "opt", "srv", + "home", "root", "boot", "dev", "proc", "sys", "tmp", "mnt", "media", + "ente", # el manifiesto de atestación del producto (hammer bootstrap) + "linuxrc", # el enlace de busybox, que va en la raíz por contrato del kernel + ".hammer"} # metadatos del store: los pone hammer al sellar, no la receta + if __name__ == "__main__": main()