feat(hydrate): hidratación dinámica real con patchelf (LinkMode::Dynamic)

Deja de ser un error "pendiente": hydrate(.., DynamicSpec{interpreter,rpath})
detecta ELF por magic, copia los que necesitan parcheo (no hardlink: patchelf
mutaría el store) y aplica --set-interpreter/--set-rpath atómicamente
(copy→patch→rename); no-ELF y spec vacío siguen hardlinkeando. ensure_patchelf
falla limpio antes de tocar el FHS si falta la herramienta. HydrateReport.patched
cuenta los reescritos. Callers (bus/cli/bootstrap/e2e) pasan None=estático.
4 tests nuevos (incl. camino de error sin patchelf y real gated). Doc §4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-06-14 03:23:12 +00:00
co-authored by Claude Opus 4.8
parent 8a5e75344a
commit f6b337f6cb
7 changed files with 225 additions and 24 deletions
+2 -2
View File
@@ -367,7 +367,7 @@ fn assemble_rootfs(
// Cada componente se proyecta al rootfs por hardlink (mismo store, mismo filesystem).
for (name, h) in components {
let dir = store.path_of(h, name);
hammer_build::run_hydrate(&dir, staging, hammer_core::LinkMode::Static)?;
hammer_build::run_hydrate(&dir, staging, hammer_core::LinkMode::Static, None)?;
}
// El sidecar de provenance de cada receta (`.hammer/recipe.toml`) no pertenece al rootfs
@@ -714,7 +714,7 @@ fn assemble_builder(spec: &BuilderSpec, store: &Store, staging: &Path) -> Result
stage1_dir.display()
)));
}
hammer_build::run_hydrate(&stage1_dir, staging, hammer_core::LinkMode::Static)?;
hammer_build::run_hydrate(&stage1_dir, staging, hammer_core::LinkMode::Static, None)?;
// 2) Esqueleto del builder: el sandbox de build (/toolchain), el store, el work y la config.
for d in ["toolchain", "store", "work", "usr/bin", "etc/hammer/recipes"] {