diff --git a/crates/hammer-build/src/fetch.rs b/crates/hammer-build/src/fetch.rs index 2b85694e..d1c17bab 100644 --- a/crates/hammer-build/src/fetch.rs +++ b/crates/hammer-build/src/fetch.rs @@ -206,7 +206,7 @@ fn verify_sha256(path: &Path, expected: &str) -> hammer_core::Result<()> { /// (plan C.2 #5). Ver `docs/runbooks/stage1-vm-boot.md` §8. /// /// Un crate sin deps de crates.io produce un config vacío y ningún `vendor/`: inocuo. -pub fn vendor_cargo_deps(src: &Path) -> hammer_core::Result<()> { +pub fn vendor_cargo_deps(src: &Path, vendor_dir: &str) -> hammer_core::Result<()> { let locked = src.join("Cargo.lock").is_file(); let mut args: Vec<&str> = vec!["vendor"]; if locked { @@ -218,7 +218,10 @@ pub fn vendor_cargo_deps(src: &Path) -> hammer_core::Result<()> { tiempo (deps no pineadas). Committear el lock en la fuente para fijarlo (plan C.2 #5)." ); } - args.push("vendor"); + // Directorio de salida (default `vendor`; override per-receta vía `[source] cargo_vendor_dir` + // para no pisar un `vendor/` propio del proyecto). `cargo vendor` emite el `[source.*]` a stdout + // apuntando a ESTE dir, así que el `.cargo/config.toml` que persistimos abajo resuelve solo. + args.push(vendor_dir); let output = Command::new("cargo") .current_dir(src) .args(&args) @@ -358,7 +361,7 @@ mod tests { return; } - vendor_cargo_deps(src).unwrap(); + vendor_cargo_deps(src, "vendor").unwrap(); assert!(src.join(".cargo/config.toml").is_file(), "debe escribir el config de vendoring"); } @@ -382,7 +385,7 @@ mod tests { return; } // No debe fallar por falta de --locked; genera el lock y escribe el config. - vendor_cargo_deps(src).unwrap(); + vendor_cargo_deps(src, "vendor").unwrap(); assert!(src.join(".cargo/config.toml").is_file()); assert!(src.join("Cargo.lock").is_file(), "vendor sin --locked genera el lock"); } diff --git a/crates/hammer-build/src/lib.rs b/crates/hammer-build/src/lib.rs index 0b65a8f1..1c9ee52d 100644 --- a/crates/hammer-build/src/lib.rs +++ b/crates/hammer-build/src/lib.rs @@ -209,7 +209,10 @@ pub fn build( // Extractor de workspaces (Etapa G): si el root es virtual, resuelve `-p ` del bin. inject_cargo_package_selector(&mut eff_recipe, &src_tree); tracing::info!("cargo: vendoreando deps para build offline"); - fetch::vendor_cargo_deps(&src_tree)?; + fetch::vendor_cargo_deps( + &src_tree, + eff_recipe.source.cargo_vendor_dir.as_deref().unwrap_or("vendor"), + )?; } // Recetas Go: análogo a Cargo. Si el árbol trae `go.mod`, vendoreamos los módulos (red en el diff --git a/crates/hammer-core/src/recipe.rs b/crates/hammer-core/src/recipe.rs index 0df369d1..d565e54f 100644 --- a/crates/hammer-core/src/recipe.rs +++ b/crates/hammer-core/src/recipe.rs @@ -60,6 +60,12 @@ pub struct Source { #[serde(default = "default_strip")] pub strip_components: usize, // --- común --- + /// Destino del vendoreo cargo (default `vendor`). Override per-receta para proyectos que + /// commitean su PROPIO `vendor/` (p.ej. mise → `vendor/aqua-registry`): `cargo vendor` borra + /// lo que no reconoce, así que se vendorea a un subdir aparte para no pisarlo. El fragmento + /// `.cargo/config.toml` que emite `cargo vendor` ya apunta al dir que se le pasa. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cargo_vendor_dir: Option, #[serde(default)] pub patches: Vec, } diff --git a/tandas/deferred-openssl-xcb/mise.toml b/tandas/deferred-openssl-xcb/mise.toml index ec4cbc10..4c928145 100644 --- a/tandas/deferred-openssl-xcb/mise.toml +++ b/tandas/deferred-openssl-xcb/mise.toml @@ -8,6 +8,9 @@ version = "2026.7.0" [source] repo = "https://github.com/jdx/mise" commit = "857b73f6a6b39a3bc90c44119a1e86ee11bd7273" +# mise commitea su PROPIO vendor/aqua-registry (registry.yml 3.1MB que build.rs lee). El +# `cargo vendor` de hammer, por defecto en `vendor/`, lo borraría. Lo mandamos aparte. +cargo_vendor_dir = ".hammer-cargo-vendor" [build] compiler = "zig-cc"