91 lines
4.5 KiB
Bash
91 lines
4.5 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# Single source of truth for the SwiftPM scratch (build) directory, keyed by where the
|
|
# build is running. Prints the flags to append to `swift build/run/test` — empty on the
|
|
# host, a `--scratch-path <dir>` redirect on the macOS VM.
|
|
#
|
|
# Why this exists
|
|
# ---------------
|
|
# SwiftPM and the SwiftBuild backend bake the repository's ABSOLUTE path into the build
|
|
# tree — build.db, the *.yaml manifests, manifest.pif, everything under
|
|
# .build/out/Intermediates.noindex/XCBuildData, and the per-file `.d` dependency lists all
|
|
# contain fully-qualified source paths. That is fine until the SAME checkout is reachable
|
|
# at more than one absolute path, which is exactly our situation:
|
|
#
|
|
# host / sandbox container : /Users/<user>/.nucleic/control/.../trunk
|
|
# per-session macOS VM : /Volumes/My Shared Files/workspace (virtio-fs share of the trunk)
|
|
#
|
|
# Both mount points see one physical .build/ (it lives inside the shared trunk). The moment
|
|
# a build runs from the second mount, the embedded paths stop matching the current location:
|
|
# incremental builds thrash or fail outright, and you end up with a .build/ that contains a
|
|
# mix of /Users/... and "/Volumes/My Shared Files/..." strings (grep it — that mixture is
|
|
# the smoking gun of a poisoned cache). On the VM there is a second cost: driving every
|
|
# object file through the virtio-fs share is far slower than the guest's own disk.
|
|
#
|
|
# The fix
|
|
# -------
|
|
# Give each mount point its own scratch tree:
|
|
# * Host / container -> the default in-repo .build/ (this script prints nothing, so the
|
|
# existing `swift build` behaviour is untouched — zero risk).
|
|
# * macOS VM (/Volumes) -> a scratch tree on the GUEST's local disk under $HOME, keyed by a
|
|
# hash of the mount path so distinct shares/worktrees never collide.
|
|
#
|
|
# This is the general recipe for "build artifact embeds a host path that differs across
|
|
# VM/sandbox mounts": don't try to rewrite the baked-in paths after the fact — instead put
|
|
# the artifact where its embedded path will be stable, one location per mount namespace.
|
|
#
|
|
# Escape hatch: export NUCLEIC_SCRATCH_PATH=/some/dir to force a specific scratch directory
|
|
# (useful for a throwaway build, or to pin the VM tree somewhere other than $HOME).
|
|
#
|
|
# Usage
|
|
# SCRATCH_ARGS="$(bash scripts/lib/build-scratch.sh)" # "" or "--scratch-path /…/<hash>"
|
|
# swift build $SCRATCH_ARGS ... # leave $SCRATCH_ARGS UNQUOTED
|
|
# swift build $SCRATCH_ARGS --show-bin-path # …and pass it to --show-bin-path too
|
|
#
|
|
# Takes the repo/working root as $1 (defaults to $PWD). Emits nothing but the flags on
|
|
# stdout, so it is safe to capture in `$(...)`.
|
|
|
|
set -euo pipefail
|
|
|
|
root="${1:-$PWD}"
|
|
|
|
# Explicit override always wins.
|
|
if [ -n "${NUCLEIC_SCRATCH_PATH:-}" ]; then
|
|
printf -- '--scratch-path %s' "$NUCLEIC_SCRATCH_PATH"
|
|
exit 0
|
|
fi
|
|
|
|
# Resolve symlinks before classifying. Inside the macOS guest the repo's ORIGINAL host
|
|
# path (/Users/…) is a symlink to the automounted virtiofs share (/Volumes/My Shared
|
|
# Files/…) — the engine re-creates it (MacVMEngine+HostPaths.swift, see MACOS_VM.md §6.1)
|
|
# so SwiftPM's baked-in /Users/… paths resolve in the guest. mac_vm_exec runs from that
|
|
# /Users path BY DEFAULT, so `$root` is that symlink and the bare "/Volumes/*" match below
|
|
# would miss it — leaving .build/ on the slow virtio-fs share, where SwiftPM's XCFramework
|
|
# extraction (Sparkle's binary target) fails outright because virtio-fs supports neither
|
|
# clonefile() nor the framework symlinks the unpack needs. Look THROUGH the link so a build
|
|
# launched from either path is recognized as being on the share.
|
|
resolved="$root"
|
|
if cd "$root" 2>/dev/null; then
|
|
resolved="$(pwd -P)"
|
|
fi
|
|
|
|
case "$resolved" in
|
|
/Volumes/*)
|
|
# On the VM share — reached directly via the /Volumes automount or through the /Users
|
|
# symlink. Redirect onto the guest's own fast, private disk. Hash the ORIGINAL,
|
|
# unresolved `$root` (not `$resolved`): a build from the /Users path bakes /Users/… into
|
|
# its tree while one from the /Volumes path bakes /Volumes/…, so they must NOT share a
|
|
# scratch tree or they poison each other's absolute paths — the very thing this guards.
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
tag="$(printf '%s' "$root" | shasum | cut -c1-12)"
|
|
else
|
|
tag="$(printf '%s' "$root" | sha1sum | cut -c1-12)"
|
|
fi
|
|
printf -- '--scratch-path %s/.nucleic-scratch/%s' "${HOME:-/tmp}" "$tag"
|
|
;;
|
|
*)
|
|
# Host / sandbox container: keep the default in-repo .build/. Print nothing.
|
|
:
|
|
;;
|
|
esac
|