Bei einem Cloud-Mac, der dauerhaft für Archivierungen und automatisierte Tests eingesetzt wird, liegt die häufigste Fehlerursache nicht unbedingt im Code. Xcode schreibt fortlaufend Daten in DerivedData, Archive, Geräte-Supportdateien und Simulatorverzeichnisse. Ist das Laufwerk nahezu voll, kann der Compiler irreführende Schreibfehler melden; auch der Signierungsschritt hinterlässt unter Umständen nur unvollständige Artefakte. Eine zuverlässige Strategie besteht deshalb nicht darin, regelmäßig sämtliche Verzeichnisse zu leeren. Stattdessen sollten zunächst die tatsächliche Belegung ermittelt, eine Kapazitätsschranke für Builds eingerichtet und die Daten anschließend nach ihrer Wiederherstellbarkeit gestaffelt bereinigt werden.
Ausgangswerte für die Speicherbelegung erfassen
Erfassen Sie zuerst den freien Systemspeicher und prüfen Sie anschließend die Entwicklungsverzeichnisse. df zeigt, wie viel Speicher im Dateisystem noch beschreibbar ist. Mit du lässt sich ermitteln, welche Verzeichnisse den Platz belegen. Die beiden Werkzeuge erfüllen unterschiedliche Aufgaben und können einander nicht ersetzen.
df -Pk /
for path in \
"$HOME/Library/Developer/Xcode/DerivedData" \
"$HOME/Library/Developer/Xcode/Archives" \
"$HOME/Library/Developer/CoreSimulator" \
"$HOME/Library/Developer/Xcode/iOS DeviceSupport"
do
if [ -e "$path" ]; then
du -sk "$path"
fi
done
Sinnvoll sind jeweils eine Messung vor einem sauberen Build, nach Abschluss der Archivierung und nach dem Ende der Tests. Diese drei Werte zeigen den Speicherzuwachs eines einzelnen Jobs und verhindern, dass normale Lastspitzen fälschlich als Speicherleck eingestuft werden. Um besonders große Verzeichnisse innerhalb von DerivedData zu finden, führen Sie Folgendes aus:
du -sk "$HOME/Library/Developer/Xcode/DerivedData"/* 2>/dev/null \
| sort -nr \
| head -20
Der von APFS ausgewiesene freie Speicher kann durch löschbare Daten beeinflusst werden. Eine Kapazitätsschranke für Builds sollte die von
dfgemeldeten verfügbaren Blöcke auswerten. Die nominelle Größe einzelner Verzeichnisse reicht nicht aus, um den erfolgreichen Abschluss eines Jobs sicher vorherzusagen.
Eine klare Kapazitätsschranke für Builds definieren
Der Schwellenwert sollte aus den tatsächlich gemessenen Spitzenwerten abgeleitet und nicht allein nach Erfahrungswerten fest eingetragen werden. Verwenden Sie zunächst 30GB als Ausgangspunkt und erfassen Sie den niedrigsten freien Speicher während einer vollständigen Archivierung. Nach mehreren stabilen Durchläufen kann der Schwellenwert auf das 1,5-Fache des Spitzenzuwachses angepasst werden. Der zusätzliche Platz für exportierte Artefakte muss ebenfalls berücksichtigt werden.
Das folgende Skript kann ganz am Anfang der Pipeline ausgeführt werden. Reicht der Speicher nicht aus, beendet es sich mit dem Statuscode 75. Der Scheduler kann den Job dadurch als vorübergehend nicht ausführbar markieren, statt einen Build zu starten, der mit hoher Wahrscheinlichkeit fehlschlägt.
#!/bin/zsh
set -euo pipefail
minimum_kb=$((30 * 1024 * 1024))
available_kb=$(df -Pk / | awk 'NR == 2 {print $4}')
if (( available_kb < minimum_kb )); then
printf 'Insufficient disk capacity: %s KB available
' "$available_kb"
exit 75
fi
printf 'Disk capacity check passed: %s KB available
' "$available_kb"
Der Schwellenwert sollte für jeden Knoten separat gespeichert werden. Unterschiedliche Xcode-Versionen, Simulatorkombinationen und Projektgrößen erzeugen jeweils eigene Lastspitzen. Die Messwerte eines kleinen Projekts dürfen daher nicht als Kapazitätsnachweis für einen großen Workspace dienen.
Jeden Build auf ein eigenes Verzeichnis begrenzen
Ein gemeinsam genutztes standardmäßiges DerivedData-Verzeichnis verursacht nicht nur Kapazitätsprobleme. Es lässt sich außerdem kaum zuverlässig feststellen, welches Verzeichnis zu welchem Job gehört. Wird dem Workspace ein eigener Pfad zugewiesen, können seine Daten nach Abschluss des Jobs gezielt gelöscht werden, ohne parallel laufende Builds zu beeinträchtigen.
job_root="$HOME/build-jobs/$BUILD_ID"
derived_data="$job_root/DerivedData"
archive_path="$job_root/artifacts/App.xcarchive"
mkdir -p "$job_root/artifacts"
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-derivedDataPath "$derived_data" \
-archivePath "$archive_path" \
archive
BUILD_ID sollte vom Jobsystem bereitgestellt werden und ausschließlich Buchstaben, Ziffern, Punkte, Unterstriche oder Bindestriche enthalten. Vor dem Löschen muss außerdem geprüft werden, ob sich der Zielpfad tatsächlich unterhalb von build-jobs befindet. So wird verhindert, dass eine leere Variable den Löschbereich versehentlich auf das Home-Verzeichnis ausweitet.
Daten nach Wiederherstellbarkeit gestaffelt bereinigen
Die Bereinigung sollte mit Daten beginnen, die sich neu erzeugen lassen. Archive, die eine manuelle Prüfung erfordern, kommen erst zuletzt an die Reihe.
| Stufe | Daten | Empfohlene Maßnahme | Wichtigste Grenze |
|---|---|---|---|
| 1 | DerivedData abgeschlossener Jobs | Nach Jobverzeichnis löschen | Sicherstellen, dass kein Build-Prozess darauf zugreift |
| 2 | Nicht mehr verfügbare Simulatoreinträge | Mit simctl bereinigen |
Das Verzeichnis Devices nicht direkt löschen |
| 3 | Alte Geräte-Supportdateien | Anhand der tatsächlich getesteten Versionen prüfen | Noch für das Debugging benötigte Systemversionen behalten |
| 4 | xcarchive und dSYM | Nach manueller Prüfung auslagern | Veröffentlichte Versionen müssen nachvollziehbar bleiben |
Nicht mehr gültige Simulatoren sollten mit dem dafür vorgesehenen Systemwerkzeug entfernt werden:
xcrun simctl delete unavailable
Leeren Sie CoreSimulator/Devices nicht direkt. Wenn der Verzeichnisinhalt nicht mehr mit dem Zustand des Simulatordienstes übereinstimmt, entstehen beim späteren Erstellen und Starten von Geräten Probleme, die deutlich schwerer zu diagnostizieren sind. Auch Archive sollten nicht pauschal gelöscht werden, nur weil sie älter als eine bestimmte Anzahl von Tagen sind. Ein xcarchive kann die dSYM-Dateien einer veröffentlichten Version enthalten. Erfassen Sie deshalb zuerst Version, Build-Nummer und Auslagerungsort, bevor Sie die lokale Kopie entfernen.
Drei typische Fehler bei der automatischen Bereinigung vermeiden
Erstens dürfen gemeinsam genutzte Verzeichnisse nicht während eines laufenden Builds bereinigt werden. Selbst eine offensichtlich alte Datei kann vom Compiler noch über einen Index oder ein Zwischenprodukt referenziert werden. Der Bereinigungsjob sollte daher eine knotenweite Sperre anfordern oder ausschließlich eigenständige Arbeitsverzeichnisse bearbeiten, deren Jobs bereits als abgeschlossen markiert wurden.
Zweitens ist eine erfolgreich ausgeführte Löschoperation nicht automatisch mit tatsächlich freigegebenem Speicher gleichzusetzen. Führen Sie nach dem Löschen erneut df -Pk / aus und prüfen Sie, ob die Zahl der verfügbaren Blöcke wirklich gestiegen ist. Bleibt sie unverändert, suchen Sie nach Dateien, die noch von Prozessen geöffnet sind, statt denselben Löschbefehl wiederholt auszuführen.
Drittens sollte ein Skript nicht selbst entscheiden, ob ein Archiv wichtig ist. Unbeaufsichtigte Jobs dürfen lediglich Löschkandidaten auflisten und beispielsweise Verzeichnisgröße, letzten Änderungszeitpunkt und zugehörige Build-Nummer erfassen. Ob ein Eintrag ausgelagert oder gelöscht wird, muss anhand der Release-Dokumentation entschieden werden.
Die Bereinigung mit einem reproduzierbaren Build abnehmen
Führen Sie nach der Bereinigung mindestens eine Archivierung aus, die dem regulären Produktionsjob entspricht. Erfassen Sie für die Abnahme den Speicher zu Beginn, den niedrigsten freien Speicher, den Speicher am Ende, den Archivpfad und den Exit-Status. Prüfen Sie außerdem, ob das .xcarchive vollständig erzeugt wurde. Wenn der Knoten auch Simulatortests ausführt, starten Sie jede beibehaltene Version aus der Testmatrix mindestens einmal. Vergewissern Sie sich dabei, dass sich die Geräte ordnungsgemäß erstellen, starten und herunterfahren lassen.
Die abschließende Checkliste kann kurz bleiben: Die Kapazitätsschranke vor dem Build wurde passiert; der Job verwendet ein eigenes DerivedData-Verzeichnis; nach seinem Ende sind keine Compilerprozesse zurückgeblieben; Archiv und dSYM wurden erfasst; simctl list enthält keine fehlerhaften Geräte; der verbleibende Speicher liegt über dem Schwellenwert für den nächsten Job. Erst wenn alle diese Bedingungen erfüllt sind, ist die Bereinigung vollständig abgeschlossen.
Häufig gestellte Fragen
Wie viel freien Speicher sollte ein Xcode-Build-Knoten mindestens haben?
Als Startwert sind 30GB vor dem Build sinnvoll. Danach sollte der Grenzwert anhand des höchsten gemessenen Verbrauchs eines vollständigen Archivs angepasst werden; große Projekte benötigen mindestens das 1,5-Fache dieses Spitzenwerts.
Darf das gesamte DerivedData-Verzeichnis gelöscht werden?
Ja, sofern garantiert kein Build darauf zugreift. Sicherer ist ein eigener DerivedDataPath pro Arbeitsbereich, damit ausschließlich abgeschlossene Auftragsverzeichnisse entfernt werden.
Welche Xcode-Daten gehören nicht in eine automatische Bereinigung?
Laufende Archive, noch nicht gesicherte xcarchive-Pakete, dSYM-Dateien veröffentlichter Versionen und aktiv verwendete Simulatorgeräte müssen von automatischen Löschregeln ausgeschlossen bleiben.
Eine reproduzierbare Umgebung auf demselben Cloud-Mac behalten
Wählen Sie Modell, Region und Mietdauer fest aus und erledigen Sie Entwicklungs- oder Automatisierungsaufgaben mit vollständiger grafischer macOS-Oberfläche und Kommandozeile.