tawasuyu

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 1488Shell 182Rust 107Markdown 963619 Definitions

harness: wrapper linker-driver que quita -static en links -shared

Las extensiones Python (python.extension_module → shared_module) son .so que el
intérprete del sandbox debe dlopen; intrínsecamente dinámicas. El harness inyecta
LDFLAGS=-static para link=static, y meson/autotools lo aplican a TODOS los targets
por igual ⇒ el .so sale ET_EXEC y dlopen da "Exec format error" (el muro de
gobject-introspection: _giscanner.cpython-312.so no cargaba).

Fix estructural (elegido sobre flip-a-dynamic): ensure_layout materializa en el
rootfs dos wrappers (hammer-zig-cc/-cxx sobre zig cc/c++) que QUITAN -static SÓLO
cuando el link lleva -shared. -static+-shared es contradictorio ⇒ correcto por
construcción: transparente (byte-idéntico) para todo link normal, arregla TODA
extensión Python futura, no sólo g-i.

Hash-safe: el ArtifactHash se deriva de inputs (receta+deps), no de los bytes ⇒
no re-hashea ninguno de los 700+ sellados (cache-hit intacto); sólo cambia builds
nuevos con .so dinámicos. Verificado: rota positional params, preserva orden y
espacios; transparente sin -shared.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

437d5bb1035a50d73f60e995fe3200d33396c5ca · by sergio · (2 months ago) · parent ee2f3b6de · browse the tree at this commit

Semantic delta · 5

crates/hammer-build/src/sandbox.rs

1 files · +60 −2

diff --git a/crates/hammer-build/src/sandbox.rs b/crates/hammer-build/src/sandbox.rs
index efabadfa..3bdc9fed 100644
--- a/crates/hammer-build/src/sandbox.rs
+++ b/crates/hammer-build/src/sandbox.rs
@@ -417,8 +417,11 @@ impl Sandbox {
             // rompe la reproducibilidad — un no-determinismo que Stage 2 cazó (runbook §8c, SDD 09
             // §2). De paso cierra el SIGILL de AVX en qemu64 (runbook §7). Las rutas SIMD que sí
             // necesita el runtime (blake3/sha2) son asm con dispatch en runtime: siguen presentes.
-            ("CC", "zig cc -mcpu=baseline"),
-            ("CXX", "zig c++ -mcpu=baseline"),
+            // Driver vía wrapper (ensure_layout lo materializa en /usr/local/bin): idéntico a
+            // `zig cc -mcpu=baseline` salvo que quita `-static` en links `-shared` (extensiones
+            // Python y demás `.so` dlopen-ables). Ver write_cc_wrappers.
+            ("CC", CC_WRAPPER),
+            ("CXX", CXX_WRAPPER),
             ("AR", "zig ar"),
             ("LC_ALL", "C"),
             ("LANG", "C"),
@@ -517,6 +520,61 @@ pub fn ensure_layout(rootfs: &Path, zig_dir: &Path) -> hammer_core::Result<()> {
             zig_dir.display()
         )));
     }
+    write_cc_wrappers(rootfs)?;
+    Ok(())
+}
+
+/// Nombres (en `/usr/local/bin`, que está en el PATH del sandbox) del driver de compilador que
+/// usan CC/CXX por defecto. Ver [`write_cc_wrappers`].
+pub const CC_WRAPPER: &str = "hammer-zig-cc";
+pub const CXX_WRAPPER: &str = "hammer-zig-cxx";
+
+/// Materializa en el rootfs dos wrappers de linker-driver (`hammer-zig-cc`/`-cxx`) que envuelven a
+/// `zig cc`/`zig c++`. Su ÚNICO efecto es QUITAR `-static` cuando el link también lleva `-shared`.
+///
+/// El harness inyecta `LDFLAGS=-static` para las recetas `link=static` (binario monolítico que pide
+/// la hidratación primaria). Pero meson/autotools aplican ese LDFLAGS a TODOS los targets por igual,
+/// incluidas las extensiones Python (`python.extension_module` → `shared_module`): un `.so` que el
+/// intérprete del sandbox debe `dlopen`. `-static` + `-shared` es contradictorio ⇒ el `.so` sale
+/// ET_EXEC y `dlopen` da `Exec format error` (el muro de gobject-introspection: `_giscanner.so`).
+///
+/// Quitar `-static` SÓLO cuando hay `-shared` es correcto por construcción: un objeto compartido
+/// nunca debe ser estático. Para todo link normal (ejecutable/`.a`) el wrapper es transparente ⇒
+/// byte-idéntico a `zig cc`. El ArtifactHash se deriva de inputs (receta+deps), no de los bytes ⇒
+/// este cambio NO re-hashea nada sellado; sólo destraba builds nuevos con extensiones dinámicas.
+fn write_cc_wrappers(rootfs: &Path) -> hammer_core::Result<()> {
+    use std::os::unix::fs::PermissionsExt;
+    let bindir = rootfs.join("usr/local/bin");
+    std::fs::create_dir_all(&bindir).map_err(|e| {
+        hammer_core::Error::Other(anyhow::anyhow!("no pude crear {}: {e}", bindir.display()))
+    })?;
+    // `case " $* "` detecta `-shared` como palabra completa; el bucle rota los positional params
+    // (pop del frente, push al fondo salvo `-static`) ⇒ filtra preservando orden y seguro con espacios.
+    let body = |driver: &str| {
+        format!(
+            "#!/bin/sh\n\
+             case \" $* \" in\n\
+             \x20 *\" -shared \"*)\n\
+             \x20   n=$#\n\
+             \x20   while [ \"$n\" -gt 0 ]; do\n\
+             \x20     arg=$1; shift\n\
+             \x20     [ \"$arg\" = \"-static\" ] || set -- \"$@\" \"$arg\"\n\
+             \x20     n=$((n - 1))\n\
+             \x20   done\n\
+             \x20   ;;\n\
+             esac\n\
+             exec {driver} -mcpu=baseline \"$@\"\n"
+        )
+    };
+    for (name, driver) in [(CC_WRAPPER, "zig cc"), (CXX_WRAPPER, "zig c++")] {
+        let path = bindir.join(name);
+        std::fs::write(&path, body(driver)).map_err(|e| {
+            hammer_core::Error::Other(anyhow::anyhow!("no pude escribir {}: {e}", path.display()))
+        })?;
+        std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755)).map_err(|e| {
+            hammer_core::Error::Other(anyhow::anyhow!("no pude chmod {}: {e}", path.display()))
+        })?;
+    }
     Ok(())
 }