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 1378Shell 129Rust 79Markdown 732463 Definiciones

build: flags estáticos vía `cargo rustc -- …` (no RUSTFLAGS) para no romper proc-macros

Tercera iteración del fix de la corrida real (runbook §8). +crt-static (y
relocation-model=static) en RUSTFLAGS rompen los proc-macros en build nativo:
RUSTFLAGS alcanza a las deps, y un proc-macro es un dylib PIC que no puede ser
estático ("cannot produce proc-macro for clap_derive ... crate types").

Fix: pasar los flags de codegen del binario por `cargo rustc --release … -- <flags>`,
que los aplica SÓLO a la crate top-level, dejando proc-macros/deps intactos. Así el
binario final es crt-static + relocation-model=static (AUTOCONTENIDO y ET_EXEC sin
interpreter, como busybox: sin libc.so, sin loader, sin soname). El linker (zig cc)
sigue por RUSTFLAGS. musl queda --disable-shared (no se necesita loader).

Validado por unit tests; la corrida real rebuildea hammerd/arje-zero crt-static.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

879af17f5c30503eea45da2c459d8377f7c1810b · por Sergio · (hace 4 meses) · padre ce7b570a6 · ver el árbol en este commit

Delta semántico · 3

crates/hammer-build/src/lib.rs

2 archivos · +22 −17

diff --git a/crates/hammer-build/src/lib.rs b/crates/hammer-build/src/lib.rs
index 1a2aabcb..28fb1877 100644
--- a/crates/hammer-build/src/lib.rs
+++ b/crates/hammer-build/src/lib.rs
@@ -288,23 +288,27 @@ fn resolve_phases(recipe: &Recipe, src: &Path) -> hammer_core::Result<Phases> {
                     // (`UnknownOperatingSystem`); lo reescribimos a `x86_64-linux-musl`. Se usa como
                     // CC (cc-rs) y como linker (RUSTFLAGS, target-agnóstico — el default sería
                     // cc/gcc, ausente en el sandbox zig-only).
-                    let crt = match recipe.build.link {
-                        // Estático = AUTOCONTENIDO: el rust de Alpine enlaza musl dinámico por
-                        // defecto (deja `DT_NEEDED libc.musl-x86_64.so.1` y rompe el boot — el
-                        // loader no resuelve memcpy/…; ver runbook §8). `+crt-static` mete musl
-                        // dentro; `relocation-model=static` lo hace ET_EXEC sin interpreter (vía de
-                        // oro de hammer, como busybox: sin libc.so, sin loader, sin soname).
-                        LinkMode::Static => " -C target-feature=+crt-static -C relocation-model=static",
+                    // Flags de codegen del BINARIO FINAL, vía `cargo rustc -- …` (NO RUSTFLAGS): en
+                    // build nativo RUSTFLAGS también alcanza a los proc-macros (deps), y
+                    // `+crt-static`/`relocation-model=static` los rompe ("cannot produce
+                    // proc-macro" — un proc-macro es un dylib PIC). `cargo rustc -- …` los aplica
+                    // sólo a la crate top-level, dejando los proc-macros intactos.
+                    let bin_flags = match recipe.build.link {
+                        // Estático AUTOCONTENIDO y no-PIE: `+crt-static` mete musl dentro (sin
+                        // DT_NEEDED — el rust de Alpine enlaza musl dinámico por defecto, lo que deja
+                        // `libc.musl-x86_64.so.1` y rompe el boot, runbook §8); `relocation-model=static`
+                        // ⇒ ET_EXEC sin interpreter (vía de oro: como busybox, sin libc.so/loader/soname).
+                        LinkMode::Static => "-C target-feature=+crt-static -C relocation-model=static",
                         // Dinámico: apaga crt-static para poder dlopen (necesita el loader/musl en el rootfs).
-                        LinkMode::Dynamic => " -C target-feature=-crt-static",
+                        LinkMode::Dynamic => "-C target-feature=-crt-static",
                     };
                     Some(format!(
                         "printf '%s\\n' '#!/bin/sh' 'for a do' \
                          'case \"$a\" in --target=*) a=--target=x86_64-linux-musl ;; esac' \
                          'set -- \"$@\" \"$a\"' 'shift' 'done' 'exec zig cc \"$@\"' \
                          > \"$PWD/.hammer-zig-cc\" && chmod +x \"$PWD/.hammer-zig-cc\" && \
-                         CC=\"$PWD/.hammer-zig-cc\" RUSTFLAGS=\"-C linker=$PWD/.hammer-zig-cc{crt}\" \
-                         cargo build --release --locked --offline{flags_suffix}"
+                         CC=\"$PWD/.hammer-zig-cc\" RUSTFLAGS=\"-C linker=$PWD/.hammer-zig-cc\" \
+                         cargo rustc --release --locked --offline{flags_suffix} -- {bin_flags}"
                     ))
                 } else {
                     // CROSS real (otro target que zig sí entiende): `--target` + linker por env
@@ -433,13 +437,13 @@ commit = "deadbeef"
         let p = resolve_phases(&recipe(&[]), d.path()).unwrap();
         assert!(p.configure.is_none(), "cargo no usa configure");
         let c = p.compile.unwrap();
-        assert!(c.contains("cargo build --release"), "{c}");
+        // Nativo: `cargo rustc` (no `cargo build`) para pasar los flags de codegen tras `--` sólo
+        // a la crate final; sin `--target` (lo cross sí; el único --target es el del wrapper saneador).
+        assert!(c.contains("cargo rustc --release"), "{c}");
         assert!(c.contains("--locked") && c.contains("--offline"), "{c}");
-        // Nativo: `cargo build` NO lleva --target (lo cross sí); el único --target del comando es
-        // el del wrapper saneador del triple alpine.
         assert!(!c.contains("--offline --target"), "nativo: cargo sin --target ({c})");
         assert!(c.contains("a=--target=x86_64-linux-musl"), "wrapper sanea el triple ({c})");
-        assert!(c.contains("target-feature=+crt-static"), "estático = autocontenido ({c})");
+        assert!(c.contains("-- -C target-feature=+crt-static"), "crt-static scoped tras -- ({c})");
         assert!(c.contains("relocation-model=static"), "no-PIE: ET_EXEC sin interpreter ({c})");
         assert!(c.contains("CC=\"$PWD/.hammer-zig-cc\""), "CC = wrapper para cc-rs ({c})");
         assert!(c.contains("RUSTFLAGS=\"-C linker=$PWD/.hammer-zig-cc"), "linker por RUSTFLAGS ({c})");
@@ -478,7 +482,8 @@ commit = "deadbeef"
         let d = tempfile::tempdir().unwrap();
         touch(d.path(), "Cargo.toml");
         let p = resolve_phases(&recipe(&["--features", "gpu"]), d.path()).unwrap();
-        assert!(p.compile.unwrap().ends_with("--features gpu"));
+        // Los flags de la receta van como args de cargo (antes del `--` que separa los de rustc).
+        assert!(p.compile.unwrap().contains("--features gpu -- "));
     }
 
     #[test]
diff --git a/recipes/musl.toml b/recipes/musl.toml
index b3c59692..e46ef5a3 100644
--- a/recipes/musl.toml
+++ b/recipes/musl.toml
@@ -18,8 +18,8 @@ link     = "static"
 
 [build.phases]
 # El sandbox exporta CC="zig cc", DESTDIR=/out y PREFIX=/usr. musl trae su propio `configure`.
-# Sólo estático: los componentes Rust se compilan crt-static (autocontenidos, sin libc.so), así que
-# el rootfs no necesita el loader/shared musl. Ver runbooks/stage1-vm-boot.md §8.
+# Sólo estático: los componentes Rust se compilan crt-static + no-PIE (autocontenidos, ET_EXEC sin
+# interpreter), así que el rootfs no necesita el loader/shared de musl. Ver runbooks §8.
 configure = "./configure --prefix=/usr --disable-shared --enable-static"
 compile   = "make -j\"$(nproc)\""
 install   = "make install"