diff --git a/crates/hammer-build/src/sandbox.rs b/crates/hammer-build/src/sandbox.rs index 7e385680..6c07098a 100644 --- a/crates/hammer-build/src/sandbox.rs +++ b/crates/hammer-build/src/sandbox.rs @@ -90,8 +90,8 @@ impl Sandbox { let tail = Arc::new(Mutex::new(VecDeque::::with_capacity(TAIL_LINES))); let out = child.stdout.take().expect("stdout piped"); let err = child.stderr.take().expect("stderr piped"); - let h_out = spawn_pump(out, tail.clone(), false); - let h_err = spawn_pump(err, tail.clone(), true); + let h_out = spawn_pump(out, tail.clone()); + let h_err = spawn_pump(err, tail.clone()); let status = child .wait() @@ -201,12 +201,19 @@ impl Sandbox { } } -/// Lanza un thread que lee `reader` línea a línea, la reenvía al stream del padre -/// (`to_stderr` decide cuál) y la acumula en el ring `tail`. Devuelve el handle para join. +/// Lanza un thread que lee `reader` línea a línea, la reenvía a stderr del padre y la +/// acumula en el ring `tail`. Devuelve el handle para join. +/// +/// El tee va SIEMPRE a stderr (tanto el stdout como el stderr del build): stdout del proceso +/// queda reservado para el resultado legible-por-máquina (el content-hash que imprime la CLI, +/// p. ej. `bootstrap stage1`). Antes el stdout del build se teeaba al stdout del padre y se +/// colaba en capturas tipo `PRIME=$(hammer … bootstrap stage1)`; con builds reales (no cacheados, +/// p. ej. el rebuild in-VM) eso son miles de líneas de `configure`/`make` ⇒ el argumento crece y +/// la fase de verificación siguiente (`--rootfs "$PRIME"`) revienta el exec con E2BIG +/// (Argument list too long). Va a stderr, junto a los logs de tracing. Ver runbook §8c. fn spawn_pump( reader: R, tail: Arc>>, - to_stderr: bool, ) -> std::thread::JoinHandle<()> { std::thread::spawn(move || { let buf = BufReader::new(reader); @@ -215,12 +222,8 @@ fn spawn_pump( Ok(l) => l, Err(_) => break, }; - // Tee en vivo al padre (sin bufferizar de más: el build puede tardar). - if to_stderr { - let _ = writeln!(std::io::stderr(), "{line}"); - } else { - let _ = writeln!(std::io::stdout(), "{line}"); - } + // Tee en vivo al padre por stderr (sin bufferizar de más: el build puede tardar). + let _ = writeln!(std::io::stderr(), "{line}"); if let Ok(mut g) = tail.lock() { push_tail(&mut g, line, TAIL_LINES); } diff --git a/scripts/selfhost-verify.sh b/scripts/selfhost-verify.sh index dd255aa0..ff25ab83 100755 --- a/scripts/selfhost-verify.sh +++ b/scripts/selfhost-verify.sh @@ -83,8 +83,14 @@ fi # 3) Inyectar los módulos del kernel DE ESTA máquina al toolchain (el builder los carga: overlay para # el sandbox bwrap, e1000 para la red del vendoring). Deben matchear el kernel que bootea la VM. -say "inyectar overlay.ko + e1000.ko (kernel $(uname -r)) al toolchain" -moddir="/lib/modules/$(uname -r)" +# La versión de los módulos DEBE matchear el kernel que bootea la VM ($KERNEL), no el del host +# corriendo el script ($(uname -r)) — en máquinas con varios kernels instalados difieren y el +# insmod falla con "version magic mismatch" → sin overlayfs, bwrap revienta. Sacamos la versión +# del propio bzImage; si no se puede, caemos a uname -r. +KVER="$(file -bL "$KERNEL" 2>/dev/null | grep -oE 'version [0-9][^ ]*' | awk '{print $2}')" +[[ -n "$KVER" && -d "/lib/modules/$KVER" ]] || KVER="$(uname -r)" +say "inyectar overlay.ko + e1000.ko (kernel $KVER) al toolchain" +moddir="/lib/modules/$KVER" for m in overlay e1000; do src="$(find "$moddir" -name "$m.ko*" 2>/dev/null | head -1)" if [[ -n "$src" ]]; then