#!/bin/sh # Le da a la caja un DRIVER DE C — hoy no tiene ninguno — usando el zig que ya está en su store. # # ── POR QUÉ (2026-09-18) ──────────────────────────────────────────────────────────────────────── # `command -v cc gcc clang zig` en la caja no devuelve NADA. Eso no es sólo incómodo: es lo que hace # que `rustc`, con el compilador propio ya sellado, **no pueda enlazar una sola dylib** —y ahí entran # TODAS las proc-macros: serde_derive, clap_derive…—: # # ld.lld: error: unable to find library -lgcc_s # ld.lld: error: unable to find library -lc # # Y el `-L /usr/lib` obvio EMPEORA las cosas: rompe los estáticos, que hoy funcionan, porque rustc # enlaza musl static-PIE con su copia self-contained y ese camino le hace preferir la `libc.a` del # sistema, que no es PIC. El camino de librerías no puede ser global: el estático lo quiere fuera y # el dinámico lo quiere dentro. Quien sabe cuál es cuál es un driver de C. (SDD 28 §6.52.) # # ⚠ NO introduce nada nuevo en el corpus: usa el `seed-zig` que YA está en el store —producto del # bootstrap (SDD 11), como `stage1-rootfs`— y sólo lo envuelve. Volver a zig el compilador de C # OFICIAL de la distro sería otra cosa, y es decisión de ADR, no de un guion de puesta a punto. # # Idempotente. Se corre EN LA CAJA, como root. set -eu STORE=${STORE:-/store} 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" >&2; exit 1; } "$ZIG" version >/dev/null 2>&1 || { echo "el zig de $ZIG no ejecuta en esta caja" >&2; exit 1; } echo "==> zig $("$ZIG" version) en $ZIG" # La caché de zig NO va en la raíz: son 5,9 G y ya nos costó una caída entera (SDD 28 §6.50 quinquies). CACHE=${CACHE:-/work/zig-cache} mkdir -p "$CACHE" for par in "cc:cc" "c++:c++"; do nombre=${par%%:*}; sub=${par##*:} cat > "/usr/bin/$nombre" < /etc/profile.d/rust-enlazador.sh <<'EOF' # El enlace de rust pasa por el driver de C (ver /usr/bin/cc). Sin esto no enlaza ninguna dylib, # o sea ninguna proc-macro. SDD 28 §6.52. export RUSTFLAGS="${RUSTFLAGS:-} -C linker=cc -C linker-flavor=gcc -C link-self-contained=no" EOF chmod 644 /etc/profile.d/rust-enlazador.sh # ⚠ …Y RESULTA QUE EN ESTA CAJA `/etc/profile.d` NO LO LEÍA NADIE: `/etc/profile` **no existía**. # O sea que dejar ahí el fichero habría sido exactamente la avería que este repo persigue —presente, # con contenido, y sin efecto—. Se descubrió con el control de abajo, que pregunta por la VARIABLE en # una shell de login y no por el fichero. De paso revive el `bash_completion.sh` que ya estaba ahí, # igual de mudo. No se toca el PATH: el de la caja lo fija el init y pisarlo desde acá sería cambiar # algo que nadie pidió. if [ ! -f /etc/profile ]; then cat > /etc/profile <<'EOF' # /etc/profile — lo lee toda shell de LOGIN. Existe para que `/etc/profile.d/*.sh` sirva de algo: # el directorio estaba y nadie lo leía (2026-09-18). for _f in /etc/profile.d/*.sh; do [ -r "$_f" ] && . "$_f" done unset _f EOF chmod 644 /etc/profile echo " (creado /etc/profile: el directorio profile.d existía y no lo leía nadie)" fi # ── CONTROLES: lo que importa es que COMPILE, no que el fichero exista ─────────────────────────── T=$(mktemp -d); trap 'rm -rf "$T"' EXIT printf '#include \nint main(void){ puts("C en takana"); return 0; }\n' > "$T/h.c" cc -o "$T/h" "$T/h.c" 2>"$T/err" || { echo "✗ el cc nuevo no compila un hola mundo:" >&2; head -5 "$T/err" >&2; exit 1; } 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" # ⚠ EL RUST QUE SE CERTIFICA TIENE QUE SER EL VIGENTE (2026-09-18). Esto decía # `ls -d …/*-rust | tail -1`, que es el ÚLTIMO ALFABÉTICO: elegía `fe277b32…` mientras el vigente # —y el que usa la jaula— es `6441302d…`. La caja certificaba un rust y el agente usaba otro; lo # detectó el agente de adentro comparando los dos guiones. # Se pregunta por el hash vigente de la receta; si no se puede, se cae al más RECIENTE con el # nombre EXACTO — `*-rust` también casa con `…-rust-toolchain-bin`. R="" if [ -x ./target/release/takana ] && [ -f recipes/rust.toml ]; then h=$(./target/release/takana --store "$STORE" hash recipes/rust.toml 2>/dev/null | grep -o "b3:[0-9a-f]*" | head -1) [ -n "$h" ] && [ -d "$STORE/${h#b3:}-rust" ] && R="$STORE/${h#b3:}-rust" fi [ -n "$R" ] || R=$(ls -dt "$STORE"/*-rust 2>/dev/null | while read -r d; do nn=${d##*/}; [ "${nn#*-}" = "rust" ] && { echo "$d"; break; }; done) 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" # shellcheck disable=SC2086 LD_LIBRARY_PATH=$R/usr/lib "$R/usr/bin/rustc" -C linker=cc -C linker-flavor=gcc -C link-self-contained=no \ --edition 2021 --crate-type proc-macro --out-dir "$T" "$T/pm.rs" 2>"$T/err2" || { echo "✗ la proc-macro sigue sin enlazar:" >&2; grep -m3 error "$T/err2" >&2; exit 1; } so=$(ls "$T"/*.so 2>/dev/null | head -1) [ -n "$so" ] || { echo "✗ no salió la dylib de la proc-macro" >&2; exit 1; } printf 'extern crate pm;\npm::m!();\nfn main(){ println!("proc-macro: {}", v()); }\n' > "$T/u.rs" LD_LIBRARY_PATH=$R/usr/lib "$R/usr/bin/rustc" -C linker=cc -C linker-flavor=gcc -C link-self-contained=no \ --edition 2021 --extern pm="$so" -o "$T/u" "$T/u.rs" 2>"$T/err3" || { echo "✗ no compila el programa que USA la macro:" >&2; grep -m3 error "$T/err3" >&2; exit 1; } echo " ✓ rust: $("$T/u")" # Y el control que evita el remedio peor que la enfermedad: el estático tiene que seguir andando. printf 'fn main(){ println!("estático ok"); }\n' > "$T/s.rs" LD_LIBRARY_PATH=$R/usr/lib "$R/usr/bin/rustc" --edition 2021 -o "$T/s" "$T/s.rs" 2>/dev/null \ && echo " ✓ rust estático (sin banderas): $("$T/s")" \ || { echo "✗ el estático SIN banderas dejó de enlazar — eso es peor que el problema" >&2; exit 1; } else echo " (no hay rust en el store: me salto sus controles)" fi # ── CONTROL DEL ENTORNO: la variable, no el fichero ───────────────────────────────────────────── # Que el `.sh` exista no dice nada; lo que importa es que una shell de LOGIN lo traiga puesto. rf=$(sh -lc 'printf "%s" "$RUSTFLAGS"' 2>/dev/null || true) case "$rf" in *"linker-flavor=gcc"*) echo " ✓ entorno: una shell de login trae RUSTFLAGS puesto" ;; *) echo "✗ /etc/profile.d/rust-enlazador.sh existe pero una shell de login NO lo recoge (RUSTFLAGS='$rf')" >&2; exit 1 ;; esac echo "la caja tiene driver de C"