Après plusieurs semaines d’exécution d’un vaste dépôt iOS sur un Mac dans le cloud, une branche peut soudainement provoquer l’erreur argument list too long dans un script, alors que les branches voisines continuent de produire leurs archives normalement. Ne commencez pas par vider DerivedData et n’attribuez pas non plus le problème à un dysfonctionnement intermittent de Xcode. Cette erreur vient généralement de E2BIG, renvoyée par execve lorsque les arguments de la commande, leurs caractères de terminaison et l’environnement hérité occupent ensemble tout l’espace accepté par le processus.
Déterminer d’abord à quel niveau l’échec se produit
La première étape consiste à conserver la commande complète et son état de sortie. Si le journal indique seulement qu’une phase de script a échoué, activez temporairement set -x dans le Run Script concerné afin de déterminer si l’échec vient de find, de rm, de l’outil d’archivage, du générateur de code ou du pilote du compilateur. N’affichez pas l’environnement complet dans un journal public, car il peut contenir des jetons. Consignez uniquement les noms des variables, leur taille en octets et une représentation expurgée de la commande.
Trois scénarios sont fréquents : un caractère générique développe en une seule fois des dizaines de milliers de chemins ; un script concatène tous les fichiers sources dans une variable ; la CI injecte un long bloc JSON, le contenu d’un certificat ou une configuration multiligne dans une variable d’environnement. Les chemins d’espace de travail trop longs et l’accumulation d’options -I, -F et -D réduisent également la marge disponible.
Le fait qu’une commande fonctionne sur une petite branche ne signifie pas que sa structure est correcte. Si elle échoue uniquement lorsque le nombre de fichiers augmente, le mécanisme de transmission des entrées n’a généralement pas été conçu avec une limite explicite.
Mesurer la marge disponible pour les arguments et l’environnement
Sous macOS, la limite utilisable ne se déduit pas de la seule longueur textuelle de la commande. Commencez par effectuer les mesures avec le même utilisateur et le même mode de lancement que la tâche de build :
getconf ARG_MAX
python3 - <<'PY'
import os
limit = os.sysconf("SC_ARG_MAX")
env_bytes = sum(len(k) + len(v) + 2 for k, v in os.environ.items())
largest = sorted(
((len(k) + len(v) + 2, k) for k, v in os.environ.items()),
reverse=True
)[:10]
print("arg_max", limit)
print("environment_bytes", env_bytes)
for size, key in largest:
print(size, key)
PY
Ces résultats servent de base de comparaison et ne doivent pas être considérés comme un budget entièrement disponible. Le système doit encore stocker les pointeurs, les caractères de terminaison et les données nécessaires au démarrage. En pratique, conservez une marge importante et surveillez toute hausse soudaine de la taille de l’environnement par rapport à sa valeur de référence historique.
Repérer les variables anormalement volumineuses
Examinez en priorité PATH, les chemins de recherche, les répertoires temporaires, les paramètres des gestionnaires de dépendances et les variables injectées par la CI. Si une configuration JSON atteint plusieurs dizaines de Ko, écrivez-la dans un fichier temporaire aux permissions contrôlées et ne transmettez au processus enfant que le chemin de ce fichier. Lorsque PATH est enrichi à plusieurs reprises, dédupliquez-le au lieu de le concaténer de nouveau à chaque étape.
Réduire l’environnement hérité sans casser la chaîne d’outils
Ne lancez pas un build Xcode complet dans un environnement vide. L’absence de HOME, de PATH, du répertoire temporaire ou du répertoire des outils de développement créerait de nouvelles pannes. Une approche plus sûre consiste à définir une liste d’autorisation pour un outil précis :
env -i \
HOME="$HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
TMPDIR="$TMPDIR" \
DEVELOPER_DIR="$DEVELOPER_DIR" \
/bin/zsh -lc 'xcrun --find xcodebuild'
Pour les tâches réelles, ajoutez également les paramètres régionaux, les répertoires de cache et la configuration du proxy requis par les dépendances. Transmettez les données sensibles au moyen de fichiers à courte durée de vie, puis supprimez-les après utilisation. Les indicateurs booléens ordinaires et les identifiants courts peuvent rester dans des variables d’environnement. Sur SetMini, les tâches automatisées doivent aussi centraliser l’initialisation de l’environnement dans un seul script d’entrée, afin d’éviter que le shell interactif, le runner de CI et les scripts Xcode ajoutent chacun les mêmes réglages.
Retirer les longues listes de fichiers de la ligne de commande
Utiliser un séparateur nul dans les scripts génériques
Les noms de fichiers pouvant contenir des espaces ou des sauts de ligne, il ne faut pas utiliser for f in $(find ...). Les outils prenant en charge le traitement par lots peuvent être associés à find -print0 et xargs -0 :
find "$PWD/Artifacts" -type f -name '*.dSYM' -print0 |
xargs -0 -n 50 /usr/bin/file
L’option -n 50 fixe une limite explicite pour chaque lot. Si l’outil sait lire une liste depuis l’entrée standard, privilégiez cette méthode afin d’éviter de relancer inutilement des processus.
Utiliser des listes de fichiers dans les phases Xcode
Les entrées et sorties d’un Run Script doivent être inscrites dans des fichiers .xcfilelist, puis configurées dans Input File Lists et Output File Lists. Xcode peut ainsi suivre les dépendances, sans que le script développe des milliers de chemins dans une seule commande. Lorsqu’un compilateur ou un éditeur de liens prend explicitement en charge les fichiers de réponse, placez-y les paramètres stables. Ne supposez pas que tous les outils tiers comprennent la syntaxe @file.
Pour les nombreuses options transmises par les réglages de build, placez les valeurs communes dans un fichier .xcconfig. Le script ne doit recevoir que quelques paramètres indispensables ; les ensembles de chemins passent par des listes de fichiers et les configurations structurées par des fichiers temporaires. La séparation de ces trois catégories d’entrées facilite également l’audit des journaux.
Mettre en place des seuils de croissance et des tests de régression
Après la correction, effectuez au minimum un build propre sur la branche contenant le plus de fichiers et les chemins les plus longs, puis un build incrémental. Vérifiez que le script gère correctement les noms de fichiers contenant des espaces, qu’un échec dans un lot renvoie un état non nul et que les fichiers temporaires sont supprimés sur tous les chemins de sortie.
Au début de la CI, vous pouvez consigner ARG_MAX, le nombre total d’octets de l’environnement et le nom des variables les plus volumineuses, mais jamais leurs valeurs. Définissez une valeur de référence commune pour la taille de l’environnement et émettez un avertissement lorsqu’un seuil est dépassé, au lieu d’attendre que le système refuse de créer le processus. Pour les ensembles de fichiers susceptibles de croître, enregistrez également leur nombre et la longueur du chemin le plus long.
L’objectif final n’est pas de maintenir de justesse la commande actuelle sous la limite, mais d’empêcher sa longueur d’augmenter linéairement avec la taille du dépôt. Lorsque l’environnement reste compact, que les ensembles de fichiers empruntent des canaux dédiés et que les lots ont une taille maximale fixe, E2BIG cesse d’être une panne intermittente pour devenir un problème de configuration détectable en amont.
Questions fréquentes
Pourquoi une commande identique échoue-t-elle seulement sur certaines branches ?
La limite couvre à la fois les arguments et l’environnement hérité. Une branche contenant plus de fichiers, des chemins plus longs ou davantage de variables peut dépasser la marge restante.
Faut-il choisir xargs ou un fichier de réponse ?
Utilisez xargs avec séparation NUL pour les outils exécutés par lots. Préférez un fichier de réponse lorsque le compilateur ou l’éditeur de liens le prend officiellement en charge.
Augmenter ARG_MAX est-il une correction durable ?
Non. Réduisez plutôt l’environnement, supprimez les options répétées et transmettez les longues collections par fichier, entrée standard ou lots bornés.
Intégrez vos étapes de développement dans un Mac cloud accessible à tout moment
Choisissez parmi deux configurations Apple Silicon et quatre centres de données. Vos ressources restent dédiées, sans partage avec d’autres clients. La disponibilité réelle est indiquée en temps réel dans la console.