Etapa G (granja): concilia la colisión de concurrencia — reintento serial de fallos por dep compartida
build-farm.sh asumía 'sin colisión entre recetas distintas', falso para deps transitivas compartidas no selladas: dos consumidores (p.ej. libadwaita/gtksourceview) construyen gtk4 a la vez en el mismo sources/gtk4-<sha>/output ⇒ 'Some other Meson process is already using this build directory'. Fase 1b: tras el pool paralelo, reintenta EN SERIE los fallos con firma de colisión — la dep ya la selló el ganador ⇒ cache-hit y compila. Un pase basta (cada build arrastra sus deps). Sólo reintenta colisiones, no MSRV/dep-faltante reales. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delta semántico
Ninguna definición cambió: el cambio es de texto, documentación o datos.
1 archivos · +23 −2
- scripts/build-farm.sh +23 −2
diff --git a/scripts/build-farm.sh b/scripts/build-farm.sh
index 6819cb48..c5d57b88 100755
--- a/scripts/build-farm.sh
+++ b/scripts/build-farm.sh
@@ -36,8 +36,10 @@ n_total=$(printf '%s\n' "$queue" | wc -l | tr -d ' ')
echo "==> GRANJA: $n_total recetas en $QUEUE, $JOBS workers en paralelo"
# ── Fase 1: BUILD en paralelo. Cada worker construye una receta y deja su status (.ok con hash | .fail
-# con motivo). El store sella atómico y cada build usa sources/<name>-<sha> + out/<hash>-<name> propios
-# ⇒ sin colisión entre recetas distintas. `xargs -P` es el pool.
+# con motivo). El store sella atómico y cada RECETA usa sources/<name>-<sha> + out/<hash>-<name> propios.
+# CAVEAT: dos recetas DISTINTAS que comparten una dep transitiva NO sellada (p.ej. gtk4 bajo libadwaita
+# y gtksourceview) la construyen en el MISMO sources/<dep>-<sha>/output ⇒ chocan (meson/tar). Eso lo
+# concilia la Fase 1b (reintento serial). `xargs -P` es el pool.
printf '%s\n' "$queue" | xargs -P"$JOBS" -I '{}' sh -c '
f="$1"; n=$(basename "$f" .toml); log="'"$FARM"'/log/$n.log"
H=$("'"$HAMMER"'" --store "'"$STORE"'" build "$f" 2>"$log" | tail -1 || true)
@@ -49,6 +51,25 @@ printf '%s\n' "$queue" | xargs -P"$JOBS" -I '{}' sh -c '
fi
' _ '{}'
+# ── Fase 1b: REINTENTO SERIAL de fallos por COLISIÓN de concurrencia. El pool paralelo puede hacer que
+# dos recetas construyan la MISMA dep transitiva compartida (gtk4/cairo/pango bajo varios consumidores)
+# a la vez en sources/<dep>-<sha>/output ⇒ meson/tar chocan ("Some other Meson process is already using
+# this build directory", "Directory not empty", "No such file"). Tras la Fase 1 la dep ya la selló el
+# ganador de la carrera ⇒ reconstruir el fallo EN SERIE ahora cache-hitea la dep y compila sin colisión.
+# Un solo pase basta: cada `hammer build` arrastra sus deps, así que aunque el orden no sea topológico,
+# la dep se construye/sella dentro del propio reintento. Sólo se reintenta lo que tiene FIRMA de colisión
+# (no MSRV/dep-faltante reales, que re-fallarían y sólo gastarían tiempo).
+for s in "$FARM"/status/*.fail; do
+ [ -f "$s" ] || continue
+ n=$(basename "$s" .fail); log="$FARM/log/$n.log"; f="$QUEUE/$n.toml"
+ [ -f "$f" ] || continue
+ grep -qiE "Some other Meson process|already using this build directory|Directory not empty|No such file or directory|gdkconfig\.h" "$log" 2>/dev/null || continue
+ H=$("$HAMMER" --store "$STORE" build "$f" 2>"$log" | tail -1 || true)
+ if [ "${H#b3:}" != "$H" ] && [ -n "$H" ]; then
+ rm -f "$s"; echo "$H" > "$FARM/status/$n.ok"; printf " ✓ (reintento serial) %-18s %s\n" "$n" "$H"
+ fi
+done
+
# ── Fase 2: PROMOTE + PUBLISH en serie (índice del repo = recurso mutable compartido).
ok=$(ls "$FARM"/status/*.ok 2>/dev/null | wc -l | tr -d ' ')
fail=$(ls "$FARM"/status/*.fail 2>/dev/null | wc -l | tr -d ' ')