diff --git a/src/tools/xce-build.sh b/src/tools/xce-build.sh index 899558a0..d47ebd39 100755 --- a/src/tools/xce-build.sh +++ b/src/tools/xce-build.sh @@ -1,6 +1,36 @@ #!/usr/bin/env bash set -euo pipefail +# --incremental (bug-1565 escape hatch): opt-in, NOT the default. Every +# invocation normally does a full `rm -rf "$BUILD_DIR"` + fresh `meson +# setup` (see setup_build below) -- correct in isolation, but since +# `meson compile` afterward only rebuilds the ONE requested component, a +# single-component build silently deletes any OTHER already-built binary +# sharing that same dir (e.g. `/xce-build xceadmin` after `/xce-build +# controller` in xce.build/ wipes xce_controller). --incremental instead +# reconfigures the EXISTING dir in place (`meson setup --reconfigure`), +# preserving whatever else was already built there. +# +# Deliberately NOT made the default: this project's capnp-generation +# custom_target (lib/controller.capnp.h/.c) has documented, unfixed +# missing-dependency-edge races (buglog.json, "retry once before +# debugging"). A full wipe papers over that gap -- worst case it's a loud, +# self-evident, retry-fixable failure on a clean build. Without the wipe, +# the SAME gap can instead cause silent staleness: a `.capnp` schema edit +# not triggering regeneration, so the build reports success while linking +# against stale generated code. Use --incremental only when you specifically +# want a sibling component alongside an already-built one in the same dir; +# rebuild without it (a real clean build) if anything looks off afterward. +INCREMENTAL=0 +ARGS=() +for arg in "$@"; do + case "$arg" in + --incremental) INCREMENTAL=1 ;; + *) ARGS+=("$arg") ;; + esac +done +set -- "${ARGS[@]}" + COMPONENT="${1:-all}" # Build PROFILE controls the meson buildtype + sanitizer (and the default build @@ -48,16 +78,25 @@ SRC_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$SRC_DIR" usage() { - echo "Usage: $0 [component] [profile|build_dir] [build_dir]" + echo "Usage: $0 [component] [profile|build_dir] [build_dir] [--incremental]" echo "Components: all (default), lib, controller, scheduler, worker, apiserver, xcecli, xceadmin" echo "Profiles: asan (default), debug (non-ASAN -O0 -g3, for gdb/cores), release" + echo "--incremental: reconfigure the existing build dir in place instead of a full" + echo " rm -rf + fresh setup -- preserves other already-built components in the same" + echo " dir (bug-1565). Not the default: see the comment at the top of this script" + echo " for the staleness tradeoff. Rebuild without it if anything looks wrong." exit 1 } setup_build() { - echo "=== Clean build: $BUILD_DIR (profile=$PROFILE) ===" - rm -rf "$BUILD_DIR" - meson setup "$BUILD_DIR" "${MESON_OPTS[@]}" + if [ "$INCREMENTAL" = 1 ] && [ -f "$BUILD_DIR/build.ninja" ]; then + echo "=== Incremental build: $BUILD_DIR (profile=$PROFILE) — reconfiguring in place, other components' binaries preserved. If anything looks wrong, rebuild without --incremental. ===" + meson setup --reconfigure "$BUILD_DIR" "${MESON_OPTS[@]}" + else + echo "=== Clean build: $BUILD_DIR (profile=$PROFILE) ===" + rm -rf "$BUILD_DIR" + meson setup "$BUILD_DIR" "${MESON_OPTS[@]}" + fi } case "$COMPONENT" in @@ -139,6 +178,11 @@ case "$COMPONENT" in setup_build echo "=== Building + testing xceadmin (via meson) ===" meson compile -C "$BUILD_DIR" xceadmin + WHEEL_FILE=$(ls "$BUILD_DIR"/xceadmin/dist/xceadmin-*.whl 2>/dev/null || true) + if [ -n "$WHEEL_FILE" ]; then + echo "=== xceadmin wheel: $WHEEL_FILE ===" + echo " Install it directly with: pip install \"$WHEEL_FILE\"" + fi ;; all) setup_build