#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" source "$SCRIPT_DIR/lib/relibc-surface.sh" # ============================================================================= # Canonical-build markers. Set ONLY by build-redbear.sh itself. # Downstream tools (cookbook, preflight) read REDBEAR_CANONICAL_BUILD to know # they are running inside the canonical pipeline. Manual `repo cook` invocations # outside this script will NOT have this set, which enables: # - gentle "you are off the canonical path" warnings # - strict-by-default dirty-source gates # ============================================================================= export REDBEAR_CANONICAL_BUILD=1 export REDBEAR_BUILD_HOST="$(uname -n 2>/dev/null || echo unknown)" export REDBEAR_BUILD_START_EPOCH="$(date +%s)" export REDBEAR_BUILD_STATE_DIR="$(mktemp -d -t redbear-build-state.XXXXXX)" # Diagnostic capture: every cookbook log goes here in addition to /tmp REDBEAR_BUILD_LOGS_DIR="$REDBEAR_BUILD_STATE_DIR/logs" mkdir -p "$REDBEAR_BUILD_LOGS_DIR" # Stash-and-restore: every Red Bear fork source whose working tree is dirty # is stashed before the build runs, then popped on EXIT (success or failure). # This ensures pkgar fingerprints always reflect the committed HEAD, not # transient WIP that could be rolled back between builds. declare -ga REDBEAR_STASHED_REPOS=() declare -gA REDBEAR_STASH_MAP=() declare -ga REDBEAR_FORK_SOURCES=( "relibc:$PROJECT_ROOT/local/sources/relibc" "kernel:$PROJECT_ROOT/local/sources/kernel" "base:$PROJECT_ROOT/local/sources/base" "bootloader:$PROJECT_ROOT/local/sources/bootloader" "installer:$PROJECT_ROOT/local/sources/installer" "redoxfs:$PROJECT_ROOT/local/sources/redoxfs" "libredox:$PROJECT_ROOT/local/sources/libredox" "syscall:$PROJECT_ROOT/local/sources/syscall" "userutils:$PROJECT_ROOT/local/sources/userutils" ) redbear_is_fork_dirty() { local dir="$1" if [ ! -d "$dir/.git" ]; then return 0 fi git -C "$dir" diff --quiet HEAD 2>/dev/null || return 0 git -C "$dir" diff --cached --quiet HEAD 2>/dev/null || return 0 if [ -n "$(git -C "$dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then return 0 fi return 1 } redbear_stash_one() { local label="$1" local dir="$2" if [ -n "${REDBEAR_STASH_MAP[$label]:-}" ]; then return 0 fi if ! redbear_is_fork_dirty "$dir"; then echo " [clean] $label" return 0 fi rm -f "$dir/.git/index.lock" local msg="redbear-build-$REDBEAR_BUILD_START_EPOCH-$label" local sha if sha=$(git -C "$dir" stash push --include-untracked -m "$msg" 2>/dev/null | \ grep -E '^[0-9a-f]{40}$' | head -1); then REDBEAR_STASH_MAP[$label]="$sha" REDBEAR_STASHED_REPOS+=("$label:$dir") echo " [stashed] $label: $sha" else echo " [FAIL] $label: git stash push failed" >&2 return 1 fi } redbear_unstash_one() { local label="$1" local dir="$2" local sha="${REDBEAR_STASH_MAP[$label]:-}" if [ -z "$sha" ]; then return 0 fi rm -f "$dir/.git/index.lock" if git -C "$dir" stash pop --quiet 2>/dev/null; then echo " [restored] $label" else echo " [WARN] $label: stash pop failed — stash preserved at $sha" >&2 echo " Run: git -C '$dir' stash pop to restore manually" >&2 fi unset REDBEAR_STASH_MAP[$label] } redbear_stash_all_dirty_forks() { echo ">>> Stashing dirty fork sources (restored at exit)..." local any_dirty=0 local entry label dir for entry in "${REDBEAR_FORK_SOURCES[@]}"; do label="${entry%%:*}" dir="${entry#*:}" [ -d "$dir" ] || continue redbear_is_fork_dirty "$dir" && any_dirty=1 redbear_stash_one "$label" "$dir" || { echo "ERROR: failed to stash $label — aborting to avoid cooking from dirty tree" >&2 redbear_unstash_all_forks "stash-failure" exit 1 } done [ "$any_dirty" = "0" ] && echo " (all fork sources clean)" } redbear_unstash_all_forks() { local reason="${1:-exit}" [ "${#REDBEAR_STASHED_REPOS[@]}" -eq 0 ] && return 0 echo ">>> Restoring stashed fork sources (reason: $reason)..." local i for ((i=${#REDBEAR_STASHED_REPOS[@]}-1; i>=0; i--)); do local entry="${REDBEAR_STASHED_REPOS[$i]}" redbear_unstash_one "${entry%%:*}" "${entry#*:}" done } # Cookbook binary fingerprint: BLAKE3-hash (via sha256sum on this system) # of all .rs files in src/. Used to detect when the cookbook needs to be # rebuilt. The hash is written to target/release/.cookbook-src-fingerprint # after each successful build. Hashing every .rs file (sorted by path # for determinism) gives content-stable fingerprints: git operations that # change content without touching mtime (checkout, bisect, etc.) still # trigger a rebuild. redbear_compute_cookbook_fingerprint() { if [ -d "$PROJECT_ROOT/src" ]; then find "$PROJECT_ROOT/src" -name "*.rs" -type f -print0 2>/dev/null \ | sort -z \ | xargs -0 sha256sum 2>/dev/null \ | sha256sum \ | cut -d' ' -f1 else echo "no-src-dir" fi } redbear_record_cookbook_fingerprint() { mkdir -p "$PROJECT_ROOT/target/release" redbear_compute_cookbook_fingerprint > "$PROJECT_ROOT/target/release/.cookbook-src-fingerprint" } # Derive Red Bear OS version from the current git branch name. # Branch "0.2.4" → REDBEAR_VERSION="0.2.4". # Exported so the redbear-release recipe and other consumers can use it. if [ -z "${REDBEAR_VERSION:-}" ]; then REDBEAR_VERSION=$(git -C "$PROJECT_ROOT" branch --show-current 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "0.0.0") fi export REDBEAR_VERSION echo ">>> Red Bear OS version: $REDBEAR_VERSION (from git branch)" # Source .config for build settings, but NEVER auto-set REDBEAR_RELEASE. # Release mode requires explicit REDBEAR_RELEASE= in the environment. if [ -f "$PROJECT_ROOT/.config" ]; then while IFS= read -r line; do line="${line%%#*}" line=$(echo "$line" | xargs) [ -z "$line" ] && continue if [[ "$line" == *"?="* ]]; then key="${line%%\?=*}" value="${line#*\?=}" elif [[ "$line" == *"="* ]]; then key="${line%%=*}" value="${line#*=}" else continue fi key=$(echo "$key" | xargs) value=$(echo "$value" | xargs) [ -z "$key" ] && continue # Skip REDBEAR_RELEASE — dev builds must not use release mode [ "$key" = "REDBEAR_RELEASE" ] && continue # Only set if not already set in environment [ -n "${!key:-}" ] || export "$key=$value" done < "$PROJECT_ROOT/.config" fi CONFIG="redbear-full" JOBS="${JOBS:-$(nproc)}" APPLY_PATCHES="${APPLY_PATCHES:-1}" ALLOW_UPSTREAM=0 NO_CACHE=0 # ARCH and HOST_ARCH are used by the Makefile (mk/config.mk) and the prefix # rule (mk/prefix.mk). They are derived from `uname -m` if unset. Exporting # them at the top of this script ensures the auto-rebuild-prefix path # below receives a valid value, and the variable is visible to subprocesses. export ARCH="${ARCH:-$(uname -m)}" export HOST_ARCH="${HOST_ARCH:-$(uname -m)}" usage() { cat <&2 usage >&2 exit 1 ;; *) POSITIONAL+=("$1") ;; esac shift done if [ ${#POSITIONAL[@]} -gt 1 ]; then echo "ERROR: Too many positional arguments" >&2 usage >&2 exit 1 fi [ ${#POSITIONAL[@]} -eq 1 ] && CONFIG="${POSITIONAL[0]}" case "$CONFIG" in redbear-full|redbear-mini|redbear-grub) ;; *) echo "ERROR: Unknown config '$CONFIG'" >&2 echo "Supported: redbear-full, redbear-mini, redbear-grub" >&2 exit 1 ;; esac echo "========================================" echo " Red Bear OS Build System" echo "========================================" echo "Config: $CONFIG" echo "Jobs: $JOBS" echo "Apply patches: $APPLY_PATCHES" echo "Upstream: $ALLOW_UPSTREAM" echo "Root: ${PROJECT_ROOT##*/}" echo "========================================" echo "" cd "$PROJECT_ROOT" # Strict dirty-source gate: refuse to build with uncommitted edits in any # Red Bear fork source. Without this, builds cook from WIP trees whose # pkgar fingerprints never match the committed HEAD on subsequent builds, # producing "works on my machine" failures and stale cache invalidation. # The escape hatch REDBEAR_ALLOW_DIRTY=1 is for emergency CI runs where # the operator accepts the risk. if [ "${REDBEAR_ALLOW_DIRTY:-0}" != "1" ] && [ -z "${REDBEAR_RELEASE:-}" ]; then DIRTY_FORKS=() for entry in "${REDBEAR_FORK_SOURCES[@]}"; do label="${entry%%:*}" dir="${entry#*:}" [ -d "$dir" ] || continue if redbear_is_fork_dirty "$dir"; then DIRTY_FORKS+=("$label ($dir)") fi done if [ ${#DIRTY_FORKS[@]} -gt 0 ]; then echo "========================================" >&2 echo " REFUSING TO BUILD WITH UNCOMMITTED EDITS" >&2 echo "========================================" >&2 echo "" >&2 echo "Uncommitted edits detected in:" >&2 for fork in "${DIRTY_FORKS[@]}"; do echo " - $fork" >&2 done echo "" >&2 echo "Each fork must have a clean HEAD so pkgar fingerprints reflect" >&2 echo "committed state. Commit, stash, or reset your changes before" >&2 echo "running a canonical build." >&2 echo "" >&2 echo "Override (emergency only): REDBEAR_ALLOW_DIRTY=1 $0 $*" >&2 exit 1 fi fi if [ -x "$PROJECT_ROOT/local/scripts/verify-overlay-integrity.sh" ] && [ -z "${REDBEAR_RELEASE:-}" ]; then echo ">>> Skipping overlay repair (causes recipe symlink corruption)" # "$PROJECT_ROOT/local/scripts/verify-overlay-integrity.sh" --repair || true echo "" fi # Per AGENTS.md: local recipes ALWAYS supersede WIP. # Any WIP directory that shadows a local/recipes/ package must be # replaced with a symlink to the local version. if [ -z "${REDBEAR_RELEASE:-}" ]; then echo ">>> Enforcing local-over-WIP recipe policy..." for local_recipe in "$PROJECT_ROOT"/local/recipes/*/*/; do pkg=$(basename "$local_recipe") [ ! -f "$local_recipe/recipe.toml" ] && continue while IFS= read -r -d '' wip_dir; do if [ ! -L "$wip_dir" ]; then wip_rel=$(realpath --relative-to="$(dirname "$wip_dir")" "$local_recipe") rm -rf "$wip_dir" ln -sf "$wip_rel" "$wip_dir" echo " WIP $pkg -> local ($wip_rel)" fi done < <(find "$PROJECT_ROOT"/recipes/wip -maxdepth 5 -name "$pkg" -type d -print0 2>/dev/null || true) done echo "" fi redbear_stash_all_dirty_forks # EXIT trap: always restore stashes (success or failure), always dump # diagnostics, always preserve the original exit code. The trap fires # even on signals (SIGINT/SIGTERM/SIGHUP) because we don't override # ERR/DEBUG and we use EXIT (the POSIX-standard pseudo-signal that fires # for any exit path). The trap runs LAST in the shell's exit order, so # any explicit `exit N` we do inside the script triggers it with $? = N. redbear_exit_trap() { local rc=$? # Phase 1: unstash so the operator's working tree is restored. redbear_unstash_all_forks "exit" # Phase 2: dump failure diagnostics if the build failed. if [ "$rc" -ne 0 ]; then redbear_dump_failure_diagnostics "$rc" fi # Phase 3: clean up the state dir unless the user wants to inspect it. if [ -d "$REDBEAR_BUILD_STATE_DIR" ] && [ "${REDBEAR_KEEP_BUILD_STATE:-0}" != "1" ]; then rm -rf "$REDBEAR_BUILD_STATE_DIR" fi exit "$rc" } trap redbear_exit_trap EXIT # Failure diagnostics: collect the last N lines of every cookbook log in # $REDBEAR_BUILD_LOGS_DIR plus any mktemp leftovers from the build, list # partial recipe build states, report orphaned cook processes, and point # the operator at the state directory for post-mortem analysis. Designed to # give the operator a complete picture without requiring them to manually # grep through /tmp. redbear_dump_failure_diagnostics() { local rc="$1" echo "" echo "========================================" echo " BUILD FAILED (exit code: $rc)" echo "========================================" echo "" echo "Diagnostic state preserved at:" echo " $REDBEAR_BUILD_STATE_DIR" echo "" echo "Set REDBEAR_KEEP_BUILD_STATE=1 before running to retain this directory" echo "after the script exits." echo "" # Last 30 lines of each cookbook log we captured. if [ -d "$REDBEAR_BUILD_LOGS_DIR" ] && [ -n "$(ls -A "$REDBEAR_BUILD_LOGS_DIR" 2>/dev/null)" ]; then echo "--- Last 30 lines of each captured build log ---" local log for log in "$REDBEAR_BUILD_LOGS_DIR"/*.log; do [ -f "$log" ] || continue echo "" echo "==> $(basename "$log")" tail -30 "$log" done fi # Cookbook / cargo processes that may be orphaned from a signal kill. # These are often the actual cause of subsequent build failures (held # locks, half-written files). echo "" echo "--- Orphaned cook/cargo processes (if any) ---" local orphan orphan=$(ps -eo pid,ppid,etime,comm 2>/dev/null | \ grep -E 'repo|cargo|rustc|cc1|cookbook_redbear_redoxer' | \ grep -v grep || true) if [ -n "$orphan" ]; then echo "$orphan" else echo " (none)" fi # Recipe build states: which packages have a stage.pkgar (success) # vs which have only stage.tmp (in-progress). echo "" echo "--- Per-recipe build state ---" local recipe_dir target_arch for recipe_dir in "$PROJECT_ROOT"/recipes/*/ "$PROJECT_ROOT"/local/recipes/*/*/; do [ -d "$recipe_dir" ] || continue [ -f "$recipe_dir/recipe.toml" ] || continue local pkg pkg=$(basename "$recipe_dir") for target_arch in "$PROJECT_ROOT"/recipes/"$pkg"/target/*/ \ "$PROJECT_ROOT"/local/recipes/*/"$pkg"/target/*/; do [ -d "$target_arch" ] || continue local stage stage_tmp size stage="$target_arch/stage" stage_tmp="$target_arch/stage.tmp" if [ -d "$stage_tmp" ]; then size=$(du -sh "$stage_tmp" 2>/dev/null | cut -f1) echo " $pkg [$target_arch]: INCOMPLETE (stage.tmp: $size)" elif [ -d "$stage" ]; then echo " $pkg [$target_arch]: complete" fi done done echo "" echo "========================================" echo "" } if [ "$APPLY_PATCHES" = "1" ] && [ -z "${REDBEAR_RELEASE:-}" ]; then echo ">>> Patches are applied by 'repo fetch' via recipe.toml (atomic mechanism)" echo ">>> Skipping direct patch application (was bypassing cookbook atomicity)" echo "" elif [ -n "${REDBEAR_RELEASE:-}" ]; then echo ">>> Release mode: skipping patch application (patches pre-applied in archived sources)" fi if [ ! -f "target/release/repo" ]; then echo ">>> Building cookbook binary (missing)..." cargo build --release redbear_record_cookbook_fingerprint else # Rebuild cookbook if any .rs file in src/ changed since the last # successful build. The previous existence-only check let stale # binaries survive `git pull`, causing CLI flags to silently no-op. CURRENT_COOKBOOK_HASH=$(redbear_compute_cookbook_fingerprint) STORED_COOKBOOK_HASH="" [ -f "target/release/.cookbook-src-fingerprint" ] && \ STORED_COOKBOOK_HASH=$(cat "target/release/.cookbook-src-fingerprint") if [ "$CURRENT_COOKBOOK_HASH" != "$STORED_COOKBOOK_HASH" ]; then echo ">>> Rebuilding cookbook binary (source changed: ${CURRENT_COOKBOOK_HASH:0:12} != ${STORED_COOKBOOK_HASH:0:12})..." cargo build --release redbear_record_cookbook_fingerprint else echo ">>> Cookbook binary is up to date (${CURRENT_COOKBOOK_HASH:0:12})" fi fi if [ "$CONFIG" = "redbear-full" ]; then redbear_ensure_relibc_desktop_surface fi FW_AMD_DIR="$PROJECT_ROOT/local/firmware/amdgpu" if [ "$CONFIG" = "redbear-full" ]; then if [ -d "$FW_AMD_DIR" ] && [ -n "$(ls -A "$FW_AMD_DIR" 2>/dev/null)" ]; then FW_COUNT=$(ls "$FW_AMD_DIR"/*.bin 2>/dev/null | wc -l) echo ">>> Found $FW_COUNT AMD firmware blobs" else echo ">>> WARNING: No AMD firmware blobs found." echo " Run: ./local/scripts/fetch-firmware.sh" echo " GPU driver will NOT function without firmware." fi echo "" fi echo ">>> Building Red Bear OS with config: $CONFIG" echo ">>> This may take 30-60 minutes on first build..." # Stale-build prevention: if a low-level source repo has commits newer # than its pkgar, delete that package's pkgar and target dir AND clean # build/sysroot dirs across all recipes. Low-level packages (relibc, # kernel, base) provide the C runtime and compiler support libs; when # they change, autotools packages (pcre2, gettext, libiconv, etc.) # retain stale configure/libtool scripts that reference the old runtime, # causing "libtool version mismatch" and "not a valid libtool object" # errors. Cleaning build/ and sysroot/ forces re-configuration while # preserving stage/ and source/ so the cookbook can skip unchanged # packages that don't use autotools. if [ "$NO_CACHE" != "1" ]; then STALE_DETECTED=0 for src in relibc kernel base bootloader installer; do src_dir="$PROJECT_ROOT/local/sources/$src" pkgar="$PROJECT_ROOT/repo/x86_64-unknown-redox/$src.pkgar" fingerprint="$PROJECT_ROOT/repo/x86_64-unknown-redox/$src.source-fingerprint" if [ -d "$src_dir/.git" ] && [ -f "$pkgar" ]; then src_commit=$(git -C "$src_dir" rev-parse HEAD 2>/dev/null || echo "") last_commit=$(cat "$fingerprint" 2>/dev/null || echo "") # Working-tree dirtiness: tracked modifications, staged # changes, and untracked files all count. Without this, # uncommitted edits (e.g. an in-progress debug session) # silently bypass the stale-detect and the next build # cooks from a fingerprint that claims "up to date" but # is actually older than the working tree. src_dirty=0 if ! git -C "$src_dir" diff --quiet HEAD 2>/dev/null \ || ! git -C "$src_dir" diff --cached --quiet HEAD 2>/dev/null \ || [ -n "$(git -C "$src_dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then src_dirty=1 fi if [ -n "$src_commit" ] && { [ "$src_commit" != "$last_commit" ] || [ "$src_dirty" = "1" ]; }; then if [ "$src_dirty" = "1" ] && [ "$src_commit" = "$last_commit" ]; then echo ">>> Stale $src detected (working tree has uncommitted changes); invalidating..." else echo ">>> Stale $src detected (source newer than last build); invalidating..." fi rm -f "$PROJECT_ROOT/repo/x86_64-unknown-redox/$src".* find "$PROJECT_ROOT/recipes" -path "*/$src/target" -type d -exec rm -rf {} + 2>/dev/null || true STALE_DETECTED=1 fi fi done if [ "$STALE_DETECTED" = "1" ]; then echo ">>> Cleaning stale build/sysroot dirs (low-level 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 fi PREFIX_LIBC="$PROJECT_ROOT/prefix/x86_64-unknown-redox/sysroot/x86_64-unknown-redox/lib/libc.a" STALE_PREFIX=0 STALE_SRCS=() if [ -f "$PREFIX_LIBC" ]; then for src in relibc kernel base; do src_dir="$PROJECT_ROOT/local/sources/$src" if [ -d "$src_dir/.git" ]; then fork_ts=$(git -C "$src_dir" log -1 --format=%ct HEAD 2>/dev/null || echo "0") prefix_ts=$(stat -c %Y "$PREFIX_LIBC" 2>/dev/null || echo "0") # Also check for uncommitted changes in the working tree src_dirty=0 if ! git -C "$src_dir" diff --quiet HEAD 2>/dev/null \ || ! git -C "$src_dir" diff --cached --quiet HEAD 2>/dev/null \ || [ -n "$(git -C "$src_dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then src_dirty=1 fi if [ "$fork_ts" != "0" ] && [ "$prefix_ts" != "0" ] && { [ "$fork_ts" -gt "$prefix_ts" ] || [ "$src_dirty" = "1" ]; }; then fork_date=$(date -d "@$fork_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unknown") prefix_date=$(date -d "@$prefix_ts" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unknown") if [ "$src_dirty" = "1" ]; then echo ">>> Stale $src detected (working tree has uncommitted changes); prefix will be rebuilt." else echo ">>> Stale $src detected (fork $fork_date newer than prefix $prefix_date); prefix will be rebuilt." fi STALE_SRCS+=("$src") STALE_PREFIX=1 fi fi done fi # Auto-rebuild prefix when stale (replaces previous warning-only behavior). # Per Phase 1.3 of local/docs/SYSTEM-STABILITY-AND-UPSTREAM-SYNC-PLAN.md, this # actually invokes `make prefix` so the toolchain stays in sync with the # fork sources — without this, recipes link against a stale libc.a and # runtime artefacts (initfs, base daemons) can crash on boot. if [ "$STALE_PREFIX" = "1" ] && [ -z "${REDBEAR_SKIP_PREFIX_REBUILD:-}" ]; then echo ">>> Auto-rebuilding prefix ($STALE_PREFIX stale fork(s))..." for src in "${STALE_SRCS[@]}"; do touch "$PROJECT_ROOT/local/sources/$src" done # ARCH and HOST_ARCH are exported at the top of this script. The # make rule for `prefix` (mk/prefix.mk) derives TARGET from ARCH. # Pass the explicit `TARGET` override to match how recipe builds # pass the target triple. if ! make prefix TARGET="${TARGET:-x86_64-unknown-redox}" 2>&1 | tail -n 20; then echo ">>> ERROR: prefix rebuild failed. Aborting build to avoid producing broken binaries." >&2 exit 1 fi echo ">>> Prefix rebuilt successfully." STALE_PREFIX=0 fi if [ "$NO_CACHE" = "1" ]; then echo ">>> Cleaning repo and recipe caches for clean build..." make repo_clean 2>/dev/null || true rm -rf "$PROJECT_ROOT"/repo find "$PROJECT_ROOT"/local/recipes -maxdepth 4 -name "target" -type d -exec rm -rf {} + 2>/dev/null || true find "$PROJECT_ROOT"/recipes -maxdepth 3 -name "target" -type d -exec rm -rf {} + 2>/dev/null || true fi if [ -n "${REDBEAR_RELEASE:-}" ]; then bash "$PROJECT_ROOT/local/scripts/build-release-mode.sh" --release="$REDBEAR_RELEASE" --config="$CONFIG" --extra-package=relibc fi bash "$PROJECT_ROOT/local/scripts/build-preflight.sh" --config="$CONFIG" ${REDBEAR_RELEASE:+--release="$REDBEAR_RELEASE"} --extra-package=relibc bash "$PROJECT_ROOT/local/scripts/sync-versions.sh" --check || { echo "WARNING: In-house crate version drift detected. Run './local/scripts/sync-versions.sh' to fix." echo " Continuing build — versions will be corrected on next source fetch." } # Pre-cook critical packages that may fail in the dependency chain. # --with-package-deps resolves ALL transitive deps; pre-cooking ensures # the repo has valid pkgars before make live processes the full graph. # Only pre-cook the desktop chain for redbear-full; mini/grub don't need it. # llvm21 is a Mesa (graphics) dep — only needed when the Mesa chain is in scope. echo ">>> Pre-cooking critical packages..." if [ "$CONFIG" = "redbear-full" ]; then PRECOOK_PKGS="relibc icu llvm21 mesa libdrm libepoxy redox-drm lcms2 libdisplay-info libxcvt sddm qtbase kwin" else PRECOOK_PKGS="relibc icu" fi for pkg in $PRECOOK_PKGS; do if [ ! -f "$PROJECT_ROOT/repo/x86_64-unknown-redox/$pkg.pkgar" ]; then echo " cooking $pkg..." log=$(mktemp) # Pre-cook offline/upstream policy must match the main build phase: # the previous hardcoded COOKBOOK_OFFLINE=false meant pre-cook could # fetch packages the main phase refused to use, leaving the repo in # an inconsistent state when ALLOW_UPSTREAM was not set. case "${REDBEAR_ALLOW_UPSTREAM:-0}" in 1) PRECOOK_OFFLINE=false ;; *) PRECOOK_OFFLINE=true ;; esac if [ -n "${REDBEAR_RELEASE:-}" ]; then PRECOOK_OFFLINE=true fi if ! REDBEAR_BUILD_PHASE=pre-cook COOKBOOK_OFFLINE="$PRECOOK_OFFLINE" \ "$PROJECT_ROOT/target/release/repo" cook "$pkg" >"$log" 2>&1; then echo "WARNING: pre-cook of $pkg failed (non-fatal; will build during main phase). Tail of repo log:" >&2 tail -50 "$log" >&2 cp "$log" "$REDBEAR_BUILD_LOGS_DIR/${pkg}.pre-cook.log" 2>/dev/null || true rm -f "$log" else cp "$log" "$REDBEAR_BUILD_LOGS_DIR/${pkg}.pre-cook.log" 2>/dev/null || true rm -f "$log" fi fi done for src in relibc kernel base bootloader installer; do src_dir="$PROJECT_ROOT/local/sources/$src" if [ -d "$src_dir/.git" ]; then git -C "$src_dir" rev-parse HEAD 2>/dev/null > \ "$PROJECT_ROOT/repo/x86_64-unknown-redox/$src.source-fingerprint" 2>/dev/null || true fi done if [ "${REDBEAR_ALLOW_UPSTREAM:-0}" = "1" ]; then echo ">>> WARNING: Upstream fetch ENABLED (REDBEAR_ALLOW_UPSTREAM=1)" REPO_OFFLINE=0 COOKBOOK_OFFLINE=false CI=1 make live "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" 2>&1 elif [ -n "${REDBEAR_RELEASE:-}" ]; then echo ">>> Release mode: building from immutable archives (offline)" REPO_OFFLINE=1 COOKBOOK_OFFLINE=true CI=1 make live "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" 2>&1 elif [ "$ALLOW_UPSTREAM" -eq 1 ]; then echo ">>> Upstream recipe refresh enabled" REPO_OFFLINE=0 COOKBOOK_OFFLINE=false CI=1 make live "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" 2>&1 else echo ">>> Upstream recipe refresh disabled (default: offline)" REPO_OFFLINE=1 COOKBOOK_OFFLINE=true CI=1 make live "CONFIG_NAME=$CONFIG" "JOBS=$JOBS" 2>&1 fi ARCH="${ARCH:-$(uname -m)}" echo "" if [ "$STALE_PREFIX" = "1" ]; then echo ">>> REMINDER: prefix sysroot is stale. Run 'make prefix' to avoid link errors on next build." fi echo "========================================" echo " Build Complete!" echo "========================================" echo "ISO: build/$ARCH/$CONFIG.iso" echo "" echo "To run in QEMU:" echo " make qemu QEMUFLAGS=\"-m 4G\"" ls -lh "$PROJECT_ROOT/build/$ARCH/$CONFIG.iso" 2>/dev/null