#!/usr/bin/env bash # repair-cook.sh — incremental-build optimizer for `repo cook` # # Equivalent to `./target/release/repo cook ` but checks # whether the existing build directory is still valid before # re-running the full configure + build cycle. Saves 30-60 seconds # per cook on incremental builds of CMake-based recipes. # # Triggers a "fast path" only when ALL of the following are true: # 1. The recipe's build/ directory already exists. # 2. The recipe's CMakeCache.txt exists in build/. # 3. CMakeCache.txt is newer than every source file in source/. # 4. The recipe's external patches (local/patches//) have # not been modified since the last successful cook. # 5. The user did NOT pass --clean-build (which forces a clean run). # # If any condition fails, falls through to a full `repo cook` run. # # Usage: # ./local/scripts/repair-cook.sh # ./local/scripts/repair-cook.sh --clean-build # make repair.qtbase # via Makefile wrapper below # # Env vars: # REPAIR_FORCE=1 skip the fast-path check, always full cook # REPAIR_VERBOSE=1 print why fast-path was rejected # REPAIR_DRY_RUN=1 print what would happen, don't execute # # This is build-system improvement #2 per local/docs/BUILD-SYSTEM-IMPROVEMENTS.md. set -euo pipefail REPO_BIN="${REPO_BIN:-$(cd "$(dirname "$0")/../.." && pwd)/target/release/repo}" RECIPE="${1:?usage: $0 [--clean-build]}" shift CLEAN_BUILD=0 for arg in "$@"; do case "$arg" in --clean-build) CLEAN_BUILD=1 ;; esac done if [ "${REPAIR_FORCE:-0}" = "1" ]; then CLEAN_BUILD=1 fi # Resolve recipe to absolute path RECIPE="$(cd "$(dirname "$RECIPE")" && pwd)/$(basename "$RECIPE")" # Recipe's name (last path component of the dir) RECIPE_NAME="$(basename "$RECIPE")" # Build directory: discovered from the cookbook's actual convention. # Per src/cook/cook_build.rs:357, the build dir is created at # `/target//build`. The target string comes from # `redoxer::target()` (cross) or `redoxer::host_target()` (host). # We probe the most common targets. The fast path requires # CMakeCache.txt to exist inside build/ — that is the canonical # signal that a prior cook completed configure. cmake_cache="" build_dir="" for try_dir in "$RECIPE/target"/*/build/CMakeCache.txt; do if [ -f "$try_dir" ]; then cmake_cache="$try_dir" build_dir="$(dirname "$cmake_cache")" break fi done verbose() { [ "${REPAIR_VERBOSE:-0}" = "1" ] && echo "repair-cook: $*" >&2 || true } # --------------------------------------------------------------------------- # Fast path: skip configure if existing build/ is still valid # --------------------------------------------------------------------------- if [ "$CLEAN_BUILD" = "0" ] && [ -n "$build_dir" ] && [ -f "$build_dir/CMakeCache.txt" ]; then source_is_newer=0 if [ -d "$RECIPE/source" ]; then # find -newer exits 0 if any file under source/ (excluding # .git/ and target/) is newer than CMakeCache.txt if [ -n "$(find "$RECIPE/source" \ -not -path '*/.git/*' \ -not -path '*/target/*' \ -newer "$build_dir/CMakeCache.txt" \ -print -quit 2>/dev/null)" ]; then source_is_newer=1 fi fi # patches_dir is `/local/patches//`, three levels up # from RECIPE which is `/local/recipes///` patches_dir="$RECIPE/../../../patches/$RECIPE_NAME" patches_are_newer=0 if [ -d "$patches_dir" ]; then if [ -n "$(find "$patches_dir" -name '[0-9]*.patch' \ -newer "$build_dir/CMakeCache.txt" \ -print -quit 2>/dev/null)" ]; then patches_are_newer=1 fi fi if [ "$source_is_newer" = "0" ] && [ "$patches_are_newer" = "0" ]; then verbose "fast path: $RECIPE_NAME — CMakeCache.txt is fresh, "\ "no source/ or patch changes since last cook" if [ "${REPAIR_DRY_RUN:-0}" = "1" ]; then echo "Would run: $REPO_BIN cook $RECIPE (fast path)" exit 0 fi # Fast path: re-package existing build/ artifacts into the # per-recipe sysroot. We do NOT pass --clean-build, so the # cookbook skips the configure + compile phases and just # runs the install/stage/package pipeline. This saves 30-60 # seconds per cook on incremental builds of CMake recipes. verbose "running: $REPO_BIN cook $RECIPE (fast path)" exec "$REPO_BIN" cook "$RECIPE" "$@" else verbose "fast path rejected for $RECIPE_NAME: "\ "source_is_newer=$source_is_newer, "\ "patches_are_newer=$patches_are_newer" fi fi # Slow path: full `repo cook` (configure + build + stage + package). # Falls through when (a) no prior build/ exists, (b) --clean-build # was passed, or (c) source/ or patches are newer than CMakeCache.txt. if [ "${REPAIR_DRY_RUN:-0}" = "1" ]; then echo "Would run: $REPO_BIN cook $RECIPE $@ (slow path)" exit 0 fi verbose "running: $REPO_BIN cook $RECIPE (slow path)" exec "$REPO_BIN" cook "$RECIPE" "$@"