diff --git a/crates/takana-build/src/lib.rs b/crates/takana-build/src/lib.rs index 8802c535..87326911 100644 --- a/crates/takana-build/src/lib.rs +++ b/crates/takana-build/src/lib.rs @@ -984,10 +984,44 @@ fn resolve_phases(recipe: &Recipe, src: &Path) -> takana_core::Result { } else { ("CGO_ENABLED=0", "-ldflags=-buildid=") }; + // ── GOTMPDIR: DENTRO del módulo con cgo apagado, FUERA con cgo encendido ────── + // `-trimpath` reescribe rutas, pero cuál gana depende de dónde esté el work dir: + // · si cae DENTRO del módulo, gana la reescritura del módulo y el componente + // ALEATORIO `go-build` SOBREVIVE; + // · si cae FUERA, Go reescribe el work dir entero a `/tmp/go-build` y el número + // desaparece. + // Con CGO off da igual: no hay fuentes C generadas, así que ninguna ruta del work + // dir entra en el DWARF — las 15 recetas Go puras verificadas REPRODUCEN. Con cgo + // sí entra, y ésa era la ÚNICA causa del no-determinismo de `gocryptfs`: dos + // reconstrucciones difieren en SIETE bytes, todos en `.debug_str`, y son los + // dígitos de `go-build598798091` contra `go-build270591055`. + // + // Medido con un módulo cgo de juguete, cachés limpias y en los dos sentidos + // (2026-09-14): + // · GOTMPDIR dentro ⇒ `go-build3806217352` aparece en el binario; dos builds DIFIEREN + // · GOTMPDIR fuera ⇒ ninguna cadena `go-build`; dos builds IDÉNTICOS + // + // ⚠ Va a `/cache`, NO a `/tmp`: el sandbox monta `--tmpfs /tmp` (~½ RAM) y un + // proyecto Go grande lo desborda con sus objetos — es la razón por la que esto + // estaba en `/src` de entrada. `/cache` es un bind RW a disco (el mismo que usa la + // caché de zig), así que conserva la propiedad de estar en disco Y queda fuera del + // módulo. + // + // ⚠ Y se COMPRUEBA que `/cache` exista en vez de dar por hecho que está: con + // `--tmp-overlay /` la raíz es escribible, así que un `mkdir -p /cache/gotmp` a + // ciegas TRIUNFARÍA creando el directorio en la capa tmpfs — o sea en RAM, que es + // exactamente el desbordamiento que se quería evitar, y en silencio. Si no está, + // el build muere diciendo por qué. + let gotmp_setup = if recipe.build.cgo { + "[ -d /cache ] || { echo '!! falta el bind /cache: GOTMPDIR caería en la capa tmpfs (RAM) y un proyecto Go grande la desborda' >&2; exit 1; }; mkdir -p /cache/gotmp" + } else { + "mkdir -p /src/.gotmp" + }; + let gotmpdir = if recipe.build.cgo { "/cache/gotmp" } else { "/src/.gotmp" }; Some(format!( - "{cd_prefix}export GOCACHE=/src/.gocache GOTMPDIR=/src/.gotmp GOPATH=/tmp/gopath \ + "{cd_prefix}export GOCACHE=/src/.gocache GOTMPDIR={gotmpdir} GOPATH=/tmp/gopath \ GOTOOLCHAIN=local {cgo_env} GOFLAGS=-mod=vendor GOPROXY=off \ - GOBIN=/out/usr/bin; mkdir -p /src/.gotmp; \ + GOBIN=/out/usr/bin; {gotmp_setup}; \ go install -trimpath {ldflags} {pkg}" )) } diff --git a/docs/state/repro-verificado.tsv b/docs/state/repro-verificado.tsv index 188f9b19..1dcbd0ab 100644 --- a/docs/state/repro-verificado.tsv +++ b/docs/state/repro-verificado.tsv @@ -236,3 +236,6 @@ libplasma 73ec5fb279ea44e6bcfdf4c58d8703ed1307ae7985e56ce55a5da078ea098b7e 2026- syntax-highlighting 3db73c19bdbddcb046a80d11ec112bc7dc60134eeec75b26e907cc756d25d081 2026-09-14 reproduce powerdevil 70ea4b373af5ac3c22cf6adde9ca45153925724ef523000bc1e0e2338e282d15 2026-09-14 reproduce ktexteditor f13baeb75249c7f768fdd1b4584b5077779e6806ffcb295a6554f50d3f31d0ec 2026-09-14 reproduce +gocryptfs 0caf96352eae21ed51b32be9ead8d5525bb07e8aa124eb33c4b9588f1a02421d 2026-09-14 NO-DETERMINISMO +gocryptfs 0caf96352eae21ed51b32be9ead8d5525bb07e8aa124eb33c4b9588f1a02421d 2026-09-14 reproduce (tras poner al día un artefacto derivado) +gocryptfs 0caf96352eae21ed51b32be9ead8d5525bb07e8aa124eb33c4b9588f1a02421d 2026-09-14 reproduce