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 { // (`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"