Un modèle de classification d’images fonctionne correctement lors des tests locaux et l’archivage réussit, mais l’application livrée ne trouve pas la ressource lors de la première inférence. Le problème vient généralement non pas de l’algorithme, mais du fait que le modèle n’a pas été ajouté à la cible, qu’un script l’a copié dans le mauvais répertoire ou qu’une ancienne version portant le même nom subsiste dans le bundle de l’application. Sur un pipeline Mac cloud, il ne suffit donc pas de vérifier que le projet peut être construit : il faut aussi s’assurer que la source du modèle peut être compilée séparément et contrôler le contenu réel du bundle livré.
Séparer la validation du modèle en deux étapes
La première étape traite directement le fichier .mlmodel ou .mlpackage afin d’écarter les problèmes liés au format du modèle, à la chaîne d’outils ou au répertoire de sortie. La seconde inspecte le fichier .app après l’archivage Xcode pour confirmer que le répertoire .mlmodelc compilé a bien été intégré à la cible livrée. Ces deux étapes doivent rester distinctes : si l’on exécute uniquement xcodebuild, il est difficile, en cas d’échec, de déterminer rapidement si le problème vient de la compilation du modèle, de son appartenance à la cible ou de la phase de copie des ressources.
Avant de commencer, fixez le répertoire du développeur et consignez les versions des outils :
set -euo pipefail
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
xcodebuild -version
xcrun --find coremlcompiler
swift --version
Si plusieurs versions de Xcode sont installées sur ArmVMS, chaque tâche doit définir explicitement DEVELOPER_DIR au lieu de dépendre de l’état de xcode-select dans un terminal interactif. Les variables d’environnement du pipeline peuvent différer de celles d’une session ouverte manuellement ; un chemin explicite facilite donc la reproduction du build.
La validation doit porter sur « le bundle d’application précis généré avec la chaîne d’outils Xcode spécifiée », et non sur un répertoire de build ayant réussi une fois par hasard sur une machine donnée.
Compiler d’abord le modèle Core ML séparément
Préparez un répertoire de sortie distinct pour chaque modèle et interdisez à plusieurs tâches de partager le même répertoire temporaire. Le script suivant accepte aussi bien une entrée .mlmodel qu’une entrée .mlpackage :
MODEL_PATH="${1:?model path required}"
OUTPUT_ROOT="${2:-$PWD/build/coreml}"
MODEL_NAME="$(basename "$MODEL_PATH")"
MODEL_NAME="${MODEL_NAME%.*}"
MODEL_OUTPUT="$OUTPUT_ROOT/$MODEL_NAME"
rm -rf "$MODEL_OUTPUT"
mkdir -p "$MODEL_OUTPUT"
xcrun coremlcompiler compile "$MODEL_PATH" "$MODEL_OUTPUT"
find "$MODEL_OUTPUT" -type f -print0 |
sort -z |
xargs -0 shasum -a 256 > "$MODEL_OUTPUT.files.sha256"
du -sk "$MODEL_OUTPUT"
Après l’exécution de la commande, vérifiez au minimum trois points : le répertoire de sortie n’est pas vide, la liste des fichiers peut être générée et la taille n’est pas nulle. N’utilisez pas directement le répertoire compilé comme cache permanent, car son contenu dépend de la version de Xcode, du modèle et du comportement du compilateur. Si vous souhaitez le réutiliser, composez la clé de cache à partir de l’empreinte de la source du modèle, de la sortie de xcodebuild -version et de la version du script de build.
Ajouter des contrôles d’entrée pour les modèles générés
Dans certains projets, le modèle est généré avant le build par Python ou par un outil de conversion. Il faut alors valider les fichiers d’entrée avant d’appeler coremlcompiler. La tâche de génération doit écrire dans un nouveau répertoire, puis déplacer atomiquement le résultat vers le chemin convenu une fois toutes les opérations terminées. Xcode ne risque ainsi pas de lire un modèle incomplet.
Il est recommandé d’enregistrer les champs suivants dans le manifeste :
| Champ | Utilité |
|---|---|
| Chemin relatif du modèle | Distinguer les modèles utilisés par l’application principale de ceux des extensions |
| SHA-256 du fichier source | Déterminer si l’entrée a réellement changé |
| Version de Xcode | Expliquer les différences entre les artefacts compilés |
| Taille après compilation | Détecter les ressources ajoutées par inadvertance |
| Nom de la cible | Vérifier Target Membership |
Vérifier le produit final dans l’archive
Une fois la compilation séparée validée, créez une archive propre. Pour éviter de récupérer les résidus d’une tâche précédente, attribuez à DerivedData et au chemin d’archive des répertoires réservés à l’exécution en cours :
RUN_ROOT="$PWD/build/model-validation"
DERIVED_DATA="$RUN_ROOT/DerivedData"
ARCHIVE_PATH="$RUN_ROOT/App.xcarchive"
rm -rf "$RUN_ROOT"
mkdir -p "$RUN_ROOT"
xcodebuild \
-workspace Example.xcworkspace \
-scheme Example \
-configuration Release \
-destination "generic/platform=iOS" \
-derivedDataPath "$DERIVED_DATA" \
-archivePath "$ARCHIVE_PATH" \
archive
Localisez ensuite le bundle de l’application et exportez la liste des modèles :
APP_PATH="$(find "$ARCHIVE_PATH/Products/Applications" -maxdepth 1 -name '*.app' -print -quit)"
test -n "$APP_PATH"
find "$APP_PATH" -type d -name '*.mlmodelc' -print |
LC_ALL=C sort > "$RUN_ROOT/bundled-models.txt"
test -s "$RUN_ROOT/bundled-models.txt"
Ne limitez pas ici la recherche à la racine de l’application principale. Un modèle peut appartenir à une extension ou être fourni par un bundle de ressources. Le script de contrôle doit enregistrer le chemin relatif complet, puis vérifier, conformément aux règles du projet, quels emplacements sont autorisés. Si deux répertoires portant le même nom apparaissent alors qu’un seul modèle est attendu, examinez immédiatement si l’application principale, une extension et un bundle de dépendance en ont chacun copié un exemplaire.
Établir une référence sans se fier à des suppositions
La taille d’un modèle dépend de sa structure et des outils de compilation. Il n’est donc pas pertinent d’appliquer une règle universelle du type « échec au-delà d’un certain pourcentage d’augmentation ». Une méthode plus fiable consiste à conserver une référence validée pour chaque modèle et à définir à la fois une variation absolue et une variation proportionnelle. Pour les petits modèles, la variation absolue est plus pertinente ; pour les grands modèles, il faut davantage surveiller la proportion.
Le fichier de référence peut utiliser un format simple séparé par des tabulations :
Classifier.mlmodelc 8421376
Embedding.mlmodelc 27156480
La comparaison doit également gérer les ajouts, les suppressions et les renommages. Un nouveau modèle ne doit pas être considéré automatiquement comme une erreur, mais le journal des modifications doit obligatoirement préciser son rôle. La suppression d’un modèle doit bloquer le build, sauf si le manifeste a lui aussi été mis à jour et validé. Un renommage doit être traité comme la suppression de l’ancien élément suivie de l’ajout du nouveau, afin d’éviter d’hériter à tort des données historiques.
Le manifeste doit être conservé comme artefact de build, sans toutefois téléverser de configuration sensible autre que la source du modèle. Si le modèle provient d’une étape de téléchargement contrôlée, enregistrez uniquement son empreinte, son nom logique et son chemin final dans le bundle. N’affichez pas dans les journaux une URL de téléchargement contenant un jeton.
Échecs courants et contrôles avant livraison
Si le modèle peut être compilé séparément mais n’apparaît pas dans le bundle de l’application, vérifiez en priorité Target Membership, Copy Bundle Resources, les dépendances des bundles de ressources et les réglages de build conditionnels. Si le modèle est présent en Debug mais absent en Release, comparez les phases de ressources ainsi que les listes d’entrées et de sorties des scripts personnalisés entre les deux configurations.
Si plusieurs modèles portent le même nom, commencez par inventorier leurs chemins relatifs complets au lieu d’en supprimer immédiatement un. L’application principale et une extension peuvent réellement avoir besoin chacune de leur propre copie ; le problème est la duplication qui n’a pas été prévue. Si la taille de l’archive augmente soudainement, vérifiez également si le répertoire de génération a été ajouté en entier à la phase des ressources, ce qui intégrerait simultanément le modèle source, les fichiers temporaires et les artefacts compilés dans l’application.
Avant la livraison, effectuez les contrôles dans l’ordre suivant :
- Fixer
DEVELOPER_DIRet consigner la version de Xcode. - Exécuter
coremlcompilerséparément dans un répertoire isolé. - Générer un manifeste d’empreintes pour la source du modèle et le résultat compilé.
- Créer une archive Release à partir d’un DerivedData entièrement neuf.
- Énumérer tous les répertoires
.mlmodelccontenus dans le fichier.xcarchive. - Comparer les chemins autorisés, le nombre attendu et les références de taille validées.
- Conserver le manifeste, la sortie des commandes et l’identifiant de l’archive pour faciliter les comparaisons ultérieures.
Cette procédure n’évalue pas la précision du modèle et ne remplace pas les tests d’inférence de bout en bout. Elle traite un problème situé plus en amont et mieux adapté à l’automatisation : vérifier que le modèle peut être compilé, qu’il est correctement intégré au bundle et que le contenu livré n’a pas changé sans explication. Une fois ces résultats enregistrés dans un manifeste lisible par une machine, l’absence d’un modèle à l’exécution peut être détectée dès l’étape d’archivage.
Questions fréquentes
Pourquoi un build Xcode réussi ne suffit-il pas ?
Un build réussi confirme seulement que la chaîne n’a pas renvoyé d’erreur. Il ne prouve pas que chaque modèle attendu se trouve dans le bundle final ; il faut inspecter l’archive et compter les répertoires mlmodelc.
Quel seuil utiliser pour bloquer une hausse de taille du modèle ?
Conservez une référence approuvée pour chaque modèle, puis combinez une limite absolue et une limite proportionnelle. Un pourcentage unique appliqué à tous les modèles produit trop de faux résultats.
Deux modèles portant le même nom indiquent-ils toujours une erreur ?
Ils doivent au minimum déclencher une vérification. S’ils se trouvent dans le même bundle, examinez Copy Bundle Resources, les dépendances de paquets et les scripts susceptibles de copier deux fois le fichier.
Choisissez un nœud physique Apple Silicon dédié pour votre prochaine compilation
Louez ArmVMS M4 ou ArmVMS M4 Pro à la journée, à la semaine, au mois ou au trimestre. Vérifiez chaque configuration, nœud et option avant de créer votre commande.