From 1c943e98fea7d4398aa888e0630eb1624aac5387 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Mon, 17 Aug 2026 10:20:36 +0000 Subject: [PATCH] feat(build): add --incremental flag to xce-build.sh (bug-1565) setup_build() unconditionally rm -rf'd the whole profile build directory on every single-component invocation -- running /xce-build xceadmin after /xce-build controller silently deleted xce_controller/xce_test/etc. from the same build dir. Added an opt-in --incremental flag (meson setup --reconfigure in place instead of wiping) rather than changing the default: this project's capnp-generation custom_target has known missing- dependency-edge races that a full wipe currently papers over as a loud, retry-fixable failure. Making incremental the default risked turning that same gap into silent stale-generated-code instead, so the safer full-wipe behavior is preserved unless explicitly opted into. Co-Authored-By: Claude Sonnet 5 --- src/tools/xce-build.sh | 52 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) 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