Un projet Unity peut parfaitement exporter un projet iOS en local, puis se bloquer sur un Mac cloud lors de l’importation des ressources, de la résolution des dépendances natives ou de l’édition de liens. Le principal problème n’est pas l’échec lui-même, mais le fait que toutes les étapes soient regroupées dans une seule commande : le journal indique seulement que la compilation a échoué, sans permettre de savoir s’il faut relancer Unity, vider le cache ou inspecter le projet Xcode. Une approche plus fiable consiste à scinder le pipeline en deux contrôles indépendants : générer d’abord un projet Xcode vérifiable, puis valider la compilation native sans effectuer de signature.
Définir les limites des entrées et des artefacts
Pour qu’un build soit reproductible, il faut au minimum figer quatre catégories d’entrées : la révision du dépôt, la version de l’éditeur Unity, le fichier de verrouillage des dépendances de paquets et la liste des scènes. Le script ne doit pas lire à la volée les paramètres de l’éditeur présents sur la machine d’un développeur, ni écraser le projet Xcode exporté lors de l’exécution précédente avant de poursuivre la compilation.
Il est recommandé d’utiliser un répertoire distinct pour chaque tâche :
| Chemin | Rôle | Mise en cache |
|---|---|---|
Source/ |
Projet Unity de la révision courante | Non |
Library/ |
Résultat de l’importation des ressources | Sous conditions |
Build/iOS/ |
Projet Xcode exporté pour cette exécution | Non |
.build/DerivedData/ |
Fichiers intermédiaires de la compilation native | Sous conditions |
Artifacts/ |
Journaux, résumés et résultats de validation | Non |
La clé de cache de Library doit inclure la version de Unity, la plateforme cible, ainsi que les empreintes de Packages/manifest.json et Packages/packages-lock.json. Toute modification de l’un de ces éléments doit déclencher une nouvelle importation. De même, DerivedData ne doit pas être réutilisé directement entre différentes versions de Xcode ou différentes configurations de projet.
Un cache ne peut accélérer que des entrées préalablement figées ; il ne remplace pas le verrouillage des versions. Un cache utilisé malgré des limites mal définies est généralement plus difficile à diagnostiquer qu’une reconstruction complète.
Exporter le projet Xcode en mode batch
Placez le point d’entrée de l’export dans Assets/Editor/IosExport.cs. La liste des scènes doit être générée à partir des éléments activés dans les paramètres de build, afin d’éviter de maintenir deux listes distinctes dans le script et dans l’interface de l’éditeur.
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.Build.Reporting;
public static class IosExport
{
public static void Run()
{
var scenes = EditorBuildSettings.scenes
.Where(scene => scene.enabled)
.Select(scene => scene.path)
.ToArray();
if (scenes.Length == 0)
throw new InvalidOperationException("No enabled scenes.");
var options = new BuildPlayerOptions
{
scenes = scenes,
locationPathName = "Build/iOS",
target = BuildTarget.iOS,
options = BuildOptions.CleanBuildCache
};
var report = BuildPipeline.BuildPlayer(options);
if (report.summary.result != BuildResult.Succeeded)
throw new InvalidOperationException("Unity export failed.");
}
}
Lors de l’exécution, indiquez explicitement le répertoire du projet, le chemin du journal et le nom de la méthode :
"$UNITY_EDITOR" \
-batchmode \
-nographics \
-quit \
-projectPath "$PWD" \
-executeMethod IosExport.Run \
-logFile "$PWD/Artifacts/unity-export.log"
Supprimez Build/iOS avant le démarrage de la tâche, mais ne supprimez pas systématiquement Library. À la fin, vérifiez simultanément le code de sortie, le fichier journal et la présence de Build/iOS/Unity-iPhone.xcodeproj. La seule présence du répertoire ne garantit pas la réussite de l’export : une tâche en échec peut également laisser un résultat partiel.
Séparer les dépendances natives de la validation de compilation
La réussite de l’export Unity indique seulement que le générateur a terminé son travail. Elle ne garantit pas que le code Objective-C et Swift, les artefacts IL2CPP et les bibliothèques natives puissent être compilés ensemble. Exécutez d’abord l’étape d’installation des dépendances natives prévue par le projet, puis déterminez s’il faut compiler le workspace ou le project. Lorsqu’un outil de dépendances a généré un fichier .xcworkspace, continuer à compiler le fichier .xcodeproj provoque souvent des erreurs de modules introuvables ou de bibliothèques manquantes lors de l’édition de liens.
set -euo pipefail
cd Build/iOS
rm -rf ../../.build/DerivedData
if [ -d "Unity-iPhone.xcworkspace" ]; then
xcodebuild \
-workspace Unity-iPhone.xcworkspace \
-scheme Unity-iPhone \
-configuration Release \
-sdk iphoneos \
-derivedDataPath ../../.build/DerivedData \
CODE_SIGNING_ALLOWED=NO \
build
else
xcodebuild \
-project Unity-iPhone.xcodeproj \
-scheme Unity-iPhone \
-configuration Release \
-sdk iphoneos \
-derivedDataPath ../../.build/DerivedData \
CODE_SIGNING_ALLOWED=NO \
build
fi
L’objectif est ici de valider la compilation et l’édition de liens, et non de produire un paquet installable destiné à la distribution. La signature doit être réservée à une étape ultérieure et contrôlée, afin que les tâches ordinaires de validation des commits n’accèdent pas inutilement à des éléments sensibles.
Prévoir l’invalidation du cache plutôt que la deviner
Dans les projets Unity, une erreur fréquente consiste à utiliser uniquement le nom de la branche comme clé de cache. Deux commits situés sur la même branche ne produisent pas nécessairement les mêmes résultats d’importation. Commencez par calculer une empreinte des entrées :
{
printf '%s
' "$UNITY_VERSION"
printf '%s
' "$XCODE_VERSION"
shasum -a 256 Packages/manifest.json
shasum -a 256 Packages/packages-lock.json
find Assets -name '*.asmdef' -o -name '*.rsp' | sort | xargs shasum -a 256
} | shasum -a 256 | awk '{print $1}' > Artifacts/cache-key.txt
Ne pas mettre en cache l’intégralité du répertoire de travail
Mettre en cache une archive de l’ensemble du projet y mélange d’anciens exports, des paramètres temporaires et des fichiers qui ont depuis été supprimés. Library et DerivedData doivent être gérés séparément, et l’empreinte des entrées doit toujours être vérifiée après restauration. En cas d’écarts de compilation inexpliqués, conservez d’abord les journaux de l’échec, puis relancez une fois avec un cache vide. Si cette exécution réussit, le problème doit être classé comme une erreur de délimitation du cache, et non conduire directement à modifier le code métier.
Archiver le minimum de preuves nécessaire à l’analyse
Après un build réussi, ne conservez pas uniquement un marqueur de succès. Archivez au minimum le journal Unity complet, la sortie de Xcode, le hash du commit, les versions des outils, la clé de cache et un résumé du projet exporté. Ce résumé peut contenir le scheme, la configuration et les paramètres de build, mais doit exclure les identifiants, les jetons et les chemins privés propres à la machine.
Les échecs courants peuvent être rapidement orientés selon l’étape concernée :
- Le journal Unity n’atteint pas
IosExport.Run: vérifiez le nom de la méthode, les erreurs de compilation des scripts et la version de l’éditeur. - L’étape d’export indique que la liste des scènes est vide : vérifiez les scènes activées dans Build Settings au lieu de coder temporairement leurs chemins en dur.
- Le workspace existe, mais le script compile le project : ajustez le moment de la détection et assurez-vous que l’installation des dépendances est terminée.
- Échec de l’édition de liens pour IL2CPP ou une bibliothèque native : vérifiez la plateforme cible, l’architecture des plug-ins et les paramètres d’importation conditionnelle.
- Le même commit ne réussit que par intermittence : désactivez le cache, relancez la tâche, puis comparez les clés de cache et les versions des outils des deux exécutions.
- Le build réussit en local mais échoue à distance : comparez la casse des chemins, les fichiers non commités et les variables d’environnement avant de mettre en cause les performances de la machine.
Enfin, définissez « export réussi » et « compilation native réussie » comme deux états indépendants. Si le premier échoue, il est inutile d’exécuter Xcode ; si le second échoue, il n’est pas nécessaire de réimporter sans cesse toutes les ressources. Le pipeline devient ainsi plus facile à relancer, ses journaux sont plus courts et les responsabilités de chaque étape sont mieux délimitées.
Questions fréquentes
Pourquoi ne pas produire directement le paquet final depuis Unity ?
La séparation distingue les erreurs de scripts et d’import Unity des problèmes de dépendances, de compilation et d’édition de liens. Une compilation sans signature valide aussi le projet avant l’utilisation d’identifiants contrôlés.
Peut-on partager le dossier Library entre plusieurs projets ?
Ce n’est pas recommandé. La clé de cache doit inclure la version de Unity, les fichiers de verrouillage des dépendances et la plateforme cible ; toute différence impose de régénérer Library.
Quand faut-il compiler le fichier xcworkspace ?
Utilisez xcworkspace lorsque la préparation des dépendances natives en crée un. En son absence seulement, utilisez xcodeproj, avec une détection exécutée après l’installation des dépendances.
Placez votre prochaine compilation iOS sur MiniDebug M4.
M4, 16 Go de RAM et SSD de 256 Go inclus. Location à la journée, à la semaine, au mois ou au trimestre, avec cinq nœuds au choix : Singapour, Tokyo, Séoul, Hong Kong et côte Est des États-Unis. La disponibilité réelle est indiquée en temps réel dans la console.