Le code n’a pas changé, mais la deuxième compilation Xcode sur le Mac cloud continue de compiler un grand nombre de cibles. Avec le même commit dans un nouvel espace de travail, le comportement redevient normal. Ce type de problème ne vient pas nécessairement de DerivedData : le code source, les fichiers générés ou les artefacts mis en cache peuvent également porter des dates de modification incorrectes. Vider le cache peut temporairement supprimer les symptômes, mais si les processus de synchronisation et de restauration restent inchangés, la dérive d’horodatage finira par réapparaître dans l’espace de travail.
Vérifier qu’il s’agit bien d’une dérive d’horodatage
Commencez par fixer le commit, le Scheme, le chemin de Xcode et les paramètres de compilation, puis lancez deux fois de suite exactement la même compilation. Laissez la première exécution produire tous les artefacts intermédiaires, puis observez les tâches qui s’exécutent encore lors de la deuxième. Si le même ensemble de tâches Swift, de traitement des ressources ou de scripts se répète, comparez les résultats obtenus dans un espace de travail propre et dans un espace de travail réutilisé.
Ne vous limitez pas à la durée totale de compilation. Relevez surtout les chemins d’entrée des tâches réexécutées, les sorties des scripts et les répertoires générés, puis vérifiez si un outil réécrit des fichiers avant le début de la compilation. Les signes courants sont notamment les suivants :
- la date de modification d’un fichier est postérieure à l’heure actuelle du système ;
- après extraction du cache, les sorties sont plus récentes ou plus anciennes que les sources ;
- un script de génération écrase à chaque exécution un fichier dont le contenu n’a pas changé ;
- un outil de synchronisation conserve une heure anormale provenant d’une autre machine ;
- l’heure du volume qui héberge l’espace de travail est correcte, mais les métadonnées de l’archive sont incohérentes.
Une compilation incrémentale ne dépend pas uniquement du contenu des fichiers. Dès que la relation temporelle entre les entrées et les sorties est faussée, le système de compilation peut continuellement considérer des tâches déjà terminées comme devant être relancées.
Rechercher les fichiers datés dans le futur et les répertoires anormaux
Vérifiez d’abord l’heure et le fuseau horaire du système, puis recherchez les fichiers dont la date est postérieure de plus de cinq minutes à l’heure actuelle. Cette marge de cinq minutes évite les très légers écarts de mesure tout en permettant de détecter une dérive évidente.
#!/bin/zsh
set -euo pipefail
root="${1:-$PWD}"
limit=$(( $(date +%s) + 300 ))
find "$root" -type f -print0 |
while IFS= read -r -d '' file; do
modified=$(stat -f '%m' "$file")
if (( modified > limit )); then
printf '%s\t%s\n' \
"$(date -r "$modified" '+%Y-%m-%d %H:%M:%S %z')" \
"$file"
fi
done
L’analyse doit couvrir au minimum le code source, les fichiers de projet, les scripts, les ressources, les répertoires de génération de code et les caches restaurés. Évitez de commencer par l’ensemble du répertoire utilisateur : les caches des gestionnaires de paquets, les journaux et les fichiers système généreraient trop de bruit. Pour chaque fichier détecté, exécutez ensuite stat -x 文件路径 afin de vérifier sa date de modification, sa date de changement et le volume auquel il appartient.
Si les anomalies se concentrent dans un seul répertoire, il est généralement possible de les rattacher directement à une étape de téléchargement, d’extraction, de synchronisation ou de génération de code. Si elles sont réparties dans tout le dépôt, examinez plutôt la méthode utilisée pour copier l’espace de travail ainsi que les scripts d’initialisation exécutés avant le démarrage de la tâche.
Choisir la correction selon l’origine du problème
Chaque origine exige un traitement adapté. Il ne faut pas tout masquer avec touch.
| Symptôme | Origine fréquente | Traitement recommandé |
|---|---|---|
| Quelques fichiers source sont datés dans le futur | Archive ou source de synchronisation anormale | Récupérer de nouveau les fichiers et vérifier l’heure de la machine source |
| Les fichiers générés changent de date à chaque exécution | Le générateur les écrase systématiquement | Effectuer un remplacement atomique uniquement si le contenu change |
| Les relations temporelles du répertoire de cache sont incohérentes | Des métadonnées incorrectes sont conservées pendant la restauration | Abandonner ce lot de cache et reconstruire la clé de cache |
| Les dates de tout l’espace de travail sont proches de l’heure de copie | Les paramètres de copie modifient les métadonnées | Uniformiser la méthode d’extraction et ne pas mélanger plusieurs stratégies de synchronisation |
| Les sorties sont antérieures aux entrées et le restent | Les dates internes du paquet de cache sont incorrectes | Régénérer le cache et valider son manifeste |
Pour le code source géré par Git, la correction la plus sûre consiste généralement, après avoir vérifié l’absence de modifications non validées, à extraire de nouveau les répertoires concernés plutôt qu’à réécrire les dates de tout le dépôt. Pour les fichiers générés, le générateur doit d’abord écrire dans un fichier temporaire, comparer le contenu, puis effectuer le remplacement :
generate_config > Config.generated.swift.tmp
if ! cmp -s Config.generated.swift.tmp Config.generated.swift; then
mv Config.generated.swift.tmp Config.generated.swift
else
rm Config.generated.swift.tmp
fi
Ainsi, lorsque le contenu ne change pas, la date de modification du fichier d’origine reste intacte et les tâches de compilation qui en dépendent ne sont pas relancées inutilement.
Vérifier les limites entre cache et synchronisation
Avant de restaurer un cache, déterminez précisément quels répertoires peuvent être réutilisés d’une tâche à l’autre. Les produits de compilation, les caches de modules et les caches des gestionnaires de paquets n’ont pas le même cycle de vie et ne doivent pas être regroupés dans une archive volumineuse impossible à retracer. Le manifeste du cache doit au minimum enregistrer la clé de cache, la version de Xcode, l’architecture, la commande de génération et le nombre de fichiers. Après la restauration, effectuez un contrôle préalable des horodatages avant d’autoriser le passage à la phase principale de compilation.
La stratégie de synchronisation doit également préciser si les dates de modification sont conservées. Leur conservation facilite les décisions de compilation incrémentale, mais propage telles quelles les heures incorrectes de la machine source. À l’inverse, ne pas les conserver peut donner l’impression que tous les fichiers viennent d’être modifiés. L’essentiel n’est pas d’imposer un paramètre particulier, mais de faire adopter à toute l’équipe une seule stratégie validée et de l’inscrire dans les scripts de tâche.
Si le code est transmis sous forme d’archive, commencez par l’extraire dans un répertoire isolé. Analysez-y les dates futures, vérifiez le nombre de fichiers et contrôlez l’état Git, puis basculez de manière atomique vers l’espace de travail définitif. N’écrasez pas directement un répertoire qui contient encore d’anciens artefacts intermédiaires.
Ajouter la référence temporelle au point d’entrée des tâches
À terme, ce contrôle doit être exécuté avant la résolution des dépendances et la compilation Xcode. Si des fichiers datés dans le futur sont détectés, faites immédiatement échouer la tâche et affichez un nombre limité de chemins. Ne corrigez pas automatiquement les dates avant de poursuivre la compilation, car cette correction effacerait les éléments nécessaires au diagnostic.
Il est recommandé de conserver les informations suivantes pour chaque tâche :
- la sortie de
dateet desystemsetup -gettimezone; - le commit actuel, le chemin de Xcode et les informations du SDK ;
- le nombre de fichiers datés dans le futur ainsi que les premiers chemins concernés ;
- la clé de cache et le résultat de la restauration ;
- les différences de tâches entre la première et la deuxième compilation.
Après la correction, lancez trois exécutions consécutives avec le même commit : la première établit le cache, la deuxième vérifie le comportement incrémental et la troisième confirme que le résultat n’est pas dû au hasard. Si les deuxième et troisième exécutions répètent encore la même tâche, poursuivez l’analyse à partir des fichiers d’entrée de cette tâche au lieu de tout nettoyer de nouveau. L’objectif de la gestion des horodatages n’est pas de donner la même date à tous les fichiers, mais de maintenir entre le code source, les fichiers générés et les caches un ordre temporel explicable et vérifiable.
Questions fréquentes
Faut-il appliquer touch à tout le dépôt après avoir trouvé un fichier daté dans le futur ?
Non. Cela modifie tous les horodatages et déclenche généralement une recompilation étendue. Corrigez uniquement les fichiers concernés ou recréez proprement le répertoire affecté.
Pourquoi le build complet reste-t-il normal alors que le build incrémental ralentit ?
Le build incrémental réutilise un graphe de dépendances et des sorties existantes. Un fichier daté dans le futur ou un cache restauré avec de mauvaises dates peut invalider ce graphe à chaque exécution.
Configurez un Mac dans le cloud pour votre prochaine tâche de développement
Choisissez Oak M4, Oak M4 Plus ou Oak M4 Pro, puis configurez votre location sur l’un des cinq nœuds physiques selon l’emplacement de votre équipe.