# build-lock.sh — exclusive per-build-dir lock for xce-build.sh (bug-1604). # # Sourced, not executed. Two xce-build.sh runs against the SAME build dir used # to race: one run's `rm -rf $BUILD_DIR` hit files the other was writing # ("Directory not empty") or silently deleted a binary the other had just # built and tested. The lock serializes them for the whole run (wipe, setup, # compile, tests); runs against DIFFERENT build dirs stay concurrent. # # The lock is a directory created with mkdir (atomic; needs nothing beyond # coreutils, unlike flock(1), which the nix dev shell does not provide), # holding the owner's PID. It lives outside the build dir -- the wipe would # delete it -- under $XCE_BUILD_LOCK_ROOT (default $TMPDIR or /tmp), named # after a hash of the build dir's absolute path, so separate checkouts never # share a lock. A lock whose owner is no longer running (a build killed with # SIGKILL) is taken over. # # build_lock_acquire blocks until held # build_lock_release releases it (safe to call twice) # # Tunables: XCE_BUILD_LOCK_ROOT, XCE_BUILD_LOCK_POLL_SECS (default 2). BUILD_LOCK_DIR="" build_lock_path() { local abs sum abs="$(realpath -m -- "$1")" sum="$(printf '%s' "$abs" | cksum | cut -d' ' -f1)" printf '%s/xce-build-%s.lock' "${XCE_BUILD_LOCK_ROOT:-${TMPDIR:-/tmp}}" "$sum" } build_lock_acquire() { local lock owner waited=0 poll="${XCE_BUILD_LOCK_POLL_SECS:-2}" lock="$(build_lock_path "$1")" while ! mkdir "$lock" 2>/dev/null; do owner="$(cat "$lock/pid" 2>/dev/null || true)" if [ -n "$owner" ] && ! kill -0 "$owner" 2>/dev/null; then # The owner died holding it. If two waiters take over the same # stale lock at once, mkdir below still lets only one of them in. echo "=== Build lock for $1 was left by PID $owner, which is gone -- taking it over ===" >&2 rm -rf -- "$lock" continue fi if [ "$waited" = 0 ]; then echo "=== Waiting for another xce-build.sh run (PID ${owner:-?}) to finish with $1 ... ===" >&2 waited=1 fi sleep "$poll" done echo "${BASHPID:-$$}" > "$lock/pid" BUILD_LOCK_DIR="$lock" } build_lock_release() { if [ -n "$BUILD_LOCK_DIR" ]; then rm -rf -- "$BUILD_LOCK_DIR" BUILD_LOCK_DIR="" fi }