diff --git a/local/scripts/build-redbear.sh b/local/scripts/build-redbear.sh index a8fd8bc372..de366b5dd2 100755 --- a/local/scripts/build-redbear.sh +++ b/local/scripts/build-redbear.sh @@ -628,7 +628,9 @@ if [ "$NO_CACHE" != "1" ]; then find "$PROJECT_ROOT/recipes" -path "*/$src/target" -type d -exec rm -rf {} + 2>/dev/null || true STALE_DETECTED=1 [ "$src" = "relibc" ] && RELIBC_INVALIDATED=1 - case "$src" in relibc|base) USERSPACE_RUNTIME_STALE=1 ;; esac + # NB: base ships NO libraries (daemons/drivers/init) and relibc is + # a shared libc.so.6 — neither forces a userspace-wide relink. Only + # the static initfs-critical binaries are handled, selectively, below. fi fi done @@ -661,13 +663,11 @@ if [ "$NO_CACHE" != "1" ]; then done - if [ "$USERSPACE_RUNTIME_STALE" = "1" ] && [ "${REDBEAR_SKIP_ABI_STALENESS:-0}" != "1" ]; then - echo ">>> Cleaning stale build/sysroot dirs (userspace-linked runtime changed)..." - find "$PROJECT_ROOT/recipes" "$PROJECT_ROOT/local/recipes" \ - \( -path "*/target/x86_64-unknown-redox/build" \ - -o -path "*/target/x86_64-unknown-redox/sysroot" \) \ - -type d -exec rm -rf {} + 2>/dev/null || true - fi + # (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, + # which is exactly what a shared-libc system never needs. Replaced by the + # selective, static-consumer-only invalidation below.) # Downstream ABI-staleness prevention (relibc consumers). # @@ -739,78 +739,36 @@ if [ "$NO_CACHE" != "1" ]; then *) return 1 ;; esac } - # ── Config-scoped ABI invalidation ────────────────────────────────── - # repo/ is shared across configs, so a `redbear-mini` build must NOT nuke - # packages that only `redbear-full` builds (Qt, mesa, sddm, ...) just because - # their pkgars sit in the repo older than relibc.pkgar. Resolve the closure - # of the config being built and only ever invalidate packages inside it. - # No env flag needed — you named the config, so we know it's mini not full. - declare -A _config_pkg_set - _config_scoped=0 - _cfg_file="$PROJECT_ROOT/config/$CONFIG.toml" - if [ -f "$_cfg_file" ]; then - while read -r _p; do - [ -n "$_p" ] && _config_pkg_set["$_p"]=1 - done < <(timeout 120 "$PROJECT_ROOT/target/release/repo" push-tree \ - --filesystem="$_cfg_file" 2>/dev/null \ - | sed -E 's/^[^A-Za-z0-9_]+//' | awk '{print $1}' | grep -E '^[A-Za-z0-9]') - [ "${#_config_pkg_set[@]}" -gt 0 ] && _config_scoped=1 - fi - if [ "$_config_scoped" = "1" ]; then - echo ">>> ABI-staleness scoped to $CONFIG (${#_config_pkg_set[@]} pkgs); other configs' repo packages untouched." - else - echo ">>> NOTE: could not resolve $CONFIG package closure; ABI sweep is repo-wide (fallback)." - fi - _pkg_in_config() { - # true unless we have a config closure and pkg is outside it - [ "$_config_scoped" != "1" ] && return 0 - [ -n "${_config_pkg_set[$1]:-}" ] - } - invalidate_consumer() { - local pkg="$1" - _pkg_in_config "$pkg" || return 0 - rm -f "$REPO_DIR/$pkg".* - find "$PROJECT_ROOT/recipes" "$PROJECT_ROOT/local/recipes" \ - -path "*/$pkg/target" -type d -exec rm -rf {} + 2>/dev/null || true - } - if [ "${REDBEAR_SKIP_ABI_STALENESS:-0}" = "1" ]; then - # Fast-iteration escape hatch: skip the entire relibc-consumer - # invalidation. Safe ONLY when relibc's ABI is unchanged (e.g. iterating - # on a single driver/app recipe). The cookbook's own BLAKE3 dep-hash - # cache still rebuilds recipes whose sources changed, so your edited - # package re-cooks while everything else stays cached — no 30-min churn - # to re-reach it. Unset to restore the stale-binary safety net (which is - # required whenever relibc/base actually changed). - echo ">>> REDBEAR_SKIP_ABI_STALENESS=1 — skipping relibc-consumer invalidation" - echo ">>> (fast iteration; assumes relibc ABI unchanged). Unset to restore it." - elif [ "$relibc_rebuilding" = "1" ]; then - echo ">>> relibc is rebuilding — it is statically linked into every" - echo ">>> userspace binary, so ALL packages are invalidated to force a" - echo ">>> clean relink (correctness over speed; prevents stale-binary skew)." - for _pkgar in "$REPO_DIR"/*.pkgar; do - [ -f "$_pkgar" ] || continue - _pkg="$(basename "$_pkgar" .pkgar)" - _pkg_in_config "$_pkg" || continue - 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") - for _pkgar in "$REPO_DIR"/*.pkgar; do - [ -f "$_pkgar" ] || continue - _pkg="$(basename "$_pkgar" .pkgar)" - [ "$_pkg" = "relibc" ] && continue - _pkg_in_config "$_pkg" || 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..." - invalidate_consumer "$_pkg" - fi + # ── Selective, static-consumer-only ABI invalidation ─────────────── + # relibc is a SHARED library (libc.so.6): dynamically-linked userspace — Qt, + # KF6, mesa, sddm, the whole desktop — resolves it at RUNTIME, so a relibc + # rebuild needs NO recompile there. That is exactly why upgrading glibc on + # Linux does not recompile the system. `base` ships no libraries at all + # (daemons/drivers/init). The ONLY binaries that can genuinely go stale are + # the STATIC, boot-critical initfs binaries that bake in the Redox + # scheme/syscall protocol before libc.so is available — the getty / redoxfs / + # init / randd class. Those come from a small fixed set of forks; when the + # boot ABI changes (relibc rebuilds, or a syscall/libredox/redox-scheme + # protocol crate changed -> USERSPACE_RUNTIME_STALE), relink ONLY them. + # + # This restores the selective rule the old "nuke every pkgar + wipe every + # build/sysroot" version had replaced (which recompiled the entire desktop on + # any driver/acpi/base edit). For a genuine relibc *C-ABI* break — a libc + # struct/inline baked into a compiled consumer, which is rare — force a full + # clean rebuild with `--no-cache`. + ABI_CRITICAL_PKGS="base redoxfs userutils bootstrap" + _boot_abi_changed=0 + [ "$relibc_rebuilding" = "1" ] && _boot_abi_changed=1 + [ "$USERSPACE_RUNTIME_STALE" = "1" ] && _boot_abi_changed=1 + if [ "$_boot_abi_changed" = "1" ]; then + echo ">>> Boot ABI touched (relibc / syscall protocol fork) — relinking ONLY the" + echo ">>> static initfs-critical binaries: $ABI_CRITICAL_PKGS." + echo ">>> Dynamically-linked userspace (Qt/KF6/mesa/sddm/desktop) is untouched —" + echo ">>> shared libc.so resolved at runtime (the Linux glibc model)." + for _pkg in $ABI_CRITICAL_PKGS; do + rm -f "$REPO_DIR/$_pkg".* + find "$PROJECT_ROOT/recipes" "$PROJECT_ROOT/local/recipes" \ + -path "*/$_pkg/target" -type d -exec rm -rf {} + 2>/dev/null || true done fi fi