Un même commit peut réussir sur une machine de développement, mais provoquer l’arrêt anormal de swift-frontend lors d’une compilation continue sur un Mac cloud. Une nouvelle exécution réussit parfois, puis, après avoir vidé le cache, l’échec se déplace vers un autre fichier. Dans cette situation, le geste le plus risqué consiste à modifier successivement le code source, mettre à jour les dépendances et supprimer tous les caches : l’environnement du problème change sur plusieurs plans à la fois, et les conditions qui déclenchaient réellement le plantage du compilateur disparaissent. Il est plus sûr de commencer par identifier la nature de l’incident, puis d’en réduire le périmètre en ne faisant varier qu’un seul paramètre à la fois.
Vérifier qu’il s’agit bien d’un plantage du compilateur
Un échec de CompileSwiftSources ne signifie pas nécessairement que le compilateur a planté. Une erreur de syntaxe, un échec d’inférence de types ou un module manquant peuvent également produire un état de sortie non nul à cette étape. Les signes qui justifient réellement de suivre la procédure décrite ici sont notamment les suivants :
- Le journal indique explicitement que
swift-frontends’est arrêté anormalement ou mentionnesignal 11. - La sortie contient
Stack dump, un échec d’assertion interne ou une pile d’appels du compilateur. - Un rapport
swift-frontenddont l’horodatage correspond à l’incident apparaît dans~/Library/Logs/DiagnosticReports/. - Le même code source et la même commande reproduisent plusieurs fois le problème, au lieu d’un simple incident ponctuel de session distante.
Commencez par consigner les informations sur la chaîne d’outils et la machine hôte. Ne conservez pas uniquement les dix dernières lignes du journal :
mkdir -p diagnostics
{
date -u
sw_vers
uname -m
xcode-select -p
xcodebuild -version
xcrun swiftc --version
} | tee diagnostics/environment.txt
find "$HOME/Library/Logs/DiagnosticReports" \
-maxdepth 1 -type f -name 'swift-frontend*' \
-exec cp {} diagnostics/ \;
Le rapport de plantage, la commande de compilation complète et le code source déclencheur doivent provenir de la même reproduction. Regrouper des éléments issus de plusieurs exécutions produit souvent de fausses pistes impossibles à vérifier.
Préserver l’état initial avec une compilation isolée
Préparez un répertoire DerivedData dédié à la reproduction et enregistrez l’intégralité de la sortie du terminal. Ne supprimez pas immédiatement les caches globaux, car leur éventuelle participation au déclenchement fait elle-même partie des variables à vérifier.
set -o pipefail
rm -rf "$PWD/.diagnostics-derived-data"
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath "$PWD/.diagnostics-derived-data" \
-jobs 1 \
build 2>&1 | tee diagnostics/build-single-job.log
La réduction du parallélisme à une seule tâche sert uniquement à déterminer si le plantage est lié à la compilation parallèle. Il ne s’agit pas d’une configuration à conserver sur le long terme. Si le plantage reste systématique avec une seule tâche, conservez ce journal. S’il disparaît, testez séparément -jobs 2 et la valeur de parallélisme habituelle, en gardant le code source et le répertoire DerivedData identiques. Répétez chaque série au moins trois fois et notez précisément s’il s’agit d’une réussite, d’une erreur de compilation ordinaire ou d’un plantage du compilateur. La seule mention « échec » ne suffit pas.
Lors de la reproduction sur un Mac cloud OVPS, consignez également le chemin Xcode réellement sélectionné. Comparer uniquement le nom de version affiché dans l’interface ne suffit pas : le numéro de build et la chaîne d’outils Swift peuvent différer au sein d’une même version majeure.
Construire une matrice comparative à variable unique
Commencez l’analyse par les variables les moins coûteuses et les moins destructrices. Ne modifiez qu’un seul élément à la fois et créez une nouvelle copie du journal pour chaque essai.
| Élément comparé | Référence | Valeur modifiée | Question à trancher |
|---|---|---|---|
| Parallélisme | -jobs 1 |
Valeur habituelle | Le problème apparaît-il uniquement en compilation parallèle ? |
| DerivedData | Répertoire isolé | Nouveau répertoire vide | Le problème dépend-il d’artefacts intermédiaires existants ? |
| Mode de compilation | Valeur actuelle du projet | Valeur temporaire de comparaison | Le problème est-il lié au chemin de compilation incrémentielle ou par module entier ? |
| Niveau d’optimisation | Réglage Debug | Réglage de comparaison autorisé par le projet | Le problème apparaît-il uniquement pendant l’optimisation ? |
| Chaîne d’outils | Version actuelle épinglée | Autre version validée | S’agit-il d’une régression propre à une chaîne d’outils ? |
Ne changez pas simultanément de version de Xcode, ne mettez pas à jour les dépendances et ne videz pas les caches en une seule opération. Si le problème disparaît après un changement de chaîne d’outils, vous pouvez seulement conclure qu’il dépend de cette combinaison de versions, pas que la nouvelle version l’a définitivement corrigé. Consignez également dans le même relevé le fichier qui échoue, le mode de compilation et le nombre de déclenchements.
Détecter les faux effets du cache
N’examinez plus précisément le cache des modules et la base de données de compilation que si un répertoire DerivedData vide réussit alors que l’ancien répertoire échoue systématiquement. Archivez d’abord le répertoire problématique, puis procédez à un nettoyage ciblé. Une suppression globale immédiate ferait perdre toute possibilité de comparer les anciens artefacts aux nouveaux.
Réduire le fichier en échec à un cas de reproduction minimal
Repérez dans le journal complet la tâche de compilation Swift qui échoue. Si le problème peut être reproduit hors du projet, copiez les déclarations concernées dans Repro.swift, puis commencez par une vérification des types :
xcrun swiftc -typecheck Repro.swift 2>&1 | tee diagnostics/repro.log
Procédez à la réduction dans l’ordre inverse des dépendances : supprimez d’abord les méthodes sans rapport avec le problème, puis les implémentations de protocoles, les contraintes génériques et les wrappers de propriétés. Exécutez le script de vérification après chaque suppression. Ne vous fiez pas à l’apparence d’une syntaxe qui semblerait « complexe » : les plantages du compilateur proviennent souvent de l’association de deux fonctionnalités ordinaires.
#!/bin/zsh
set -o pipefail
output="$(mktemp)"
xcrun swiftc -typecheck Repro.swift >"$output" 2>&1
if grep -Eiq 'signal 11|segmentation fault|stack dump|swift-frontend.*failed' "$output"; then
cp "$output" diagnostics/latest-crash.log
rm -f "$output"
exit 0
fi
rm -f "$output"
exit 1
Ce script considère que « le plantage ciblé se produit encore » correspond à une réussite, ce qui facilite une réduction manuelle par dichotomie ou l’utilisation d’un outil de minimisation du code source. Après chaque réduction, vérifiez également que la signature de l’erreur reste identique. Si une assertion interne est remplacée par une simple erreur de type, cela signifie qu’une condition essentielle a été supprimée.
Si le problème ne peut pas être reproduit dans un seul fichier, conservez la plus petite frontière de module possible : un projet minimal, les réglages de compilation indispensables, les versions de dépendances verrouillées et une commande d’exécution unique. N’incluez aucune donnée métier, aucun identifiant d’accès ni aucune ressource sans rapport avec le problème.
Préparer un dossier de diagnostic vérifiable
Le dossier final doit permettre à un autre Mac cloud d’effectuer la vérification sans explication orale. Il est recommandé de n’y inclure que les éléments suivants :
README.md: comportement attendu, commande d’exécution, nombre de répétitions et résultats observés.environment.txt: architecture du système, numéro de build de Xcode et version de Swift.Repro.swiftou projet minimal : uniquement le code source indispensable.build.log: sortie standard et sortie d’erreur complètes, sans troncature.- Rapport de plantage : fichier de diagnostic correspondant à l’heure de l’exécution concernée.
check.sh: script dont l’état de sortie indique clairement si le plantage ciblé a été reproduit.
Avant la remise, exécutez une nouvelle fois l’ensemble dans un nouveau répertoire afin de vérifier que le script ne dépend d’aucun chemin absolu vers le projet d’origine, d’aucun répertoire utilisateur ni d’aucun cache de modules résiduel. Si le problème ne survient qu’avec un niveau de parallélisme précis, indiquez clairement le paramètre correspondant et la probabilité de reproduction. S’il n’apparaît qu’avec un numéro de build particulier de la chaîne d’outils, consignez également la version de référence qui réussit. Vous obtiendrez ainsi non pas une simple affirmation selon laquelle « Swift plante parfois », mais un ensemble de preuves techniques reproductibles, comparables et exploitables pour poursuivre la correction.
Questions fréquentes
Un échec de CompileSwiftSources indique-t-il toujours un crash du compilateur ?
Non. Une erreur de syntaxe, de typage ou de dépendance peut échouer au même stade. Il faut rechercher une terminaison anormale de swift-frontend, un signal, une pile interne ou un rapport de crash correspondant.
Faut-il conserver tout le projet Xcode pour signaler le problème ?
Non. Si un fichier autonome et une commande swiftc reproduisent le crash, ils constituent un meilleur cas de test. Un projet réduit reste nécessaire lorsque le défaut dépend des modules ou des réglages de build.
Placez la prochaine compilation sur un nœud physique dédié
Choisissez parmi trois configurations Apple Silicon et six nœuds proposés à la vente. La disponibilité réelle est indiquée en temps réel dans la console.