Etapa G: el importador nix NO emite buildInputs como [deps] en recetas Rust

Hallazgo al escalar la primera tanda (12 CLI Rust): `hammer build` resuelve las deps
relativas al dir de la receta y aborta si falta `recipes/<dep>.toml`. Las recetas Rust
importadas arrastraban los buildInputs de nix como `[deps]` activas — pero nix lista el
closure MAXIMAL (todos los backends C opcionales: zlib/pcre2/openssl/jemalloc), mientras
el build Rust del lab usa las features DEFAULT de cargo (backend Rust puro: miniz_oxide
vs zlib, rustls vs openssl) o las deja opt-in (pcre2). bat→zlib, fd→jemalloc,
ripgrep→pcre2, xh→openssl fallaban al instante por deps espurias.

Las recetas Rust validadas del corpus (ripgrep/uutils) NO declaran `[deps]`: cargo
resuelve el grafo por vendoring; un sys-lib C que SÍ haga falta es adaptación per-paquete
(patch/feature, p.ej. ripgrep-no-jemalloc), no una dep de corpus.

- is_rust ⇒ los buildInputs quedan como COMENTARIO de provenance (no se pierden: señalan
  qué C podría necesitarse), no como `[deps]`. Imports C (no-Rust) intactos.
- test rust_buildinputs_are_not_active_deps; filters_nix_stdenv_noise (C) sigue válido.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 09:09:19 -04:00
co-authored by Claude Opus 4.8
parent 66dbc3a561
commit 8913eae26b
+29
View File
@@ -160,8 +160,19 @@ pub fn to_recipe_toml(pkg: &NixPkg) -> Result<String, String> {
deps.push(n);
}
}
// Rust (buildRustPackage): NO emitir los buildInputs como `[deps]` activas. nix lista el closure
// MAXIMAL (todos los backends C opcionales: zlib, pcre2, openssl, jemalloc…), pero el build Rust
// del lab usa las features DEFAULT de cargo, que suelen traer backend Rust puro (miniz_oxide vs
// zlib, rustls vs openssl) o lo dejan opt-in (pcre2). Cargo resuelve el grafo Rust por vendoring;
// un sys-lib C que SÍ haga falta es adaptación per-paquete (patch/feature, p.ej.
// ripgrep-no-jemalloc), no una dep de corpus. Las recetas Rust validadas (ripgrep/uutils) NO
// declaran `[deps]`; emitirlas rompe `hammer build` (busca recipes/<dep>.toml). Quedan como
// COMENTARIO de provenance (no se pierden: señalan qué C podría necesitarse).
let deps_block = if deps.is_empty() {
String::new()
} else if pkg.is_rust {
let list = deps.join(", ");
format!("\n# buildInputs de nix (NO deps de hammer — cargo vendorea; backend Rust por\n# defecto). Si algún sys-crate C falla, adaptá per-paquete (patch/feature): {list}\n")
} else {
let list = deps.iter().map(|d| format!("\"{d}\"")).collect::<Vec<_>>().join(", ");
format!("\n[deps]\nbuild = [{list}]\n")
@@ -417,6 +428,24 @@ mod tests {
assert!(inst.contains("cp target/release/rg /out/usr/bin/rg"), "{inst}");
}
#[test]
fn rust_buildinputs_are_not_active_deps() {
// nix lista el closure maximal (pcre2/openssl…); para Rust NO deben volverse `[deps]` (cargo
// vendorea, backend Rust por defecto) — romperían `hammer build`. Quedan como comentario.
let json = r#"{
"pname": "rg", "version": "14",
"source": { "url": "https://github.com/o/r/archive/abc.tar.gz", "output_hash_mode": "recursive" },
"is_rust": true, "main_program": "rg",
"build_inputs": ["pcre2", "openssl"]
}"#;
let pkg: NixPkg = serde_json::from_str(json).unwrap();
let toml = to_recipe_toml(&pkg).unwrap();
let recipe = hammer_core::Recipe::from_toml(&toml).unwrap();
assert!(recipe.deps.build.is_empty(), "Rust no emite deps activas: {:?}", recipe.deps.build);
assert!(toml.contains("# buildInputs de nix"), "deja provenance comentada: {toml}");
assert!(toml.contains("pcre2"), "lista los nombres en el comentario: {toml}");
}
#[test]
fn filters_nix_stdenv_noise() {
let json = r#"{