Etapa G Fase 3: flags autotools $CBUILD/$CHOST — el lab provee el triple nativo

El residuo autotools de los imports de Alpine (configure --build=$CBUILD --host=$CHOST
del abuild) ya no es trabajo a mano:

- El lab exporta CBUILD/CHOST con el triple nativo SANEADO (x86_64-linux-musl, el
  mismo que el wrapper zig-cc emite) ⇒ las fases traducidas de Alpine que referencian
  $CBUILD/$CHOST literal resuelven en runtime en vez de quedar vacías (config.guess
  detectaría x86_64-alpine-linux-musl, vendor que zig rechaza).
- La heurística autotools inyecta --build/--host al triple saneado cuando la receta no
  los puso ya (juicio per-paquete gana). build==host ⇒ NATIVO: autotools sigue corriendo
  sus AC_RUN tests; sólo normaliza el triple.
- Inerte para Cargo/CMake/Meson (no leen esas envs ni el triple).

VALIDADO REAL: e2e autotools BUILDEA (configure 'cross compiling... no', sella+corre).
3 tests nuevos de heurística + import comment actualizado.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 08:57:05 -04:00
co-authored by Claude Opus 4.8
parent 1180bd9154
commit 41aec17048
2 changed files with 63 additions and 5 deletions
+59 -2
View File
@@ -217,6 +217,15 @@ pub fn build(
env.push(("AR".into(), "llvm-ar".into()));
}
}
// Autotools (Etapa G Fase 3): exporta CBUILD/CHOST con el triple nativo SANEADO del sandbox
// (`x86_64-linux-musl`, el mismo que el wrapper zig-cc emite). Las recetas importadas de Alpine
// traducen `./configure --build=$CBUILD --host=$CHOST` literal del abuild (`translate_abuild` los
// deja intactos a propósito); sin estas envs `$CBUILD/$CHOST` quedan VACÍAS y autotools cae a
// `config.guess`, que detecta `x86_64-alpine-linux-musl` — vendor que zig NO conoce
// (`UnknownOperatingSystem`). Resuelve el residuo autotools que antes era trabajo a mano. Inerte
// para Cargo/CMake/Meson (no leen estas envs).
env.push(("CBUILD".into(), SANDBOX_NATIVE_TARGET.to_string()));
env.push(("CHOST".into(), SANDBOX_NATIVE_TARGET.to_string()));
if let Some(cache) = &cfg.cache_root {
std::fs::create_dir_all(cache.join("zig"))?;
}
@@ -450,12 +459,26 @@ fn resolve_phases(recipe: &Recipe, src: &Path) -> hammer_core::Result<Phases> {
let flags = recipe.build.flags.join(" ");
let flags_suffix = if flags.is_empty() { String::new() } else { format!(" {flags}") };
// Autotools (Etapa G Fase 3): fija `--build`/`--host` al triple nativo saneado para que
// `config.guess` no detecte el vendor `alpine` que zig rechaza, e iguala lo que el wrapper zig-cc
// produce. Sólo si la receta no los puso ya en sus flags (juicio per-paquete gana). build==host
// ⇒ NATIVO (no cross): autotools sigue corriendo sus AC_RUN tests; sólo normaliza el triple.
let autotools_triple = if recipe.build.flags.iter().any(|f| {
f.starts_with("--build=") || f.starts_with("--host=")
}) {
String::new()
} else {
format!(" --build={SANDBOX_NATIVE_TARGET} --host={SANDBOX_NATIVE_TARGET}")
};
if out.configure.is_none() {
out.configure = match bs {
BuildSys::AutoconfRaw => Some(format!(
"autoreconf -fi && ./configure --prefix=/usr{flags_suffix}"
"autoreconf -fi && ./configure --prefix=/usr{autotools_triple}{flags_suffix}"
)),
BuildSys::AutoconfReady => Some(format!("./configure --prefix=/usr{flags_suffix}")),
BuildSys::AutoconfReady => {
Some(format!("./configure --prefix=/usr{autotools_triple}{flags_suffix}"))
}
BuildSys::CMake => Some(format!(
"cmake -S . -B _build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release{flags_suffix}"
)),
@@ -623,6 +646,40 @@ commit = "deadbeef"
assert!(c.ends_with("--enable-foo"), "{c}");
}
#[test]
fn autotools_injects_native_triple() {
// Etapa G Fase 3: configure heurístico fija --build/--host al triple saneado.
let d = tempfile::tempdir().unwrap();
touch(d.path(), "configure.ac");
touch(d.path(), "configure");
let c = resolve_phases(&recipe(&[]), d.path()).unwrap().configure.unwrap();
assert!(c.contains("--build=x86_64-linux-musl"), "{c}");
assert!(c.contains("--host=x86_64-linux-musl"), "{c}");
}
#[test]
fn autotools_triple_yields_to_recipe_flags() {
// Si la receta ya fija el triple (juicio per-paquete), no duplicamos.
let d = tempfile::tempdir().unwrap();
touch(d.path(), "configure.ac");
touch(d.path(), "configure");
let c = resolve_phases(&recipe(&["--host=aarch64-linux-musl"]), d.path())
.unwrap()
.configure
.unwrap();
assert!(c.contains("--host=aarch64-linux-musl"), "{c}");
assert!(!c.contains("x86_64-linux-musl"), "no inyecta si la receta ya lo puso ({c})");
}
#[test]
fn cmake_ignores_autotools_triple() {
// El triple es exclusivo de autotools; CMake/Meson/Cargo no lo reciben.
let d = tempfile::tempdir().unwrap();
touch(d.path(), "CMakeLists.txt");
let c = resolve_phases(&recipe(&[]), d.path()).unwrap().configure.unwrap();
assert!(!c.contains("--host="), "{c}");
}
#[test]
fn detect_cmake() {
let d = tempfile::tempdir().unwrap();
+4 -3
View File
@@ -106,7 +106,7 @@ pub fn recipe_from_apkbuild(text: &str) -> Result<String, String> {
let mut s = String::from("\n[build.phases]\n");
if let Some(b) = compile {
let t = translate_abuild(&b, &pkgname, &version);
s.push_str(&format!("# de build() de Alpine (traducido; revisá $CBUILD/$CHOST/--shared para estático):\ncompile = {}\n", toml_multiline(&t)));
s.push_str(&format!("# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático):\ncompile = {}\n", toml_multiline(&t)));
}
if let Some(p) = install {
let t = translate_abuild(&p, &pkgname, &version);
@@ -249,8 +249,9 @@ fn func_body(text: &str, name: &str) -> Option<String> {
/// Traduce las variables de abuild a su equivalente en el lab de hammer. Substituciones SEGURAS
/// (inequívocas): `$pkgdir`→`/out` (el DESTDIR del lab), `$pkgname`→nombre, `$pkgver`→versión.
/// NO toca `$CBUILD/$CHOST/$srcdir/$builddir` ni `--shared` (juicio por-paquete; quedan para el
/// humano, marcados en el comentario de la fase). `make DESTDIR="$pkgdir"` → `make DESTDIR="/out"`.
/// NO toca `$CBUILD/$CHOST/$srcdir/$builddir` ni `--shared`. `$CBUILD/$CHOST` los RESUELVE el lab en
/// runtime (env con el triple nativo saneado, Etapa G Fase 3 — `hammer-build`); `$srcdir/$builddir` y
/// `--shared` quedan para el humano. `make DESTDIR="$pkgdir"` → `make DESTDIR="/out"`.
fn translate_abuild(body: &str, name: &str, version: &str) -> String {
body.replace("${pkgdir}", "/out")
.replace("$pkgdir", "/out")