build-redbear.sh: invalidate a recipe's target/ when its vendored source/ changes

The staleness gap behind the qtshadertools failure: cookbook keys its build
cache on the source.tar hash, but the actual build input for a vendored recipe
is the git-committed source/ tree. A version bump that propagates into source/
without changing source.tar (Qt's source.tar was already 6.11.1 while source/
was a 6.11.0 muddle) leaves cookbook reusing a stale stage — the desktop then
builds the old version. New loop fingerprints each vendored source/ by its git
tree hash (+ dirty flag) and rm's target/ on change; first observation only
seeds the fingerprint so it never forces a spurious full rebuild.
This commit is contained in:
2026-08-01 03:52:28 +03:00
parent ee46489ccb
commit 3e32079b79
+41 -1
View File
@@ -9,7 +9,7 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# REDBEAR_VERSION (which tracks the OS release derived from the git branch).
# Starts at 1.0 and is bumped AUTOMATICALLY on every change by the pre-commit
# git hook (local/scripts/bump-build-version.sh); do not edit the minor by hand.
BUILD_REDBEAR_VERSION="1.7"
BUILD_REDBEAR_VERSION="1.8"
# ── Colorized output ──────────────────────────────────
# Enabled only on a TTY with NO_COLOR unset, so redirected build logs and CI
@@ -772,6 +772,46 @@ if [ "$NO_CACHE" != "1" ]; then
done
# Vendored-recipe source staleness (the Qt/KDE 6.11.0->6.11.1 / 6.10->6.28
# class). Many recipes under local/recipes vendor their upstream tree in git
# at <recipe>/source/ AND declare a [source] tar=. Cookbook keys its build
# cache on the source.tar hash (source_identifier), but the ACTUAL build
# input is the vendored source/ tree. When a version bump propagates into
# source/ WITHOUT changing source.tar (e.g. source.tar was already the new
# version, or a manual source/ edit), cookbook sees an unchanged
# source_identifier and reuses a STALE stage — the desktop then builds the
# old version (qtshadertools wanted Qt 6.11.1 but got a cached 6.11.0 qtbase).
#
# Fix: fingerprint each vendored source/ by its git tree hash (content-based,
# mtime-independent) plus a working-tree-dirty flag, and rm that recipe's
# target/ when it changes. First observation for a recipe only SEEDS the
# fingerprint (no invalidation) so this never triggers a spurious full
# rebuild; thereafter any source/ change forces exactly that recipe to
# re-cook. Mirrors verify-external-source-versions.sh at the cache layer.
while IFS= read -r _rt; do
_rdir="$(dirname "$_rt")"
_sdir="$_rdir/source"
[ -d "$_sdir" ] || continue
_tgt="$_rdir/target/x86_64-unknown-redox"
[ -f "$_tgt/stage.pkgar" ] || continue # nothing cooked yet -> nothing stale
_relsrc="${_sdir#"$PROJECT_ROOT"/}"
_tree="$(git -C "$PROJECT_ROOT" rev-parse "HEAD:$_relsrc" 2>/dev/null || echo "")"
[ -n "$_tree" ] || continue # source/ not git-tracked -> skip
_dirty=""
git -C "$PROJECT_ROOT" diff --quiet HEAD -- "$_relsrc" 2>/dev/null || _dirty="-dirty"
[ -n "$(git -C "$PROJECT_ROOT" ls-files --others --exclude-standard -- "$_relsrc" 2>/dev/null)" ] && _dirty="-dirty"
_cur="${_tree}${_dirty}"
_fp="$_tgt/.redbear-source-tree"
_last="$(cat "$_fp" 2>/dev/null || echo "")"
if [ -z "$_last" ]; then
printf '%s\n' "$_cur" > "$_fp" # seed only, trust the existing build
elif [ "$_cur" != "$_last" ]; then
echo "${C_INFO}>>>${C_RESET} Stale vendored source: $(basename "$_rdir") (source/ changed since last cook) — invalidating target/"
rm -rf "$_rdir/target"
STALE_DETECTED=1
fi
done < <(find "$PROJECT_ROOT/local/recipes" -name recipe.toml -not -path '*/wip/*' 2>/dev/null)
# (The blanket "wipe every recipe's build/sysroot when a runtime fork changed"
# step used to live here. It was the bug: it recompiled the entire dynamically
# -linked desktop — Qt, KF6, mesa, sddm — on any relibc/base/syscall change,