← Tous les articles

Xcode 27 supprime ld64 et exige des noms de modules uniques

Apple a mis un éditeur de liens à la retraite en une seule phrase : « The ld64 linker has been removed and the -ld_classic option is no longer supported. »1 Le drapeau ainsi disparu est celui-là même qu’Apple demandait aux développeurs d’ajouter. Les notes de version de Xcode 15 proposaient -Wl,-ld_classic comme contournement de deux bugs de l’éditeur de liens.3

En bref

  • Xcode 27 supprime ld64 et n’accepte plus -ld_classic.1 Les notes de Xcode 15 prescrivaient ce drapeau pour des plantages liés aux symboles faibles et des bugs d’import LTO, Xcode 16 l’a déprécié, et Xcode 26 n’en a rien dit du tout.3410
  • Une seconde rupture se loge dans le compilateur Swift. Apple a fait de l’analyse des dépendances une action partagée unique : désormais, « every Clang module reachable from a single Swift dependency-scan action must have a unique module name ».2 Apple prend deux fois ses précautions : l’analyse « may report an error », et auparavant l’analyseur « may have tolerated duplicating names ».2
  • Les deux se déclenchent à la mise à jour de la chaîne d’outils, et non par un choix de cible de déploiement ou de SDK, et ni l’une ni l’autre n’a de composante à l’exécution. La population exposée : binaires fournis par un tiers, CocoaPods, ou une grosse base de code mixte Swift/Objective-C/C++.
  • Le « may » d’Apple ne relève pas de la prudence rédactionnelle. Sous Xcode 26.6, j’ai placé deux module maps déclarant un même nom sur un unique chemin de recherche et lancé l’analyseur 20 fois : neuf exécutions ont planté avec un SIGSEGV, cinq ont avorté, et six se sont terminées proprement.8
  • Un module map fourni par un tiers qui redéclare un module du SDK — le cas qu’Apple nomme — compile aujourd’hui en silence et masque le vrai module. Mon shim SQLite3 tiers a passé la vérification de types avec un code de sortie 0, et sqlite3_open a cessé d’exister.8
  • Sur les sept projets audités : zéro -ld_classic, OTHER_LDFLAGS non défini partout, et zéro nom de module en double parmi 391 module maps, car aucun dépôt n’en contient un écrit à la main.9

Ces deux notes figurent dans les notes de version de la bêta 4 de Xcode 27, aux côtés de Swift 6.4 et des SDK 27.11 À lire comme un texte de bêta.

Le drapeau qu’Apple vous avait dit d’ajouter

L’histoire commence par une réécriture de l’éditeur de liens. Xcode 15 annonçait un nouvel éditeur de liens, en faisait le choix par défaut « for all macOS, iOS, tvOS and visionOS binaries », et fixait les conditions de la mise à la retraite en une proposition : « The classic linker can still be explicitly requested using -ld64, and will be removed in a future release. »3

Le nouvel éditeur de liens est arrivé avec des bugs, et Apple a documenté deux fois la porte de sortie sur la même page. Premier problème connu, traduit de l’anglais (texte original en note) : « Les binaires qui utilisent des symboles à définition faible plantent à l’exécution sur iOS 14/macOS 12 ou antérieurs. Cela touche avant tout les projets C++, en raison de leur usage massif des symboles faibles. » Le contournement proposé par Apple consistait à relever la cible de déploiement ou à « ajouter -Wl,-ld_classic au réglage de compilation OTHER_LDFLAGS ».3 Le second, concernant des fichiers objets LTO qui liaient des imports de symboles faibles comme non faibles, offrait « -Wl,-weak_reference_mismatches,weak or -Wl,-ld_classic » dans le même réglage.3

Les deux bugs frappaient le plus durement les projets C++, ce qui explique qui traîne encore ce drapeau. Personne ne revient sur un réglage qui a mis fin à un plantage.

Xcode 16 a prévenu en une phrase : « -ld_classic linker option is deprecated and will be removed in a future release. »4 Puis plus rien. J’ai cherché ld_classic, ld64 et l’éditeur de liens classique dans les notes de version de Xcode 26 : aucune occurrence.10

La chaîne d’outils actuelle accepte toujours le drapeau et s’en plaint toujours. Sous Xcode 26.6, j’ai lié un programme C trivial avec :7

xcrun clang hello.c -o hello -Wl,-ld_classic
ld: warning: -ld_classic is deprecated and will be removed in a future release

Code de sortie zéro. Le binaire se lie. En substituant -Wl,-ld64, on obtient l’avertissement identique nommant -ld_classic : les deux graphies employées par Apple mènent aujourd’hui au même chemin de code.7 La note de Xcode 27 ne nomme que -ld_classic, et je n’ai pas pu tester si -ld64 échoue de la même manière.

Un détail complique le mot « supprimé ». L’éditeur de liens de Xcode 26.6, sommé de s’identifier avec xcrun ld -v, indique les architectures qu’il délègue :7

@(#)PROGRAM:ld PROJECT:ld-1267
BUILD 16:38:58 Jun  8 2026
configured to support archs: armv6 armv7 armv7s arm64 arm64e arm64_32 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em armv8m.main armv8.1m.main
will use ld-classic for: armv6 armv7 armv7s i386 armv6m armv7k armv7m armv7em

ld-classic existe comme véritable binaire dans la chaîne d’outils, avec sa propre page de manuel, et l’éditeur de liens y achemine encore huit architectures.7 Aucune ne survit dans un SDK applicatif actuel : iPhoneOS 26.5 ne déclare que arm64e et arm64, et le SDK watchOS ajoute arm64_32.7 La liste se résume à l’ARM 32 bits, i386 et des cibles embarquées Cortex-M. Lire leur absence des SDK comme la raison pour laquelle cette suppression ne menace pas les développeurs d’applications relève de ma déduction, pas d’une affirmation d’Apple.

Trouver le drapeau sans se fier à grep

-ld_classic vit dans OTHER_LDFLAGS : demandez donc au système de compilation ce à quoi ce réglage se résout, plutôt que de chercher du texte dans des fichiers :

xcodebuild -showBuildSettings \
  -project YourApp.xcodeproj \
  -configuration Release -sdk iphoneos 2>/dev/null \
  | grep -E "OTHER_LDFLAGS|SUPPORTED_PLATFORMS"

Sur le projet Reps, une seule ligne revient :9

    SUPPORTED_PLATFORMS = appletvos appletvsimulator iphoneos iphonesimulator macosx

OTHER_LDFLAGS n’apparaît pas du tout, et c’est bien la réponse : le réglage n’est pas défini, donc aucun drapeau d’édition de liens ne parvient de là à l’étape de liaison. Une absence dans les réglages de compilation résolus porte une information qu’une absence dans une recherche textuelle n’a pas. Lisez aussi SUPPORTED_PLATFORMS pour la question des plateformes, jamais un *_DEPLOYMENT_TARGET, que Xcode écrit qu’une destination soit réelle ou non.

Trois pièges ont produit de faux résultats propres pendant l’audit, et tous trois n’affichent rien tout en sortant avec succès. timeout n’existe pas sur un macOS d’origine : y encapsuler xcodebuild renvoie un code 127 et une sortie vide qui se lit comme un projet sans aucun drapeau d’édition de liens. Sous zsh, un --include=*.pbxproj non protégé par des guillemets subit l’expansion glob avant que grep ne le voie, et la commande meurt sur un « no matches found » au lieu de signaler zéro correspondance. Et find ne suit pas un lien symbolique donné comme point de départ, ce qui compte puisque xcrun --sdk iphoneos --show-sdk-path en renvoie précisément un : find "$SDK" -name '*.modulemap' ne trouve rien, tandis que find -H "$SDK" trouve 266 fichiers.9 Protégez vos motifs par des guillemets, passez -H, et vérifiez que la commande trouve bien quelque chose dont vous connaissez l’existence avant de faire confiance à un zéro.

Une cible maintenue à la main garde le drapeau dans project.pbxproj ou dans un .xcconfig ; un projet CocoaPods peut aussi le recevoir d’un point d’accroche post_install, qu’aucun fichier édité par un développeur ne consigne. Le parc que j’ai audité n’a ni l’un ni l’autre, si bien que cette dernière voie est restée non vérifiée.9

La règle des noms de modules, et les deux précautions d’Apple

La seconde rupture arrive déguisée en gain de performance, classée sous les nouveautés plutôt que sous les dépréciations. L’entrée d’Apple sur le compilateur Swift, dans son intégralité, traduite de l’anglais ; l’original est reproduit en note :

L’analyseur de dépendances de Swift a été optimisé pour éviter le travail de préparation et les recherches d’en-têtes redondants lors de la résolution des modules Clang au cours d’une même action d’analyse des dépendances, ce qui améliore sensiblement les performances de l’analyse. En conséquence de ce changement, chaque module Clang atteignable depuis une même action d’analyse des dépendances Swift doit porter un nom de module unique. Si deux module maps visibles par la même analyse déclarent un module Clang portant le même nom, l’analyse peut signaler une erreur. Auparavant, l’outil d’analyse pouvait tolérer des noms en double. Les cas les plus courants sont les projets ou SDK qui fournissent un même nom de module Clang depuis plusieurs emplacements du chemin de recherche d’en-têtes, et les sources tierces qui livrent un module.modulemap redéclarant un module du SDK.2

Quatre éléments méritent d’y être distingués. Apple limite l’exigence à ce qu’une seule analyse peut atteindre, pas à votre disque entier. Apple formule la conséquence au conditionnel — « may report an error » dans le texte original —, et prend la même précaution sur l’ancien comportement. Apple nomme les deux configurations déclenchantes : un nom de module fourni depuis deux endroits du chemin de recherche d’en-têtes, et un module.modulemap tiers qui redéclare un module du SDK. Et le déclencheur est la chaîne d’outils, puisque rien ne renvoie à une cible de déploiement ni à une version de SDK.

La règle est antérieure à l’optimisation. Le langage des module maps de Clang l’énonce sans ambages : « Each module shall have a single definition. »6 Ce que la documentation n’énonce jamais, c’est la conséquence d’une entorse à la règle, et ce silence s’avère bien mérité : la chaîne d’outils ne répond pas par un comportement, mais par plusieurs.

Ce que produit réellement une collision

Les précautions d’Apple m’ont donné envie de voir l’échec, alors j’en ai construit la version minimale : deux répertoires, chacun avec un module.modulemap déclarant le même module.8

A/module.modulemap    B/module.modulemap
module Widget {       module Widget {
    header "widget.h"     header "widget.h"
    export *              export *
}                     }

Avec les deux répertoires sur le chemin de recherche, une simple vérification de types échoue toujours de la même façon, 20 fois sur 20 :8

B/module.modulemap:1:8: error: redefinition of module 'Widget'
1 | module Widget {
  |        `- error: redefinition of module 'Widget'
A/module.modulemap:1:8: note: previously defined here

redefinition of module est la chaîne à chercher dans un journal de compilation, et le clang de Xcode 26.6 l’émet déjà. Retirer l’un des chemins de recherche fait réussir la même compilation, ce qui confirme que le déclencheur est la visibilité au sein d’une même compilation, et non la simple présence sur le disque.8

L’analyseur de dépendances, lui, se comporte autrement, et c’est toute la raison d’être du « may » d’Apple. En passant les deux mêmes module maps par swiftc -scan-dependencies 20 fois, j’ai obtenu trois résultats distincts : neuf plantages avec SIGSEGV, cinq abandons, et six réussites propres.8 Les traces de pile des exécutions plantées passent par performParallelClangModuleLookup, ce qui cadre avec une situation de compétition dans la recherche parallèle qu’Apple dit remplacer. Attribuer le non-déterminisme à cette situation de compétition est ma lecture de la trace, pas une déclaration d’Apple.

Le second cas nommé par Apple est le cas silencieux. J’ai écrit un module map tiers déclarant SQLite3, un module bien réel du SDK iPhoneOS, je l’ai placé sur le chemin de recherche, et j’ai compilé un fichier qui s’en sert :8

Vendor/module.modulemap
module SQLite3 {
    header "shim.h"
    export *
}

La compilation a réussi. Code de sortie 0, aucun avertissement, aucune note, aucun diagnostic d’aucune sorte. J’ai ensuite demandé sqlite3_open à la même configuration :8

error: cannot find 'sqlite3_open' in scope

Sans le répertoire tiers sur le chemin de recherche, le fichier identique compile. Le module map tiers a complètement occulté le SQLite3 du SDK, et la chaîne d’outils n’a rien dit. Aujourd’hui, donc, le cas de la redéclaration par un tiers n’échoue pas bruyamment : il échoue sous la forme d’une API manquante dans un module que vous pensiez avoir importé. La note d’Apple annonce que l’analyse de Xcode 27 « may report an error » à cet endroit, ce qui transformerait une occultation silencieuse en échec de compilation.

Je compile sous Xcode 26.6 (build 17F113) : tous les diagnostics ci-dessus proviennent donc de la chaîne d’outils précédente.78 Je ne peux pas rapporter le texte d’erreur de Xcode 27, et je n’en ai inventé aucun. Le mécanisme de diagnostic, la chaîne à rechercher et la tolérance qu’Apple annonce retirer existent tous dès aujourd’hui.

Auditer les noms de modules en double

Recenser les noms de modules déclarés ressemble à un grep d’une ligne, mais deux constructions du langage des module maps produisent de mauvaises réponses.

extern module Foo "Foo.modulemap" est une référence anticipée, pas une définition, et le SDK d’Apple en emploie 79 dans un seul module map.7 Un balayage naïf compte la référence et la vraie définition comme deux déclarations de Foo. Ensuite, module Darwin.C { ... } utilise un identifiant de module pointé pour étendre un module déclaré ailleurs ; 13 module maps du SDK ouvrent un module Darwin.quelquechose, et lire le premier composant comme une déclaration de premier niveau amalgame ces 13 fichiers avec la vraie déclaration Darwin en une collision unique sur 14 fichiers.7 Ces deux faux positifs sont apparus dans mon premier jet.

Ce qui survit gère les commentaires, la profondeur d’accolades — pour que les sous-modules explicit module imbriqués restent en dehors — et les chemins comportant des espaces :

find -H "${1:-.}" \( -name '*.modulemap' -o -name 'module.map' \) \
  -not -path '*/build/*' -not -path '*/.build/*' \
  -not -path '*/DerivedData*/*' -print0 |
while IFS= read -r -d '' map; do
  awk -v f="$map" '
    { line = $0; sub(/\/\/.*/, "", line)
      if (depth == 0 && line !~ /extern[[:space:]]+module/ &&
          match(line, /(^|[[:space:]])module[[:space:]]+[A-Za-z_][A-Za-z0-9_.]*/)) {
        n = substr(line, RSTART, RLENGTH); sub(/.*module[[:space:]]+/, "", n)
        if (n !~ /\./) print n "\t" f
      }
      for (i = 1; i <= length(line); i++) {
        c = substr(line, i, 1)
        if (c == "{") depth++; else if (c == "}") depth--
      }
    }' "$map"
done | sort -u > /tmp/modnames.txt

cut -f1 /tmp/modnames.txt | uniq -d | while read -r n; do
  echo "duplicate: $n"
  awk -F'\t' -v n="$n" '$1==n {print "  " $2}' /tmp/modnames.txt
done

Le meilleur témoin négatif disponible est le SDK d’Apple lui-même, qui doit satisfaire la règle pour que la chaîne d’outils fonctionne, tout simplement. Pointée sur le SDK iPhoneOS 26.5, la commande trouve 1 099 déclarations de premier niveau sous usr/include et 205 dans les module maps de frameworks, sans aucun doublon ni d’un côté ni de l’autre.7 Pointée sur des jeux d’essai construits pour entrer en collision, elle nomme le doublon et les deux fichiers.8

Les exclusions pèsent lourd. -not -path '*/build/*' n’exclut pas .build/, car le motif exige le nom littéral du répertoire, et SwiftPM y écrit des module maps générés par dizaines. Laisser .build dans le périmètre a produit mes seuls « doublons » sur sept projets : GrappleCore dans six fichiers et GrappleRender dans trois, tous issus de SwiftPM, couvrant deux cibles Swift réparties sur différentes racines de compilation.9 Ces neuf fichiers ne sont même pas identiques octet pour octet, et la raison est instructive : chacun enveloppe le même nom de module autour d’un chemin absolu vers l’en-tête généré de sa propre racine de compilation, si bien qu’ils diffèrent par une chaîne de caractères et par rien d’important. Un audit qui les qualifie de collisions épuise sa crédibilité avant d’atteindre quoi que ce soit de réel.

Xcode fabrique le même faux positif avec moins d’ambiguïté. À l’intérieur d’un même arbre DerivedData, il écrit deux fois le module map généré de chaque paquet Swift, dans GeneratedModuleMaps-iphonesimulator/ et dans les intermédiaires de la cible elle-même, avec un chemin d’en-tête relatif les deux fois. Dans l’arbre d’un projet, 15 noms apparaissent chacun deux fois, et les 15 paires sont identiques octet pour octet.9 Cantonnez l’audit aux sources, pas aux produits de compilation.

Ce que contiennent sept projets

J’ai passé les deux audits sur sept projets Xcode sous Xcode 26.6. Le résultat honnête est un zéro net.9

Projet Fichiers Swift Objective-C / C++ / C Dépendances -ld_classic Module maps sources
Reps 77 0 2 SPM locaux 0 0
Return 57 0 aucune 0 0
Banana List 55 0 aucune 0 0
Ace Citizenship 26 0 aucune externe 0 0
Water 34 0 (2 .metal) aucune 0 0
ResumeGeni 71 0 3 SPM distants, 9 versions figées 0 0
Yawara 143 0 2 SPM locaux 0 0
Total 463 0 SPM uniquement 0 0

OTHER_LDFLAGS n’est défini dans aucun des sept projets, aucun fichier .xcconfig n’existe nulle part dans le parc, et il n’y a ni CocoaPods, ni Carthage, ni .framework ou .xcframework fourni par un tiers.9 L’audit des noms de modules a examiné 391 module maps, 204 dans les répertoires de projets et 187 dans le DerivedData partagé où atterrissent les compilations de l’interface graphique de Xcode, et tous sont des produits de compilation.9 Pas un seul dépôt ne contient de module map écrit à la main.

Les deux zéros ont la même cause, et c’est le constat qu’il faut emporter : la population exposée, ce sont les projets qui mêlent plusieurs langages, et ceux-ci n’en sont pas. Sur 463 fichiers Swift, le parc ne compte aucune source Objective-C, aucune C++ et aucune C, les deux shaders Metal de Water constituant le seul code compilé qui ne soit pas du Swift.9 Un résultat nul issu d’une base de code qui n’a jamais été exposée est une preuve faible quant aux dangers, et une preuve forte quant à qui doit auditer.

Deux détails comptent davantage que les zéros. Les seuls module maps écrits à la main dans les paquets résolus appartiennent à swift-crypto, qui fournit quatre shims C nommés CCryptoBoringSSL, CCryptoBoringSSLShims, CXKCP et CXKCPShims, tous distincts.9 Et aucun nom déclaré par le parc n’apparaît où que ce soit dans l’espace de noms de modules Clang du SDK, vérifié face aux 1 318 noms de premier niveau que la commande trouve dans le SDK iPhoneOS 26.5.9 Même les noms génériques venus de Supabase passent à côté : le SDK déclare des modules Clang nommés Foundation, UIKit et SQLite3, et aucun nommé Crypto, Storage ou Auth.

Le plancher de déploiement C++ qui se dérobe

Un changement connexe frappe les mêmes projets C++ que ceux qui gardent -ld_classic. Apple a relevé un plancher, en ne nommant que macOS et aucune autre plateforme : « The minimum supported deployment target on macOS for the C++ standard library has been increased to 11.0. »5

La même entrée offre une porte de sortie et en date la fermeture dans la phrase suivante. libc++ a modifié le résultat de lower_bound et upper_bound sur std::map et std::set pour les comparateurs qui ne sont pas un ordre faible strict, et définir _LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND « will revert to the historical implementation of these operations ». Puis : « That escape hatch will be removed in an upcoming release (likely in the next release). »5 Un changement voisin fait que multimap::find et multiset::find ne renvoient plus nécessairement le premier élément égal, un comportement dont Apple note qu’il « was never guaranteed by the Standard » quoique libc++ l’ait toujours assuré, et il est livré sans aucune possibilité de désactivation.5 Traitez la macro comme un ticket de migration, pas comme un correctif.

FAQ

Pourquoi mon projet contient-il -ld_classic ?

Presque certainement parce que les notes de version de Xcode 15 le prescrivaient. Deux problèmes connus y recommandaient d’ajouter -Wl,-ld_classic à OTHER_LDFLAGS : l’un où des binaires utilisant des symboles à définition faible plantaient à l’exécution sur iOS 14 et macOS 12 ou antérieurs, dont Apple notait qu’il « impacts primarily C++ projects », et l’autre où des fichiers objets LTO liaient des imports de symboles faibles comme non faibles.3 Apple a déprécié l’option dans Xcode 16 et supprimé l’éditeur de liens qui la portait dans Xcode 27.14 Si le drapeau est là, vérifiez que le bug d’origine se reproduit encore avant de partir en quête d’un remplaçant.

Comment trouver des noms de modules Clang en double dans mon projet ?

Recensez les déclarations de modules de premier niveau dans chaque module map, cherchez un nom présent dans deux fichiers, et écartez les répertoires de compilation des résultats. Trois choses font que l’affaire dépasse le simple grep : extern module Foo "path" est une référence et non une définition, module Foo.Bar étend un module déclaré ailleurs au lieu de déclarer Foo, et les sous-modules explicit module imbriqués n’entrent pas en collision avec les noms de premier niveau.6 Excluez explicitement .build, build et DerivedData, puisque -not -path '*/build/*' rate .build et que SwiftPM comme Xcode dupliquent régulièrement les module maps générés.9

À quoi ressemble l’erreur sous Xcode 27 ?

Je ne peux pas le dire, et personne qui compile sous Xcode 26 ne le peut. Ma machine tourne sous Xcode 26.6 (build 17F113) : je ne rapporte donc aucune sortie de Xcode 27.78 Ce que Xcode 26.6 émet pour deux module maps déclarant un même nom, c’est error: redefinition of module 'Widget' accompagné d’un note: previously defined here, à chaque exécution d’une simple vérification de types.8 La formulation d’Apple pour Xcode 27 dit que l’analyse « may report an error » : cherchez donc redefinition of module dans les journaux, plutôt qu’une chaîne devinée par quelqu’un.

Un échec de nom de module unique dépend-il de ma cible de déploiement ?

Non. Apple formule l’exigence par rapport à une action unique d’analyse de dépendances Swift, et ne nomme dans cette entrée aucune version d’OS, aucun SDK ni aucune cible de déploiement.2 Le changement d’éditeur de liens se lit de la même manière : une suppression dans la chaîne d’outils.1 Les deux surviennent dès la première compilation sous Xcode 27, au même titre que la macro @State, et non comme les exigences déclenchées par le SDK du même cycle.

À retenir

Pour les développeurs iOS : - Interrogez OTHER_LDFLAGS via xcodebuild -showBuildSettings -configuration Release -sdk iphoneos au lieu de chercher -ld_classic avec grep. Une absence dans les réglages résolus signifie « non défini » ; une absence dans une recherche textuelle ne signifie rien.9 - Cherchez redefinition of module dans les journaux de compilation — le diagnostic que Xcode 26.6 émet déjà — et non un texte d’erreur inventé de Xcode 27.8

Pour les équipes avec des dépendances C ou C++ fournies par des tiers : - Auditez d’abord les fichiers module.modulemap tiers face à l’espace de noms du SDK : Apple nomme ce cas, et il échoue aujourd’hui en silence. Mon shim SQLite3 tiers a compilé avec un code de sortie 0 et fait disparaître sqlite3_open.8 - Cantonnez l’audit aux sources et excluez .build, build et DerivedData. Chaque doublon apparent parmi 391 module maps était un produit de compilation généré pour une seule cible Swift.9

Pour les responsables de publication : - Considérez les deux changements comme déclenchés par la chaîne d’outils et programmez-les pour la première compilation sous Xcode 27, pas pour la migration de SDK. Aucun des deux ne renvoie à une cible de déploiement ni n’a de composante à l’exécution.12 - Gardez les précautions d’Apple dans le ticket. L’analyse « may report an error », et 20 exécutions d’une même collision ont donné trois résultats différents : une compilation qui passe une fois ne prouve rien.28


Le cycle 27 échoue chaque fois à un endroit différent : la clé d’écran de lancement bloque une soumission, l’obligation du cycle de vie des scènes bloque un lancement, et la macro @State bloque une compilation au niveau du code source. La suppression de l’éditeur de liens et la règle des noms de modules, elles, bloquent une couche plus bas, dans les parties de la chaîne d’outils que personne ne configure exprès. Le sommaire complet de la série se trouve dans la série Écosystème Apple.

Références


  1. Apple, Xcode 27 Release Notes, section Linking, Deprecations in Xcode 27 Beta (radar 165165518). Source de la suppression, citée intégralement : « The ld64 linker has been removed and the -ld_classic option is no longer supported. » Vérifié dans le JSON de documentation d’Apple le 26 juillet 2026, puisque la page HTML affiche son contenu via JavaScript. Le titre de la page à cette date est « Xcode 27 Beta 4 Release Notes ». 

  2. Apple, Xcode 27 Release Notes, section Swift Compiler, New Features in Xcode 27 Beta (radar 136303612). Source de l’entrée sur l’analyseur de dépendances, traduite intégralement dans le corps de cet article, y compris les deux précautions et les deux cas nommés. Texte original, cité mot pour mot : « The Swift dependency scanner has been optimized to avoid redundant setup work and header searches when looking up Clang modules during a single dependency-scan action, substantially improving scanning performance. As a consequence of this change, every Clang module reachable from a single Swift dependency-scan action must have a unique module name. If two module maps visible to the same scan declare a Clang module with the same name, the scan may report an error. Previously, the scanner may have tolerated duplicating names. The most common cases are projects or SDKs that vend the same Clang module name from more than one location on the header search path, and vendored third-party sources that ship a module.modulemap redeclaring an SDK module. » Vérifié dans le JSON de documentation d’Apple le 26 juillet 2026. À noter : l’entrée est classée sous New Features, et non sous Deprecations ou Known Issues. 

  3. Apple, Xcode 15 Release Notes, section Linking. New Features (radar 108915312) est la source de « A new linker has been written to significantly speed up static linking. It’s the default for all macOS, iOS, tvOS and visionOS binaries and anyone using the ‘Mergeable Libraries’ feature. The classic linker can still be explicitly requested using -ld64, and will be removed in a future release. » Known Issues est la source des deux contournements recommandant le drapeau : radar 114813650 (FB13097713), « Binaries using symbols with a weak definition crash at runtime on iOS 14/macOS 12 or older. This impacts primarily C++ projects due to their extensive use of weak symbols. », avec pour contournement de relever la cible de déploiement « or add -Wl,-ld_classic to the OTHER_LDFLAGS build setting » ; et radar 115521975 (FB13171424), « Weak symbol imports are linked as non-weak imports, when used from LTO object files. », avec pour contournement « Add -Wl,-weak_reference_mismatches,weak or -Wl,-ld_classic options to the OTHER_LDFLAGS build setting. » Vérifié dans le JSON de documentation d’Apple le 26 juillet 2026. 

  4. Apple, Xcode 16 Release Notes, section Linking, Deprecations (radar 128502299) : « -ld_classic linker option is deprecated and will be removed in a future release. » Vérifié dans le JSON de documentation d’Apple le 26 juillet 2026. 

  5. Apple, Xcode 27 Release Notes, section C++ Standard Library, Deprecations in Xcode 27 Beta. Apple n’imprime qu’un seul radar pour tout le bloc, 178191050, à la suite de son dernier élément plutôt qu’en regard de chacun. Source de « The minimum supported deployment target on macOS for the C++ standard library has been increased to 11.0. », du changement sur multi{map,set}::find (« code relying on the first element being returned from find will be broken, and lower_bound or equal_range should be used instead »), ainsi que de la porte de sortie et de son échéance : « Since this may be tricky to work around in some cases, an escape hatch is provided in this release: defining _LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND will revert to the historical implementation of these operations. That escape hatch will be removed in an upcoming release (likely in the next release). » Vérifié dans le JSON de documentation d’Apple le 26 juillet 2026. 

  6. Équipe Clang, Clang Modules documentation, Module Map Language. Source de « Each module shall have a single definition. », de la règle sur l’identifiant de module et de la déclaration extern module, de « The explicit qualifier can only be applied to a submodule, i.e., a module that is nested within another module. », et de la découverte des module maps par le nom de fichier module.modulemap, module.map étant recherché par compatibilité. Cité comme la référence propre à la chaîne d’outils pour le langage des module maps, et non comme un document destiné aux développeurs Apple ; le clang d’Apple dérive de cette implémentation. 

  7. Tests de l’auteur sous macOS 26.5.2 (build 25F84) avec Xcode 26.6 (build 17F113), Apple clang 21.0.0, le 26 juillet 2026. Sorties de commandes reproduites mot pour mot. xcrun clang hello.c -o hello -Wl,-ld_classic affiche « ld: warning: -ld_classic is deprecated and will be removed in a future release » et sort avec le code 0 ; en substituant -Wl,-ld64, on obtient l’avertissement identique nommant -ld_classic. xcrun ld -v rapporte PROJECT:ld-1267 ainsi que les listes d’architectures citées plus haut. ld-classic est présent à /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld-classic, avec une page de manuel à ses côtés. Les architectures du SDK proviennent de SDKSettings.plist : iPhoneOS 26.5 déclare arm64e et arm64, WatchOS 26.5 déclare arm64, arm64e et arm64_32. Les décomptes de module maps du SDK (1 099 déclarations de premier niveau dans usr/include, 205 dans System/Library/Frameworks, zéro doublon des deux côtés) proviennent de l’exécution de la commande publiée ci-dessus sur le SDK iPhoneOS 26.5 ; les 79 déclarations extern module se trouvent dans usr/include/module.modulemap, et 13 module maps du même répertoire ouvrent un module Darwin.* pointé (bank, Darwin_C, Darwin_Mach, Darwin_Mach_machine, Darwin_machine, Darwin_POSIX, Darwin_sys, device, mach_debug, net, netinet, netinet6 et uuid), qu’une lecture par premier composant regroupe avec la vraie déclaration Darwin de Darwin.modulemap en une collision unique sur 14 fichiers. Le comportement sous Xcode 27 n’a pas été testé, Xcode 27 n’étant pas installé sur la machine utilisée. 

  8. Reproduction par l’auteur sur la même machine et la même chaîne d’outils, le 26 juillet 2026. Deux répertoires contenant chacun un module.modulemap déclarant module Widget, compilés avec les deux répertoires sur le chemin de recherche via swiftc. -typecheck a produit error: redefinition of module 'Widget' avec note: previously defined here sur 20 exécutions sur 20, code de sortie 1 ; retirer l’un des chemins -I a fait compiler le même fichier avec un code de sortie 0. -scan-dependencies sur la même entrée, sur 20 exécutions, a produit neuf sorties sur signal 11 (SIGSEGV), cinq sur signal 6 (SIGABRT) et six sorties avec le statut 0, les traces de pile des exécutions plantées passant par swift::ModuleDependencyScanner::performParallelClangModuleLookup. Séparément, un module map tiers déclarant SQLite3 (un module du SDK iPhoneOS 26.5) placé sur le chemin de recherche a passé la vérification de types avec un code de sortie 0 et aucun diagnostic, tandis qu’un fichier appelant le sqlite3_open du SDK sous la même configuration a échoué avec « error: cannot find ‘sqlite3_open’ in scope » et a compilé proprement une fois le répertoire tiers retiré du chemin de recherche. Les jeux d’essai comprenaient un chemin contenant un espace, afin de vérifier la commande publiée. Cette commande compare les noms entre fichiers : un nom déclaré deux fois à l’intérieur d’un même module map est donc hors de son périmètre par conception. Aucune sortie de Xcode 27 n’est rapportée où que ce soit dans cet article. 

  9. Audit par l’auteur de sept projets Xcode (Reps, Return, Banana List, Ace Citizenship, Water, ResumeGeni et Yawara) sous macOS 26.5.2 avec Xcode 26.6 (build 17F113), le 26 juillet 2026. -ld_classic et ld64 apparaissent zéro fois dans les arbres de travail, et OTHER_LDFLAGS n’est défini dans aucun projet, sans fichiers .xcconfig, sans Podfile, sans Carthage et sans .framework ou .xcframework fourni par un tiers nulle part dans le parc ; la recherche a couvert les arbres de travail actuels seulement, ni l’historique git ni les journaux de compilation archivés, si bien que le résultat est « absent aujourd’hui » et non « jamais utilisé ». La ligne SUPPORTED_PLATFORMS citée pour Reps provient de xcodebuild -showBuildSettings -configuration Release -sdk iphoneos. Totaux de module maps : 391 examinés, 204 dans les répertoires des sept projets et 187 dans les arbres propres à ces sept projets sous le ~/Library/Developer/Xcode/DerivedData partagé (le répertoire partagé complet en contient bien davantage, appartenant à d’autres projets), tous produits de compilation, avec zéro module map écrit à la main ou fourni par un tiers dans les sept dépôts. Les doublons apparents ont été vérifiés par empreinte de contenu, et les deux générateurs se comportent différemment. Les 15 paires de noms de Xcode dans l’arbre ResumeGeni audité (le build/DerivedData interne au projet), écrites une fois sous GeneratedModuleMaps-iphonesimulator/ et une fois sous les intermédiaires de la cible, sont identiques octet pour octet 15 fois sur 15, parce que Xcode émet un chemin d’en-tête relatif. Le décompte vaut par arbre et non par projet : l’arbre de la même application sous le ~/Library/Developer/Xcode/DerivedData partagé en contient 16, et sa variante Index.noindex 22. C’est le mécanisme qui se généralise, pas le nombre. Les copies de SwiftPM, elles, ne le sont pas : les six fichiers GrappleCore et les trois GrappleRender sous les répertoires .build de Yawara portent respectivement six et trois empreintes distinctes et pèsent de 171 à 185 octets, parce que chacun intègre un chemin absolu vers le -Swift.h généré dans sa propre racine de compilation et est identique par ailleurs. Aucun nom de module déclaré dans le parc n’apparaît parmi les 1 318 noms de modules Clang de premier niveau distincts que la commande publiée trouve lorsqu’on la pointe sur l’ensemble du SDK iPhoneOS 26.5, dont 1 099 sous usr/include et 205 sous System/Library/Frameworks ; l’intersection avec les noms déclarés par le parc est vide. La même analyse rapporte zéro doublon sur l’ensemble du SDK, ce qui est le témoin négatif qu’exige la règle. CryptoKit est absent de cette liste parce qu’il est livré comme framework purement Swift, sans module map Clang : l’absence de collision sur Crypto tient donc au fait que le SDK ne déclare aucun module Clang de ce nom, et non à ce que CryptoKit l’occuperait. swift-crypto 4.4.0 fournit les seuls module maps écrits à la main du graphe de dépendances résolu (CCryptoBoringSSL, CCryptoBoringSSLShims, CXKCP, CXKCPShims, une déclaration chacun). Les variantes SymbolKit outil-hôte et cible se trouvent dans le paquet local partagé 941Kit, et non dans les sept applications. Les décomptes de sources excluent les environnements virtuels Python, correction qui a eu son importance : un décompte naïf créditait Reps de fichiers C et d’en-têtes qui se sont tous avérés vivre dans .venv et site-packages et n’être compilés par aucune cible Xcode. Water n’a pas d’arbre de compilation local : son décompte nul de module maps reflète donc un projet non compilé plutôt qu’un graphe de compilation vérifié propre. Les trois pièges de faux résultat propre ont chacun été confirmés directement : timeout est absent d’un macOS d’origine et sort avec le code 127 ; zsh développe un --include=*.pbxproj non protégé par des guillemets et abandonne sur « no matches found » ; et xcrun --sdk iphoneos --show-sdk-path renvoie un lien symbolique, face auquel find sans -H rapporte zéro module map là où find -H en rapporte 266. 

  10. Apple, Xcode 26 Release Notes. Recherche de ld_classic, ld64 et de toute entrée décrivant l’éditeur de liens classique le 26 juillet 2026 ; aucune n’apparaît, si bien que l’avis de dépréciation d’Apple dans Xcode 16 et la suppression dans Xcode 27 n’ont aucune reformulation intermédiaire. 

  11. Apple, Xcode 27 Release Notes, Overview : « Xcode 27 beta 4 includes Swift 6.4 and SDKs for iOS 27, iPadOS 27, tvOS 27, watchOS 27, macOS 27, and visionOS 27. » Les mêmes notes consignent sous Intel Deprecation (radar 162138432) que « Xcode 27 will only install and run on Apple silicon Macs. » Vérifié le 26 juillet 2026. 

Articles connexes

La macro @State : ce que Xcode 27 refuse désormais de compiler

Xcode 27 réimplémente le @State de SwiftUI en macro Swift. La rupture arrive à la mise à jour de la chaîne d'outils, pas…

19 min de lecture

Xcode 27 abandonne Intel : ce qui s'arrête et ce qui continue de sortir

Xcode 27 exige Apple silicon, produit encore des applications Universal et retire x86_64 d'ARCHS_STANDARD dès que la cib…

24 min de lecture