gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1181Shell 82Rust 62Markdown 521515 Sutichaykuna

jaula: el toolchain del corpus no arrancaba adentro — faltaba el cargador musl

Los diez envoltorios daban verde porque el control se corrió EN LA CAJA, que es
musl. Adentro de la imagen de Arch, `rustc`, `cargo` y `python3` —las tres musl
DINÁMICAS— mueren con `cannot execute: required file not found`: su PT_INTERP
pide `/lib/ld-musl-x86_64.so.1` y ahí no está. `LD_LIBRARY_PATH` no puede
taparlo, actúa después de que el cargador cargue. Ahora se invoca el cargador
del lab explícitamente, y sólo si la caja no tiene el suyo: el mismo guion sirve
en los dos sitios sin ramificar.

Segundo defecto, del mismo viaje: `zig cc` a secas no sirve de `cc` para cargo.
cc-rs le pasa su propio triple y zig no lo entiende —`unable to parse target
query 'x86_64-unknown-linux-musl'`—, lo que tumba `blake3` y con él medio árbol
de tawasuyu. Los dos drivers (el de la caja y el de la jaula) lo descartan.

Las banderas de enlace van dentro del envoltorio de `rustc` y no en
/etc/profile.d: la jaula no corre shells de login, así que allá el fichero
quedaría presente, con contenido y sin efecto.

Y los controles ahora COMPILAN en vez de preguntar `--version`, que no tocaba ni
el driver de C ni el enlazador y por eso no vio ninguno de los dos defectos.

Medido con esto puesto y sin una sola variable a mano:
`cargo check -p tejido` sobre tawasuyu, verde en 1m13s.

785518b9085016ef49e52d34b8b57ca896a2825c · ruwaq Sergio · (1 killa ñawpaq) · tayta 990e3ecc6 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

2 qillqakuna · +150 −10

diff --git a/scripts/servidor/cc-por-zig.sh b/scripts/servidor/cc-por-zig.sh
index b3baa398..cccf0900 100755
--- a/scripts/servidor/cc-por-zig.sh
+++ b/scripts/servidor/cc-por-zig.sh
@@ -36,7 +36,18 @@ for par in "cc:cc" "c++:c++"; do
   cat > "/usr/bin/$nombre" <<EOF
 #!/bin/sh
 # Generado por scripts/servidor/cc-por-zig.sh — envoltorio de \`zig $sub\`.
-ZIG_GLOBAL_CACHE_DIR=\${ZIG_GLOBAL_CACHE_DIR:-$CACHE} exec "$ZIG" $sub "\$@"
+# ⚠ Se DESCARTA el \`--target=\` que le pasa cc-rs. Usa triples de rustc y zig sólo entiende los
+# suyos, así que el driver a secas muere con (medido 2026-09-18, dentro de la jaula):
+#     error: unable to parse target query 'x86_64-unknown-linux-musl': UnknownOperatingSystem
+# Y eso tumba \`blake3\`, que cuelga de medio árbol de tawasuyu. Mismo filtro en
+# scripts/servidor/jaula-herramientas.sh, que arma el driver del lado de adentro.
+args=""
+for a in "\$@"; do
+  case "\$a" in --target=*|-target) continue ;; esac
+  args="\$args '\$a'"
+done
+export ZIG_GLOBAL_CACHE_DIR="\${ZIG_GLOBAL_CACHE_DIR:-$CACHE}"
+eval exec "$ZIG" $sub -target x86_64-linux-musl \$args
 EOF
   chmod 755 "/usr/bin/$nombre"
 done
@@ -79,6 +90,13 @@ cc -o "$T/h" "$T/h.c" 2>"$T/err" || { echo "✗ el cc nuevo no compila un hola m
 out=$("$T/h") || { echo "✗ compiló y no corre" >&2; exit 1; }
 echo "  ✓ C: $out"
 
+# ⚠ Y el mismo hola mundo CON el triple que pasa cc-rs, que es por donde entra cargo. El control de
+# arriba no lo tocaba, así que no habría visto el defecto que tumbaba blake3.
+cc --target=x86_64-unknown-linux-musl -o "$T/h2" "$T/h.c" 2>"$T/errcc" || {
+  echo "✗ el cc nuevo no acepta el --target= de cc-rs (es lo que rompe blake3):" >&2; head -3 "$T/errcc" >&2; exit 1; }
+"$T/h2" >/dev/null || { echo "✗ compiló con el triple de cc-rs y no corre" >&2; exit 1; }
+echo "  ✓ C: con el --target= de cc-rs"
+
 R=$(ls -d "$STORE"/*-rust 2>/dev/null | tail -1)
 if [ -n "$R" ] && [ -x "$R/usr/bin/rustc" ]; then
   printf 'extern crate proc_macro;\nuse proc_macro::TokenStream;\n#[proc_macro]\npub fn m(_i: TokenStream) -> TokenStream { "fn v() -> u32 { 42 }".parse().unwrap() }\n' > "$T/pm.rs"
diff --git a/scripts/servidor/jaula-herramientas.sh b/scripts/servidor/jaula-herramientas.sh
index 8c83ec78..9f86a8f7 100755
--- a/scripts/servidor/jaula-herramientas.sh
+++ b/scripts/servidor/jaula-herramientas.sh
@@ -16,40 +16,135 @@
 # lo coherente con la distro: el corpus es el que provee, no Arch.
 #
 # `go` y `zig` no van acá: viven en el lab (`/work/dev-fs/tools`) y el lanzador ya los pone en PATH.
+#
+# ── TRES DE LAS DIEZ NO SON ESTÁTICAS, Y ADENTRO NO ARRANCAN (2026-09-18, medido) ───────────────
+# `rustc`, `cargo` y `python3` son musl **dinámicas**. Su `PT_INTERP` pide `/lib/ld-musl-x86_64.so.1`
+# y **la imagen de Arch no lo tiene**, así que adentro las tres mueren con
+#
+#     …/usr/bin/cargo: cannot execute: required file not found
+#
+# `LD_LIBRARY_PATH` no puede taparlo: actúa DESPUÉS de que el cargador cargue, y acá el que falta es
+# el cargador. Por eso este guion pasó en verde la primera vez: se corrió EN LA CAJA, que es musl y
+# lo tiene nativo. El control decía la verdad sobre el sitio equivocado.
+#
+# La cura es invocar el cargador del lab explícitamente. Y el envoltorio de `rustc` tiene que
+# llamarse `rustc` **en el PATH**, no sólo existir: cargo lanza a `rustc` POR NOMBRE, y sin eso muere
+# con `could not execute process … rustc -vV (never executed)`, que no nombra la causa.
+#
+# ── Y `cc` TIENE QUE FILTRAR EL TRIPLE QUE LE PASA cc-rs (2026-09-18, medido) ───────────────────
+# `zig cc` a secas no sirve de `cc` para cargo. cc-rs le pasa SU triple y zig no lo entiende:
+#
+#     $ zig cc --target=x86_64-unknown-linux-musl -o h h.c
+#     error: unable to parse target query 'x86_64-unknown-linux-musl': UnknownOperatingSystem
+#
+# Eso tumba `blake3`, que cuelga de medio árbol de tawasuyu. Es el MISMO defecto que tiene el `cc`
+# de `cc-por-zig.sh` en la caja, arreglado allá igual que acá.
+#
+# ── Y LAS BANDERAS DE ENLACE VAN EN EL ENVOLTORIO, NO EN /etc/profile.d ─────────────────────────
+# En la caja las pone `cc-por-zig.sh` en `/etc/profile.d/rust-enlazador.sh`. Acá eso NO serviría: la
+# jaula no corre shells de login —el lanzador hace `exec` directo— así que el fichero quedaría
+# presente, con contenido, y sin efecto: justo la avería que este repo persigue. Van adentro del
+# envoltorio de `rustc`, que es el único sitio por el que pasan TODAS las compilaciones, incluidas
+# las de los build scripts (a los que `RUSTFLAGS` no alcanza cuando se pasa `--target`).
+# Van PRIMERO para que lo que ponga la persona en `RUSTFLAGS` gane: en rustc, la última `-C` manda.
 set -eu
 
 U=${1:-sergio}
 BIN=${BIN:-/work/$U/bin}
 install -d -o "$U" -g "$U" "$BIN"
 
+# ── El lab: de ahí sale el cargador musl que la imagen ajena no tiene ───────────────────────────
+LAB=${TAKANA_LAB:-}
+if [ -z "$LAB" ]; then
+  for d in /work/dev-fs/alpine "/work/$U/dev-fs/alpine"; do
+    [ -x "$d/lib/ld-musl-x86_64.so.1" ] && { LAB=$d; break; }
+  done
+fi
+[ -n "$LAB" ] || { echo "no encuentro el alpine del lab (probé /work/dev-fs/alpine y /work/$U/dev-fs/alpine)" >&2; exit 1; }
+echo "==> lab en $LAB"
+
+# El zig del CORPUS, el mismo que usa cc-por-zig.sh en la caja. No se toma el de `/work/dev-fs/tools`
+# para que el `cc` de adentro y el de afuera sean el mismo binario.
+ZIG=$(find /store -maxdepth 3 -type f -perm -100 -name zig -path '*seed-zig*' 2>/dev/null | head -1)
+[ -n "$ZIG" ] || { echo "no encuentro el zig de seed-zig en /store — sin él no hay driver de C" >&2; exit 1; }
+echo "==> zig $("$ZIG" version) en $ZIG"
+
 # Cuerpo común de los envoltorios. $1 = nombre del binario, $2 = nombre de la RECETA, $3 = extra.
 #
 # ⚠ El nombre de la receta se compara ENTERO, no por sufijo: el glob `/store/*-make` también casa
 # con `…-cargo-make` —medido: la primera versión eligió cargo-make y `make --version` moría con un
 # `exec` que no nombraba la causa—. El directorio es `<hash>-<receta>`: se le quita el hash y se
 # compara. Y se elige el MÁS RECIENTE, no un hash cableado, para sobrevivir a un re-sellado.
+busca_artefacto() {
+  echo "b=\$(ls -dt /store/*-$1 2>/dev/null | while read -r d; do n=\${d##*/}; [ \"\${n#*-}\" = \"$1\" ] && { echo \"\$d\"; break; }; done)"
+  echo "[ -n \"\$b\" ] || { echo '$2: no hay artefacto -$1 en /store' >&2; exit 1; }"
+}
+
 envoltorio() {
   t=$1; r=$2; pre=$3
   {
     echo '#!/bin/sh'
     echo "# Envoltorio: $t sale del store (musl estático, corre dentro de la imagen glibc)."
-    echo "b=\$(ls -dt /store/*-$r 2>/dev/null | while read -r d; do n=\${d##*/}; [ \"\${n#*-}\" = \"$r\" ] && { echo \"\$d\"; break; }; done)"
-    echo "[ -n \"\$b\" ] || { echo '$t: no hay artefacto -$r en /store' >&2; exit 1; }"
+    busca_artefacto "$r" "$t"
     echo "$pre"
     echo "exec \"\$b/usr/bin/$t\" \"\$@\""
   } > "$BIN/$t"
   chmod 755 "$BIN/$t"
 }
 
-for par in make:make pkgconf:pkgconf python3:python3 jq:jq rsync:rsync git:git curl:curl takana:takana; do
+# Las dinámicas: por el cargador del lab, y sólo si la caja no tiene el suyo. Así este mismo guion
+# sirve en la caja (musl, cargador nativo) y en la jaula (Arch, sin cargador musl) sin ramificar.
+# $1 = binario, $2 = receta, $3 = banderas que el envoltorio antepone a los argumentos.
+envoltorio_dinamico() {
+  t=$1; r=$2; flags=$3
+  {
+    echo '#!/bin/sh'
+    echo "# Envoltorio: $t sale del store. Es musl DINÁMICA: sin el cargador no arranca (ver cabecera)."
+    busca_artefacto "$r" "$t"
+    echo "L=\"$LAB/lib:$LAB/usr/lib:\$b/usr/lib\""
+    echo 'if [ -x /lib/ld-musl-x86_64.so.1 ]; then'
+    echo "  LD_LIBRARY_PATH=\"\$L\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}\" exec \"\$b/usr/bin/$t\" $flags \"\$@\""
+    echo 'else'
+    echo "  exec \"$LAB/lib/ld-musl-x86_64.so.1\" --library-path \"\$L\" \"\$b/usr/bin/$t\" $flags \"\$@\""
+    echo 'fi'
+  } > "$BIN/$t"
+  chmod 755 "$BIN/$t"
+}
+
+for par in make:make pkgconf:pkgconf jq:jq rsync:rsync git:git curl:curl takana:takana; do
   envoltorio "${par%%:*}" "${par##*:}" ":"
 done
-# rust necesita su LD_LIBRARY_PATH: la receta va con `rpath = false` (y por buenas razones, ver
-# recipes/rust.toml), así que el cargador no encuentra librustc_driver por su cuenta.
-for t in rustc cargo; do
-  envoltorio "$t" rust "export LD_LIBRARY_PATH=\"\$b/usr/lib\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}\""
+
+# `python3` y `cargo` no llevan banderas; `rustc` sí — son las tres que necesita el enlace en este
+# triple, las mismas que `cc-por-zig.sh` pone en la caja. `link-self-contained=no` porque este
+# toolchain no trae `rust-lld` (incompatible con el llvm-config externo).
+envoltorio_dinamico python3 python3 ""
+envoltorio_dinamico cargo    rust    ""
+envoltorio_dinamico rustc    rust    "-C linker=$BIN/cc -C linker-flavor=gcc -C link-self-contained=no"
+
+# ── El driver de C ─────────────────────────────────────────────────────────────────────────────
+# La caché de zig NO va en el home: son varios G. Misma razón que en cc-por-zig.sh.
+CACHE=${CACHE:-/work/$U/.cache/zig}
+install -d -o "$U" -g "$U" "$CACHE" 2>/dev/null || mkdir -p "$CACHE"
+for sub in cc c++; do
+  nombre=$sub; [ "$sub" = "c++" ] && nombre=c++
+  cat > "$BIN/$nombre" <<EOF
+#!/bin/sh
+# Envoltorio de \`zig $sub\` como driver de C de la jaula.
+# ⚠ Se DESCARTA el \`--target=\` que pasa cc-rs: usa triples de rustc (\`x86_64-unknown-linux-musl\`) y
+# zig sólo entiende los suyos (\`x86_64-linux-musl\`). Sin esto, \`blake3\` no compila. Medido.
+args=""
+for a in "\$@"; do
+  case "\$a" in --target=*|-target) continue ;; esac
+  args="\$args '\$a'"
+done
+export ZIG_GLOBAL_CACHE_DIR="\${ZIG_GLOBAL_CACHE_DIR:-$CACHE}"
+eval exec "$ZIG" $sub -target x86_64-linux-musl \$args
+EOF
+  chmod 755 "$BIN/$nombre"
 done
-chown -R "$U:$U" "$BIN"
+
+chown -R "$U:$U" "$BIN" 2>/dev/null || :
 
 # ── CONTROL: que CORRAN, no que existan ─────────────────────────────────────────────────────────
 # ⚠ Sin tubería: `cmd | head` devuelve el estado de `head`, o sea SIEMPRE 0, y la primera versión de
@@ -62,5 +157,32 @@ for t in make pkgconf python3 jq rsync git curl takana rustc cargo; do
     printf "  ✗ %-9s %s\n" "$t" "$(printf '%s' "$out" | head -1 | cut -c1-60)"; fallos=$((fallos+1))
   fi
 done
-[ "$fallos" -eq 0 ] || { echo "==> $fallos herramienta(s) no corren" >&2; exit 1; }
+
+# ── CONTROL: que COMPILEN, que es otra cosa ────────────────────────────────────────────────────
+# `--version` no toca ni el driver de C ni el enlazador, así que no habría visto NINGUNO de los dos
+# defectos de arriba. Estos tres sí, y ninguno necesita red:
+#   1. C con el `--target=` de cc-rs  → el defecto que tumbaba blake3
+#   2. una proc-macro                 → el enlace de dylibs, que es lo que rompía sin las banderas
+#   3. un binario estático            → que el remedio no sea peor que la enfermedad
+T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
+printf '#include <stdio.h>\nint main(void){ puts("C"); return 0; }\n' > "$T/h.c"
+if "$BIN/cc" --target=x86_64-unknown-linux-musl -o "$T/h" "$T/h.c" 2>"$T/e1" && [ "$("$T/h")" = "C" ]; then
+  printf "  ✓ %-9s con el --target= de cc-rs\n" "cc"
+else
+  printf "  ✗ %-9s %s\n" "cc" "$(head -1 "$T/e1")"; fallos=$((fallos+1))
+fi
+printf 'extern crate proc_macro;\nuse proc_macro::TokenStream;\n#[proc_macro]\npub fn m(_i: TokenStream) -> TokenStream { "fn v() -> u32 { 42 }".parse().unwrap() }\n' > "$T/pm.rs"
+if "$BIN/rustc" --edition 2021 --crate-type proc-macro --out-dir "$T" "$T/pm.rs" 2>"$T/e2" && [ -n "$(ls "$T"/*.so 2>/dev/null)" ]; then
+  printf "  ✓ %-9s proc-macro (dylib) enlazada\n" "rustc"
+else
+  printf "  ✗ %-9s %s\n" "rustc" "$(grep -m1 error "$T/e2" 2>/dev/null || head -1 "$T/e2")"; fallos=$((fallos+1))
+fi
+printf 'fn main(){ println!("estatico"); }\n' > "$T/s.rs"
+if "$BIN/rustc" --edition 2021 -o "$T/s" "$T/s.rs" 2>"$T/e3" && [ "$("$T/s")" = "estatico" ]; then
+  printf "  ✓ %-9s binario estático\n" "rustc"
+else
+  printf "  ✗ %-9s %s\n" "rustc" "$(grep -m1 error "$T/e3" 2>/dev/null || head -1 "$T/e3")"; fallos=$((fallos+1))
+fi
+
+[ "$fallos" -eq 0 ] || { echo "==> $fallos comprobación(es) en rojo" >&2; exit 1; }
 echo "==> $BIN listo"