Xcode 26 build database locked : comment le réparer pour une compilation à distance en 2026 ?

Symptôme : deux tâches lancées sur un Mac distant renvoient « Xcode 26 build database locked ».

Solution la plus rapide : arrêtez d’abord les tâches concurrentes qui partagent le même répertoire de construction, attribuez un DerivedData distinct à chaque tâche, puis ne nettoyez la base concernée qu’après avoir confirmé qu’aucun processus résiduel ne tourne.

Cet article s’adresse aux développeurs indépendants qui exécutent xcodebuild par SSH et retrouvent une tâche active après une coupure de session. Il concerne aussi les petites équipes dont la CI lance plusieurs constructions du même projet, ainsi que les responsables d’un Mac partagé pour Build, Test et Archive.

Ce que le message « build database locked » permet réellement de conclure

Le message indique qu’une opération de construction ne peut pas obtenir l’accès attendu à une base utilisée par le système de compilation. Il ne prouve pas, à lui seul, que la base est corrompue. Dans un environnement distant, la cause la plus fréquente à examiner est d’abord la concurrence : deux tâches écrivent dans le même espace de travail, une relance CI n’a pas annulé l’ancienne, ou un processus enfant continue après la disparition de la session SSH.

Apple décrit le système de compilation comme un ensemble qui coordonne les dépendances, les tâches et les produits générés ; la documentation officielle du système de compilation de Xcode constitue donc le point de départ pour distinguer une dépendance normale d’une écriture concurrente.

Ne commencez pas par supprimer tout le cache utilisateur. Cette action peut masquer le symptôme sans corriger la cause, détruire des traces utiles et rallonger le prochain diagnostic. La méthode de décision est la suivante :

  • si plusieurs processus travaillent encore sur le même chemin, préservez la tâche utile et arrêtez la tâche fautive ;
  • si les processus utilisent des chemins communs, isolez DerivedData, OBJROOT, SYMROOT, les résultats de test et les archives ;
  • si aucun processus ne reste actif, nettoyez uniquement le périmètre du travail affecté ;
  • après la correction, vérifiez séparément Build, Test et Archive au lieu de considérer une compilation réussie comme une preuve suffisante.

Première étape : conserver la scène complète de l’échec

Avant de relancer la CI ou de supprimer un fichier, conservez la commande complète, le répertoire de travail, les variables utiles et le premier message d’erreur pertinent. La dernière ligne du journal est souvent seulement la conséquence du verrou.

Un relevé exploitable doit contenir, avec les noms sensibles remplacés :

  • la commande complète xcodebuild, y compris le projet ou l’espace de travail, le schéma et les paramètres transmis ;
  • l’heure de début et de fin de chaque tâche ;
  • le répertoire depuis lequel la commande a été lancée ;
  • le chemin effectivement utilisé pour DerivedData ;
  • les chemins OBJROOT, SYMROOT, -resultBundlePath et -archivePath lorsqu’ils sont définis ;
  • l’identité du processus parent et des processus enfants ;
  • l’opération demandée : Build, Test, Archive ou téléversement ;
  • la première occurrence de build database locked, et non une copie tronquée du journal.

Remplacez le nom du projet, le schéma, l’utilisateur, l’adresse de l’hôte, le Bundle ID, le Team ID et les chemins privés par des valeurs neutres. La désidentification ne doit cependant pas supprimer la structure des arguments : le but est de savoir si deux tâches résolvent le même chemin.

Pour établir une chronologie depuis la session SSH, commencez par observer les processus, sans les terminer :

ps -axo pid,ppid,lstart,etime,user,command | grep -E 'xcodebuild|XCBBuildService|simctl' | grep -v grep

Complétez cette observation par le répertoire courant du processus lorsque votre système le permet :

lsof -p <PID> | grep -E 'cwd|DerivedData|Build|xcarchive|xcresult'

Ne supposez pas que le processus portant le nom xcodebuild est le seul responsable. Un service enfant peut encore tenir un fichier ouvert alors que la commande parente semble terminée. Le lien entre parent, enfant, répertoire et heure de démarrage est plus fiable que le simple résultat d’une recherche textuelle.

Le verrou vient-il de processus concurrents ou d’un même chemin partagé ?

Ces deux causes se ressemblent dans le journal, mais leur correction n’est pas la même. Une tâche concurrente exige d’abord une décision d’exploitation ; un chemin partagé exige une modification de la configuration. Dans les deux cas, tuer tous les processus liés à Xcode est une mauvaise première réaction.

Reconnaître une concurrence réelle

Cherchez notamment les situations suivantes :

  • un développeur a lancé une construction dans une session SSH, puis une tâche CI a démarré sur le même Mac ;
  • la CI a relancé une tâche après un délai d’attente sans annuler le processus initial ;
  • une coupure SSH a interrompu l’affichage, mais pas la commande exécutée sur l’hôte distant ;
  • Build, Test et Archive ont été déclenchés presque ensemble par des scripts distincts ;
  • deux utilisateurs disposent de la même configuration de construction par défaut.

Pour chaque PID trouvé, notez la tâche à laquelle il appartient. Si une Archive est presque terminée, l’arrêter peut compromettre l’xcarchive, les dSYM ou le journal de distribution. Si une tâche de test est abandonnée, son xcresult peut devenir incomplet. Si un script de téléversement est déjà engagé, sa suppression peut laisser une étape de publication sans état clair.

L’ordre de préférence est donc :

  • laisser se terminer une opération utile qui ne bloque pas les autres ;
  • annuler proprement la relance CI et attendre la sortie des processus enfants ;
  • terminer seulement la tâche identifiée comme doublon ou fautive ;
  • forcer un processus uniquement s’il ne répond plus et empêche la récupération du système.

Après l’arrêt, vérifiez de nouveau la liste des processus. Une absence de sortie ne suffit pas si une commande a été détachée ou exécutée par un autre utilisateur. Contrôlez également le journal de la CI afin de savoir si la tâche a été annulée, mise en échec ou simplement déconnectée.

Vérifier la collision des chemins

Deux tâches peuvent avoir des répertoires sources différents tout en écrivant dans le même DerivedData. Cela arrive lorsqu’un script fixe un chemin global, lorsqu’une configuration CI réutilise une variable statique ou lorsqu’un appel indirect ignore le chemin prévu par la tâche principale.

Inspectez les paramètres réellement transmis :

xcodebuild \
  -workspace <EspaceDeTravail.xcworkspace> \
  -scheme <Schema> \
  -configuration Release \
  -derivedDataPath "$DERIVED_DATA_PATH" \
  -resultBundlePath "$RESULT_PATH" \
  build

Le paramètre -derivedDataPath doit être propre à la tâche. Les réglages OBJROOT et SYMROOT doivent eux aussi être examinés lorsque le projet ou le script les définit explicitement. La référence des réglages de compilation Xcode aide à retrouver les réglages susceptibles de modifier l’emplacement final.

Ne mélangez pas les catégories de fichiers :

  • les sources et le dépôt doivent rester la base de travail ;
  • DerivedData, les objets intermédiaires et les produits temporaires doivent être isolables par tâche ;
  • xcresult doit être conservé avec l’identifiant du travail qui l’a produit ;
  • xcarchive doit être placé dans un espace d’artefacts distinct et durable ;
  • les dSYM doivent être sauvegardés avec l’archive correspondante ;
  • l’état d’un téléversement ne doit pas être déduit d’un simple dossier de compilation.

Voici le tableau de décision à appliquer avant de modifier la configuration :

Situation observée Action immédiate Organisation cible Critère de réussite
Deux tâches écrivent dans le même DerivedData Arrêter le doublon, puis relever les chemins Un chemin dérivé par tâche Les journaux montrent deux chemins distincts
Les sources sont séparées, mais OBJROOT ou SYMROOT est commun Corriger le réglage explicite Intermédiaires isolés par tâche Deux produits intermédiaires indépendants
Les archives sont écrites dans le répertoire temporaire Préserver les archives existantes archivePath séparé et durable Chaque tâche possède son propre xcarchive
Une coupure SSH laisse un processus actif Reprendre la main avant toute suppression Exécution détachable et journalisée Le processus est identifié, terminé ou achevé proprement
Aucun processus ne subsiste et l’erreur revient Nettoyer seulement le travail concerné Répertoire recréé sans toucher aux autres artefacts La même commande passe en tâche unique puis en Archive

Pour une CI parallèle, générez un chemin avec l’identifiant propre à la tâche, plutôt qu’avec le nom fixe du projet :

JOB_ROOT="$HOME/ci/work/<identifiant-de-tache>"
DERIVED_DATA_PATH="$JOB_ROOT/DerivedData"
RESULT_PATH="$JOB_ROOT/Results.xcresult"
ARCHIVE_PATH="$JOB_ROOT/Archives/App.xcarchive"

mkdir -p "$JOB_ROOT"

Le nom exact de l’identifiant dépend de votre orchestrateur. L’essentiel est qu’il ne soit pas partagé par deux tâches actives. Conservez ensuite les artefacts dans un emplacement de collecte qui ne sert pas directement de répertoire de construction.

Rappel : l’isolation ne signifie pas qu’il faut dupliquer indéfiniment les archives. Les intermédiaires doivent être séparés pendant l’exécution ; les artefacts finaux peuvent être centralisés après la fin de la tâche, une fois leur production terminée.

Une dépendance ou un script lance-t-il une construction imbriquée ?

Une construction imbriquée est différente d’une simple exécution parallèle de la CI. Le processus principal peut appeler un script qui relance xcodebuild, compile un sous-projet ou invoque un outil de publication avec le même répertoire de sortie.

Examinez les phases « Run Script », les dépendances entre cibles et les outils appelés par les scripts. La documentation Apple sur l’exécution de scripts personnalisés pendant une construction rappelle l’importance des entrées et sorties déclarées. Un script qui lit ou génère des fichiers sans déclaration claire peut être considéré comme nécessaire à des moments imprévus, tandis qu’un script qui relance une construction peut créer un second niveau de concurrence.

Cherchez dans les scripts :

grep -R "xcodebuild\|build-for-testing\|archive" Scripts .github ci 2>/dev/null

Cette recherche ne constitue pas une preuve ; elle indique seulement où examiner. Pour chaque appel trouvé, documentez :

  • qui déclenche le script ;
  • si l’appel vise le même projet ou une cible distincte ;
  • quels chemins d’entrée et de sortie sont utilisés ;
  • si la construction enfant est indispensable ;
  • à qui appartient l’archive ou le produit généré ;
  • comment revenir au comportement précédent en cas d’échec.

Ne désactivez pas automatiquement la construction parallèle dans Xcode. Cette option peut modifier le temps d’exécution sans supprimer un chemin partagé ni corriger un script qui lance une seconde commande. La configuration du schéma, des dépendances de cibles et des actions d’Archive doit être vérifiée à partir des réglages de schéma Xcode.

Après modification, recherchez dans le journal :

  • une seule invocation de la commande attendue ;
  • une séparation explicite des répertoires ;
  • l’absence de relance indirecte au même moment ;
  • un propriétaire clair pour le résultat final.

Une construction réussie n’est pas encore une validation complète si le script de publication n’a pas été exécuté ou si l’archive n’est pas exploitable.

Que nettoyer quand aucun processus ne reste actif ?

Lorsque xcodebuild, les tests et les services enfants ont réellement quitté le système, le verrou peut provenir d’une interruption brutale ou d’un état que la base ne parvient plus à réutiliser. Le nettoyage doit alors progresser par niveaux.

Commencez par conserver :

  • le dépôt source ;
  • les certificats et profils de signature ;
  • les archives existantes ;
  • les dSYM ;
  • les xcresult ;
  • les journaux de la tâche ;
  • la commande exacte ayant échoué.

Nettoyez ensuite uniquement le répertoire temporaire de la tâche fautive. Si le problème persiste, élargissez au DerivedData propre au projet. Ne supprimez pas immédiatement tout le cache de l’utilisateur : vous perdriez la possibilité de comparer l’état initial et vous pourriez perturber d’autres travaux.

Une séquence contrôlée peut ressembler à ceci :

mv "$DERIVED_DATA_PATH" "${DERIVED_DATA_PATH}.old-<identifiant>"
mkdir -p "$DERIVED_DATA_PATH"

Le déplacement, plutôt que la suppression immédiate, conserve une copie pour analyse et permet un retour en arrière. Lorsque vous êtes certain que cette copie ne contient aucun artefact nécessaire, sa suppression peut être planifiée séparément.

Relancez ensuite exactement la même commande, avec un chemin propre et un seul travail actif. Une variation simultanée de la version d’outil, du schéma, du répertoire et de la commande rendrait le résultat difficile à interpréter.

La suppression de DerivedData ne doit pas être confondue avec la suppression d’une archive. Pour les opérations de distribution signées, reportez-vous aux consignes Apple sur la création de code signé pour la distribution. Vos scripts doivent faire apparaître séparément les certificats, profils, archives et répertoires de travail.

FAQ de dépannage

Les réponses ci-dessous reprennent les recherches fréquentes autour de DerivedData, de xcodebuild et d’un Mac distant, mais ajoutent la condition qui permet de choisir l’action sans effacer des preuves.

Validation progressive après la correction

Ne passez pas directement à plusieurs Archives parallèles. Utilisez une progression qui permet de localiser une réapparition du problème :

  • lancez un Build unique avec un chemin DerivedData neuf ;
  • exécutez ensuite un Test unique et conservez son xcresult ;
  • relancez deux tâches parallèles avec des chemins entièrement distincts ;
  • effectuez une Archive réelle avec un archivePath propre ;
  • vérifiez que l’archive, les dSYM et les journaux sont récupérables ;
  • simulez une annulation de tâche et confirmez que ses processus enfants disparaissent ;
  • reconnectez-vous après une coupure SSH et vérifiez qu’une tâche détachée reste contrôlable ;
  • redémarrez l’hôte lorsque cette procédure fait partie de votre exploitation habituelle, puis contrôlez la recréation des répertoires.

Pour les tests et l’interprétation des résultats, utilisez la documentation Apple consacrée à l’exécution des tests et à la lecture des résultats. Le critère de passage n’est pas seulement l’absence du texte « build database locked » : deux tâches doivent produire deux résultats identifiables, et une Archive complète doit rester disponible après la construction.

Checklist à cocher avant de déclarer l’incident terminé

  • [ ] La commande complète et le premier message d’erreur ont été conservés.
  • [ ] Les noms sensibles ont été désidentifiés sans supprimer les arguments techniques.
  • [ ] Chaque PID a été relié à une tâche, un utilisateur et un chemin.
  • [ ] Les processus issus d’une ancienne session SSH ont été traités individuellement.
  • [ ] DerivedData possède un chemin propre à chaque tâche parallèle.
  • [ ] OBJROOT et SYMROOT ne pointent pas vers un répertoire commun non prévu.
  • [ ] xcresult, xcarchive et les dSYM ne sont pas stockés dans les intermédiaires.
  • [ ] Les scripts ne relancent pas silencieusement une construction concurrente.
  • [ ] Le nettoyage a commencé par le répertoire affecté, sans effacement global.
  • [ ] Un Build unique a réussi après la correction.
  • [ ] Un Test a produit un résultat complet.
  • [ ] Une Archive réelle a été créée et sauvegardée.
  • [ ] Une annulation, une coupure SSH et une reprise ont été vérifiées.
  • [ ] La procédure de récupération est documentée pour la prochaine tâche.

Si le problème réapparaît malgré des chemins indépendants, cherchez alors un défaut de gestion du cycle de vie : relance CI trop agressive, processus détaché, script d’arrêt incomplet ou hôte partagé sans contrôle suffisant. La documentation Apple sur les flux de compilation continue pour les projets et paquets Swift peut servir à comparer votre orchestration avec une séparation plus explicite des tâches.

Pour approfondir la continuité d’une construction après déconnexion, vous pouvez consulter notre guide sur la configuration d’un Mac distant pour les tâches persistantes. Si vous exploitez un agent auto-hébergé, notre page consacrée aux cas d’usage du Mac distant aide à comparer une machine partagée avec un environnement réservé. Pour les contraintes de déploiement et de conservation des artefacts, les informations de KVMFLUX en français donnent le contexte du service sans remplacer votre propre test de récupération.

Un Mac déjà utilisé par plusieurs développeurs peut rester adapté si vous maîtrisez les processus, les chemins et les annulations. En revanche, un poste partagé devient un mauvais choix lorsque les tâches ne peuvent pas être isolées, que la coupure SSH laisse régulièrement des processus orphelins ou que l’hôte n’est pas disponible en continu. Dans ce cas, un environnement distant KVMFLUX peut être plus simple à évaluer qu’un achat immédiat : vous pouvez tester un espace de construction persistant, contrôler les accès et vérifier la reprise après interruption avant de décider d’un engagement plus long. Pour une équipe qui veut conserver une machine dédiée, un achat local peut toutefois rester préférable si la charge est stable, si des interfaces physiques sont nécessaires ou si la conservation matérielle à long terme prime sur la flexibilité.

Commencez par exécuter le test final avec un Build parallèle, une déconnexion SSH et un redémarrage contrôlé. Si votre Mac actuel échoue sur l’isolation des répertoires ou la maîtrise des processus, comparez alors les conditions d’un environnement Mac distant à louer avec votre organisation actuelle, en vérifiant surtout la persistance, les droits de contrôle et la récupération des archives.

Pour aller plus loin

Stabilisez vos compilations Xcode à distance avec KVMFLUX

Louez un Mac distant KVMFLUX pour vos projets iOS et macOS et gardez un environnement de compilation maîtrisé. Accédez à une infrastructure Mac à distance pour exécuter vos builds, vos tests et vos Archives avec davantage de fiabilité. Choisissez les ressources adaptées à vos besoins afin de limiter les conflits de processus et les verrous de base de données pendant la compilation. Confiez vos compilations à distance à KVMFLUX et avancez sereinement dans vos flux de développement et d’intégration continue.

Mac Mini M4 · 16GB / 256GB
Jour$19.3 /jour
Semaine$52.2 /sem.
Mois$96.7 /mois
Trimestre$263 /trim.