build: stop re-cooking llvm21/toolchain on every relibc/base bump

The relibc-consumer invalidation (correctly) force-deletes every pkgar+target
when relibc rebuilds or its pkgar is missing, to prevent stale-ABI skew. But it
swept up heavy, ABI-inert toolchain packages (llvm21, clang21, lld21, rust)
identically to a 200KB daemon — forcing a 1-2h LLVM re-cook on EVERY base/relibc
bump, which cripples Mesa/KDE iteration.

These are build-time-only STATIC archives (BUILD_SHARED_LIBS=off; no runtime .so
of their own). Their libc/relibc symbol references are UNRESOLVED in the .a and
resolve at the CONSUMER final link — mesa links libLLVM*.a and resolves against
the freshly-built relibc there. So a relibc protocol change is absorbed by
relinking mesa (NOT on the preserve-list; stays invalidated), and recompiling
all of LLVM from scratch is unnecessary.

Add TOOLCHAIN_PRESERVE={llvm21,clang21,lld21,rust} skipped in both invalidation
branches, with escape hatch REDBEAR_FORCE_TOOLCHAIN_RECOOK=1 for a genuine
libc-ABI change. Trades a rare theoretical risk for a large repeated speedup;
the churn during driver/desktop work is Redox scheme/syscall shims the compilers
never call, so the default is safe.
This commit is contained in:
2026-07-24 13:14:24 +09:00
parent 60597ae154
commit f6b09ff713
+32 -1
View File
@@ -706,6 +706,30 @@ if [ "$NO_CACHE" != "1" ]; then
if [ "$RELIBC_INVALIDATED" = "1" ] || [ ! -f "$RELIBC_PKGAR" ]; then
relibc_rebuilding=1
fi
# Heavy, ABI-inert toolchain packages preserved across relibc invalidation.
#
# These are BUILD-TIME-ONLY static archives (built with BUILD_SHARED_LIBS=off;
# they ship no runtime .so of their own). Their references to libc/relibc
# symbols are UNRESOLVED in the .a and get resolved at the CONSUMER's final
# link — e.g. mesa links libLLVM*.a into libgallium and resolves malloc/etc.
# against the freshly-built relibc at THAT point. So a relibc protocol change
# is absorbed by relinking the consumer (mesa is NOT on this list and stays
# invalidated), and recompiling all of LLVM/Rust from scratch to pick up a
# relibc change is unnecessary — it just burns 1-2h per base/relibc bump.
#
# This trades a rare theoretical risk (a relibc change to a libc struct/inline
# actually baked into an llvm .o) for a large, repeated speedup. The churn
# during driver/desktop work is Redox scheme/syscall-protocol shims, which
# these compilers never call, so the default is safe. Escape hatch:
# REDBEAR_FORCE_TOOLCHAIN_RECOOK=1 restores the full from-scratch nuke.
TOOLCHAIN_PRESERVE="llvm21 clang21 lld21 rust"
_rb_preserve_toolchain() {
[ "${REDBEAR_FORCE_TOOLCHAIN_RECOOK:-0}" = "1" ] && return 1
case " $TOOLCHAIN_PRESERVE " in
*" $1 "*) return 0 ;;
*) return 1 ;;
esac
}
invalidate_consumer() {
local pkg="$1"
rm -f "$REPO_DIR/$pkg".*
@@ -718,7 +742,13 @@ if [ "$NO_CACHE" != "1" ]; then
echo ">>> clean relink (correctness over speed; prevents stale-binary skew)."
for _pkgar in "$REPO_DIR"/*.pkgar; do
[ -f "$_pkgar" ] || continue
invalidate_consumer "$(basename "$_pkgar" .pkgar)"
_pkg="$(basename "$_pkgar" .pkgar)"
if _rb_preserve_toolchain "$_pkg"; then
echo ">>> preserving ABI-inert toolchain pkg '$_pkg' (consumer relinks;" \
"set REDBEAR_FORCE_TOOLCHAIN_RECOOK=1 to force a full re-cook)."
continue
fi
invalidate_consumer "$_pkg"
done
elif [ -f "$RELIBC_PKGAR" ]; then
relibc_ts=$(stat -c %Y "$RELIBC_PKGAR" 2>/dev/null || echo "0")
@@ -726,6 +756,7 @@ if [ "$NO_CACHE" != "1" ]; then
[ -f "$_pkgar" ] || continue
_pkg="$(basename "$_pkgar" .pkgar)"
[ "$_pkg" = "relibc" ] && continue
_rb_preserve_toolchain "$_pkg" && continue
_pkg_ts=$(stat -c %Y "$_pkgar" 2>/dev/null || echo "0")
if [ "$relibc_ts" != "0" ] && [ "$relibc_ts" -gt "$_pkg_ts" ]; then
echo ">>> ABI-stale $_pkg (older than relibc.pkgar — interrupted prior build); invalidating..."