1) `publicar-webs.sh` corría como root y hacía el `git pull` igual: git dejó 24 entradas de root dentro del .git de /work/sergio/tawasuyu —refs, logs, config, directorios de objetos— y el dueño se quedó sin poder ni hacer `fetch` («unable to append to .git/logs/refs/remotes/origin/main»). El clon quedó congelado 21 commits atrás sin que nada fallara del lado de root. Ahora el pull va COMO EL DUEÑO, y los cuatro clones quedaron con sus permisos. 2) `cc-por-zig.sh` elegía el rust con `ls -d …/*-rust | tail -1`, que es el último ALFABÉTICO: certificaba `fe277b32…` mientras la jaula usaba el vigente `6441302d…`. Dos artefactos distintos, uno certificado y otro en uso. Ahora pregunta por el hash vigente de la receta y sólo cae al más reciente —con el nombre exacto— si no puede. Lo detectó el agente comparando los dos guiones. 3) `rustdoc` entra en `tools`: el artefacto salía con cargo y rustc y SIN rustdoc, o sea que en una caja takana `cargo doc` no existe. `docs = false` se queda —la documentación de la std es otra decisión, cientos de MB—. Radio medido: 0 dependientes de build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
141 lines
8.5 KiB
Bash
Executable File
141 lines
8.5 KiB
Bash
Executable File
#!/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" <<EOF
|
|
#!/bin/sh
|
|
# Generado por scripts/servidor/cc-por-zig.sh — envoltorio de \`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
|
|
|
|
# rustc NO usa el driver por su cuenta: su enlazador por defecto en este triple es `ld.lld` invocado
|
|
# directo. Las tres banderas son necesarias —y `link-self-contained=no` lo es porque este toolchain
|
|
# no trae `rust-lld` (incompatible con el llvm-config externo); la variante fina `=-linker` es
|
|
# INESTABLE en este triple y exige `-Z`.
|
|
mkdir -p /etc/profile.d
|
|
cat > /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 <stdio.h>\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"
|