Wenn Grafikfunktionen auf dem Entwicklungsrechner einwandfrei laufen, aber erst gegen Ende der CI-Archivierung ein Metal-Kompilierfehler auftritt, liegt das meist nicht an „zu wenig Maschinenleistung“. In der Regel werden die Shader nicht als eigenständige Build-Eingaben verwaltet. Robuster ist es, auf dem Cloud-Mac zunächst mit der aktuellen Xcode-Werkzeugkette offline je eine metallib für Gerät und Simulator zu erzeugen und erst danach den vollständigen Projekt-Build zu starten. So werden Syntaxfehler, ein falsch gewähltes SDK und fehlende Funktionen früher sichtbar, während die Fehlerprotokolle kürzer bleiben.
Warum Shader eine eigene CI-Prüfung brauchen
Xcode verarbeitet .metal-Dateien automatisch, wenn sie einem Target hinzugefügt wurden. Fehler gehen jedoch häufig in langen Protokollen unter, die auch die Auflösung von Abhängigkeiten, das Kopieren von Ressourcen und die Codesignierung enthalten. Die separate Prüfung ersetzt den Xcode-Build nicht, sondern beantwortet vorab drei Fragen:
- Lassen sich alle Shader mit der aktuellen Werkzeugkette kompilieren?
- Können die Bibliotheken für Gerät und Simulator getrennt erzeugt werden?
- Lassen sich die von der App tatsächlich referenzierten Funktionsnamen aus der Bibliothek laden?
Kopieren Sie eine auf dem Entwicklungsrechner erzeugte
metallibnicht direkt in ein dauerhaft genutztes Artefaktverzeichnis. Nach Änderungen an Werkzeugkette, SDK oder Kompilierparametern kann die alte Datei dort weiterhin vorhanden sein, obwohl sie nicht mehr dem aktuellen Quellcode entspricht.
Im Repository sollten nur die .metal-Quelldateien und die Kompilierskripte eingecheckt werden. .air- und .metallib-Dateien gehören in ein bereinigbares Build-Verzeichnis. Wenn das Team einen Cache benötigt, sollte dessen Schlüssel mindestens den Quellcode-Hash, den Xcode-Pfad, die SDK-Version und die Zielplattform enthalten.
Werkzeugkette für den aktuellen Build festlegen
Auf einem Cloud-Mac können mehrere Xcode-Versionen installiert sein. Skripte sollten sich nicht auf die zuletzt in der grafischen Oberfläche ausgewählte Version verlassen, sondern DEVELOPER_DIR ausdrücklich prüfen. Der Pfad richtet sich nach der tatsächlichen Installation auf dem jeweiligen Knoten. Ein nicht verifizierter Wert darf nicht über mehrere Jobs hinweg wiederverwendet werden.
set -euo pipefail
: "${DEVELOPER_DIR:?DEVELOPER_DIR is required}"
xcodebuild -version
xcrun --sdk iphoneos --show-sdk-version
xcrun --sdk iphonesimulator --show-sdk-version
xcrun --sdk iphoneos --find metal
xcrun --sdk iphoneos metal --version
mkdir -p build/metal/metadata
{
echo "developer_dir=$DEVELOPER_DIR"
xcodebuild -version
echo "iphoneos=$(xcrun --sdk iphoneos --show-sdk-version)"
echo "iphonesimulator=$(xcrun --sdk iphonesimulator --show-sdk-version)"
xcrun --sdk iphoneos metal --version
} > build/metal/metadata/toolchain.txt
toolchain.txt sollte zusammen mit den Fehlerprotokollen archiviert werden. Damit lässt sich schnell unterscheiden, ob eine Regression im Quellcode vorliegt oder der Runner auf eine andere Xcode-Version umgestellt wurde. Es genügt nicht, nur xcodebuild -version zu erfassen, da auch das tatsächlich verwendete SDK das Ergebnis beeinflusst.
Artefakte für Gerät und Simulator getrennt kompilieren
Kompilieren Sie zunächst jede Quelldatei in eine eigene .air-Datei und führen Sie diese anschließend zu einer Bibliothek zusammen. Bei der dateiweisen Kompilierung lässt sich ein Fehler direkt der betroffenen Datei zuordnen, statt lediglich einen unspezifischen Linkerfehler zu erhalten.
set -euo pipefail
sources=(Shaders/*.metal)
if [ ! -e "${sources[0]}" ]; then
echo "No Metal source files found" >&2
exit 1
fi
for sdk in iphoneos iphonesimulator; do
out="build/metal/$sdk"
rm -rf "$out"
mkdir -p "$out/air"
for src in "${sources[@]}"; do
name="$(basename "$src" .metal)"
xcrun --sdk "$sdk" metal -c "$src" \
-o "$out/air/$name.air"
done
xcrun --sdk "$sdk" metallib "$out"/air/*.air \
-o "$out/AppShaders.metallib"
test -s "$out/AppShaders.metallib"
shasum -a 256 "$out/AppShaders.metallib" \
> "$out/AppShaders.metallib.sha256"
done
Die beiden Artefaktsätze müssen in getrennten Verzeichnissen gespeichert werden, damit ein später ausgeführter Job nicht die zuvor erzeugten Dateien überschreibt. Wenn der Quellcode Plattformen über C/C++-Präprozessormakros unterscheidet, sollten die Makroparameter zentral im Skript definiert werden. Die Xcode Build Phase muss dieselben Definitionen verwenden.
| Prüfobjekt | Geräte-Target | Simulator-Target |
|---|---|---|
| SDK | iphoneos |
iphonesimulator |
| Zwischendateien | Separate .air |
Separate .air |
| Ausgabebibliothek | Separat archiviert | Separat archiviert |
| Endabnahme | Gerät oder reguläre Archivierung | Smoke-Test im Simulator |
Funktionsvertrag mit einem Ladetest prüfen
Eine nicht leere Datei beweist lediglich, dass der Compiler eine Ausgabe erzeugt hat. Sie belegt nicht, dass die von der App gesuchten Funktionen weiterhin vorhanden sind. Nach dem Umbenennen einer Kernel- oder Fragment-Funktion bleiben die entsprechenden Zeichenfolgen im Swift-Code möglicherweise unverändert. Deshalb sollte das Test-Target die Bibliothek laden und die entscheidenden Einstiegspunkte abfragen.
import Metal
import XCTest
final class ShaderLibraryTests: XCTestCase {
func testRequiredFunctionsExist() throws {
let device = try XCTUnwrap(MTLCreateSystemDefaultDevice())
let url = try XCTUnwrap(
Bundle(for: Self.self).url(
forResource: "AppShaders",
withExtension: "metallib"
)
)
let library = try device.makeLibrary(URL: url)
for name in ["imageVertex", "imageFragment", "toneMapKernel"] {
XCTAssertNotNil(
library.makeFunction(name: name),
"Missing Metal function: \(name)"
)
}
}
}
Die Funktionsliste muss den Namen entsprechen, die die App beim tatsächlichen Erstellen ihrer Pipelines verwendet. Das Test-Bundle muss außerdem sicherstellen, dass das iphonesimulator-Artefakt kopiert wurde und nicht versehentlich eine im Workspace verbliebene Gerätedatei. Ein minimaler Smoke-Test erstellt ein MTLDevice, lädt die Bibliothek und fragt die Funktionen ab. Wenn Texturformate oder Einschränkungen für Thread-Gruppen relevant sind, sollte zusätzlich die Pipeline-Erstellung getestet werden.
Welche Nachweise bei Fehlern aufbewahrt werden sollten
Für die Analyse von Metal-Fehlern in der CI ist nicht das vollständige Arbeitsverzeichnis am wertvollsten, sondern eine kleine Menge direkt vergleichbarer Informationen:
DEVELOPER_DIRsowie die Versionen von Xcode und beiden SDKs;- die tatsächlich ausgeführten Kompilierbefehle und die Standardfehlerausgabe;
- der Quellcode-Hash jeder
.metal-Datei; - Größe und SHA-256 der beiden
metallib-Dateien; - der Name der nicht ladbaren Funktion, das Test-Target und das Ausführungsziel;
- die für den Job verwendete Commit-Kennung.
Die Quellcode-Hashes lassen sich mit find Shaders -name '*.metal' -print0 | sort -z | xargs -0 shasum -a 256 erzeugen. Eine Änderung am Artefakt-Hash allein sollte keinen Fehler auslösen, da auch ein Upgrade der Werkzeugkette die Binärdatei verändern kann. Vergleichen Sie stattdessen zunächst den Fingerabdruck der Werkzeugkette und verwenden Sie anschließend die Ergebnisse der erneuten Kompilierung, des Ladetests und der Pipeline-Erstellung als Prüfkriterien.
Auch bei solchen Jobs auf dedizierten physischen Knoten von MacMLab kann derselbe Workspace durch aufeinanderfolgende Ausführungen verunreinigt werden. Leeren Sie deshalb vor jedem Build das Zielverzeichnis und archivieren Sie nach Abschluss des Jobs nur Metadaten, Protokolle und die endgültigen Bibliotheken. So gelangen keine alten .air-Dateien in eine neue Bibliothek, und der nächste Fehler lässt sich von einem reproduzierbaren Ausgangspunkt aus untersuchen.
Häufig gestellte Fragen
Warum ist eine separate Shader-Kompilierung nötig, wenn Xcode sie bereits ausführt?
Sie meldet Syntax-, SDK- und Zielprobleme vor dem vollständigen Archiv und liefert kürzere Protokolle. Der reguläre Xcode-Schritt bleibt trotzdem bestehen.
Kann dieselbe metallib-Datei auf Gerät und Simulator verwendet werden?
Nein, darauf sollte man sich nicht verlassen. Beide Varianten werden mit iphoneos beziehungsweise iphonesimulator erzeugt und auf ihrem Ziel getestet.
Bedeutet ein geänderter Artefakt-Hash automatisch einen Fehler?
Nein. Zuerst müssen Xcode-Pfad, SDK- und Compiler-Version verglichen werden; entscheidend sind anschließend Ladeprüfung und Rauchtest.
Dedizierter physischer Knoten
Entwicklungs- und Build-Aufgaben auf einem dedizierten Cloud-Mac ausführen
Wählen Sie Modell, Knotenregion und Mietdauer passend zur Aufgabe. Jede Instanz läuft auf einem dedizierten physischen Rechner und nicht in einer virtuellen Umgebung.