diff --git a/README.md b/README.md index c0b4880a..247babfd 100644 --- a/README.md +++ b/README.md @@ -642,8 +642,7 @@ The internal disk runs nearly full with the owner's own data, so builds go to th external drive, one folder per project: `/Volumes/Scratch//`. For example `dolphin-maps/` holds its checkout, `build/` (DerivedData, archives, result bundles: `BUILD_DIR` in its scripts), `tours/` (screenshots) and `scratch/` (logs, helpers). -Simulator devices still live on the internal disk (CoreSimulator), so delete a tour's -simulator after use. Never write a project's files into `~/src` loose. +Never write a project's files into `~/src` loose. Temporary files follow the same rule. Anything a session creates with `mktemp` (a test run's DerivedData, a build's intermediates) lands in `TMPDIR`, which by default is on @@ -654,6 +653,31 @@ its disk is mounted, for every zsh, non-interactive SSH sessions included; with or the drive unplugged, the system default stays. On this Mac: `ssh mac 'mkdir -p ~/.config/mac-setup && echo /Volumes/Scratch/tmp > ~/.config/mac-setup/tmpdir'`. +Xcode's DerivedData works the same way: when `~/.config/mac-setup/deriveddata` names a folder +on a mounted disk, `scripts/mac-setup.sh` sets Xcode's `IDECustomDerivedDataLocation` to it, +which every `xcodebuild` without `-derivedDataPath` then uses. On this Mac: +`ssh mac 'echo /Volumes/Scratch/DerivedData > ~/.config/mac-setup/deriveddata'`, then rerun +`scripts/mac-setup.sh`. With the drive unplugged, Xcode builds fail rather than fill the +internal disk. + +Simulator devices are on the drive too: `~/Library/Developer/CoreSimulator/Devices` is a +symlink to `/Volumes/Scratch/CoreSimulator/Devices` (moved 2026-10-27, when they held 72 GB +of the internal disk; free space went from 18 GB to 88 GB). CoreSimulator needs two things +from the drive first, each a one-time step **(at the Mac)**, or it cannot create or read a +device there ("device remained in Creating state", Cocoa error 513): +- ownership on: `sudo diskutil enableOwnership /Volumes/Scratch`, then the drive's root back + to the user: `sudo chown "$(whoami)":staff /Volumes/Scratch`; +- Full Disk Access for the simulator service: System Settings > Privacy & Security > Full Disk + Access, add `/Library/Developer/PrivateFrameworks/CoreSimulator.framework/Versions/A/XPCServices/com.apple.CoreSimulator.CoreSimulatorService.xpc`. + +Check it with a throwaway set before moving: `xcrun simctl --set /Volumes/Scratch/ +create probe "iPhone 17" ` must list and boot. Then: `rsync -aH --delete` the folder +across while simulators run, `xcrun simctl shutdown all` and stop +`com.apple.CoreSimulator.CoreSimulatorService`, sync again, require identical listings, swap +in the symlink, and keep the old folder until `xcrun simctl list` shows the same device count. +(`ditto` folds `._` files into extended attributes, so its copy cannot be compared file for +file.) A tour's simulator is still deleted after use. + ##### MacBook: Checks ```bash diff --git a/scripts/mac/install.zsh b/scripts/mac/install.zsh index f0e4c788..face7648 100644 --- a/scripts/mac/install.zsh +++ b/scripts/mac/install.zsh @@ -50,6 +50,22 @@ for t in swift xcodebuild xcrun; do [ "$got" = "$bin/$t" ] || { echo "mac-setup: a non-interactive zsh finds $t at $got, not $bin/$t" >&2; exit 1; } done +# Xcode's DerivedData on the disk ~/.config/mac-setup/deriveddata names, when that file exists and the disk is +# mounted; Xcode's default is ~/Library/Developer/Xcode/DerivedData, on the internal disk. It is a user default, +# so it applies to Xcode and to every xcodebuild run without -derivedDataPath. +dd_file=$HOME/.config/mac-setup/deriveddata +if [ -r "$dd_file" ]; then + dd=$(<"$dd_file") + if [ -n "$dd" ] && [ -d "${dd:h}" ] && mkdir -p "$dd"; then + if [ "$(defaults read com.apple.dt.Xcode IDECustomDerivedDataLocation 2>/dev/null)" != "$dd" ]; then + defaults write com.apple.dt.Xcode IDECustomDerivedDataLocation "$dd" + echo "mac-setup: Xcode's DerivedData now goes to $dd" + fi + else + echo "mac-setup: WARNING: $dd_file names $dd, whose disk is not mounted; DerivedData left where it is" >&2 + fi +fi + # The pinned Peekaboo app and CLI, and its LaunchAgent (the wrapper above runs its CLI). if [ "$update_peekaboo" = --update-peekaboo ]; then /bin/sh "$src/peekaboo-install.sh" --update-running