From 59e6e8d6069863d4f97caa179bd5ea9019f97492 Mon Sep 17 00:00:00 2001 From: EJ <1694850+ejc3@users.noreply.github.com> Date: Tue, 27 Oct 2026 06:32:34 +0000 Subject: [PATCH] Mac: DerivedData on the scratch drive via mac-setup; simulators moved there, with what CoreSimulator needs first mac-setup sets Xcode's IDECustomDerivedDataLocation from ~/.config/mac-setup/deriveddata when its disk is mounted. The README records the simulator move (a symlink to the scratch drive), the two one-time steps at the Mac it depends on (volume ownership and Full Disk Access for CoreSimulatorService), and how to move and verify it. --- README.md | 28 ++++++++++++++++++++++++++-- scripts/mac/install.zsh | 16 ++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) 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