Xcode 27 elimina ld64 y exige nombres de módulo únicos
Un target mantenido a mano guarda la opción en project.pbxproj o en un .xcconfig; un proyecto con CocoaPods también puede recibirla desde un bloque post_install, que no queda registrado en ningún archivo editado por quien desarrolla. El conjunto que audité no tiene ninguna de las dos cosas, así que esa última vía quedó sin verificar.9
Apple jubiló un enlazador en una sola frase: «El enlazador ld64 se ha eliminado y la opción -ld_classic ya no es compatible».1 La opción que ahora desaparece es la misma que Apple les dijo a los desarrolladores que agregaran. Las propias notas de versión de Xcode 15 ofrecían -Wl,-ld_classic como solución alterna para dos errores del enlazador.3
TL;DR
- Xcode 27 elimina ld64 y deja de aceptar
-ld_classic.1 Las notas de Xcode 15 recetaban esa opción para los fallos por símbolos débiles y los errores de importación con LTO, Xcode 16 la marcó como obsoleta y Xcode 26 no dijo absolutamente nada.3410 - La segunda ruptura está en el compilador de Swift. Apple convirtió el escaneo de dependencias en una única acción compartida, de modo que «cada módulo Clang alcanzable desde una sola acción de escaneo de dependencias de Swift debe tener un nombre de módulo único».2 Apple matiza dos veces: el escaneo «puede reportar un error» y antes el escáner «puede haber tolerado nombres duplicados».2
- Ambos se disparan al actualizar la cadena de herramientas, no por el objetivo de despliegue ni por la elección de SDK, y ninguno tiene componente en tiempo de ejecución. La población expuesta: binarios de terceros incorporados, CocoaPods o una base de código grande que mezcle Swift, Objective-C y C++.
- El «puede» de Apple no es cautela editorial. Con Xcode 26.6 puse dos mapas de módulos que declaran el mismo nombre en una única ruta de búsqueda y ejecuté el escáner 20 veces: nueve ejecuciones se cayeron con SIGSEGV, cinco abortaron y seis terminaron sin problemas.8
- Un mapa de módulos incorporado que vuelve a declarar un módulo del SDK —el caso que Apple menciona— hoy compila en silencio y oculta el módulo real. Mi shim
SQLite3incorporado pasó la verificación de tipos con código de salida 0 ysqlite3_opendejó de existir.8 - En siete proyectos auditados: cero
-ld_classic,OTHER_LDFLAGSsin definir en todos y cero nombres de módulo duplicados entre 391 mapas de módulos, porque ningún repositorio contiene uno escrito a mano.9
Ambas notas aparecen en las notas de versión de Xcode 27 beta 4, junto con Swift 6.4 y los SDK de la línea 27.11 Léelas como texto de beta.
La opción que Apple te dijo que agregaras
La historia empieza con la reescritura del enlazador. Xcode 15 anunció uno nuevo, lo puso como predeterminado «para todos los binarios de macOS, iOS, tvOS y visionOS» y fijó las condiciones de la jubilación en una sola cláusula: «El enlazador clásico todavía se puede solicitar explícitamente con -ld64, y se eliminará en una versión futura».3
El nuevo enlazador llegó con errores, y Apple documentó la vía de escape dos veces en la misma página. El primer problema conocido: «Los binarios que usan símbolos con una definición débil fallan en tiempo de ejecución en iOS 14/macOS 12 o anteriores. Esto afecta sobre todo a los proyectos en C++ por su uso extensivo de símbolos débiles». La solución alterna de Apple era subir el objetivo de despliegue «o agregar -Wl,-ld_classic al ajuste de compilación OTHER_LDFLAGS».3 El segundo, que cubría archivos objeto con LTO que enlazaban importaciones de símbolos débiles como no débiles, ofrecía «-Wl,-weak_reference_mismatches,weak o -Wl,-ld_classic» en el mismo ajuste.3
Los dos errores golpearon con más fuerza a los proyectos en C++, lo que explica quién sigue arrastrando la opción. Nadie vuelve a revisar un ajuste que detuvo un fallo.
Xcode 16 avisó en una frase: «La opción de enlazador -ld_classic está obsoleta y se eliminará en una versión futura».4 Después, silencio. Busqué ld_classic, ld64 y cualquier mención del enlazador clásico en las notas de versión de Xcode 26 y no encontré ninguna.10
La cadena de herramientas actual todavía acepta la opción y todavía se queja. En Xcode 26.6 enlacé con ella un programa trivial en C:7
xcrun clang hello.c -o hello -Wl,-ld_classic
ld: warning: -ld_classic is deprecated and will be removed in a future release
Estado de salida cero. El binario se enlaza. Sustituir por -Wl,-ld64 produce la misma advertencia, que nombra a -ld_classic, así que las dos formas que usó Apple llegan hoy a la misma ruta de código.7 La nota de Xcode 27 solo menciona -ld_classic, y no pude probar si -ld64 falla igual.
Un detalle complica la palabra «eliminado». Al pedirle al enlazador de Xcode 26.6 que se identifique con xcrun ld -v, este informa qué arquitecturas delega: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 como binario real dentro de la cadena de herramientas, con su propia página de manual, y el enlazador todavía le deriva ocho arquitecturas.7 Ninguna sobrevive en un SDK de app actual: iPhoneOS 26.5 declara solo arm64e y arm64, y el SDK de watchOS añade arm64_32.7 La lista se reduce a ARM de 32 bits, i386 y objetivos embebidos Cortex-M. Leer su ausencia en los SDK como la razón por la que la eliminación es segura para quienes desarrollan apps es una inferencia mía, no una afirmación de Apple.
Encontrar la opción sin confiar en grep
-ld_classic vive en OTHER_LDFLAGS, así que en lugar de buscar texto dentro de los archivos, pregúntale al sistema de compilación a qué se resuelve:
xcodebuild -showBuildSettings \
-project YourApp.xcodeproj \
-configuration Release -sdk iphoneos 2>/dev/null \
| grep -E "OTHER_LDFLAGS|SUPPORTED_PLATFORMS"
Ejecutado contra el proyecto Reps, devuelve una sola línea:9
SUPPORTED_PLATFORMS = appletvos appletvsimulator iphoneos iphonesimulator macosx
OTHER_LDFLAGS no aparece en absoluto, y esa es la respuesta: el ajuste está sin definir, así que de ahí no llega ninguna opción al paso de enlazado. La ausencia en los ajustes de compilación resueltos aporta información que la ausencia en una búsqueda de texto no aporta. Para la pregunta de plataformas lee también SUPPORTED_PLATFORMS, nunca un *_DEPLOYMENT_TARGET, que Xcode escribe exista o no un destino real.
Tres trampas produjeron falsos resultados limpios durante la auditoría, y las tres no imprimen nada y salen con código de éxito. timeout no existe en un macOS de fábrica, así que envolver xcodebuild en él devuelve código 127 y una salida vacía que se lee como un proyecto sin opciones de enlazado. En zsh, un --include=*.pbxproj sin comillas se expande como glob antes de que grep lo vea, de modo que el comando muere con «no matches found» en vez de reportar cero coincidencias. Y find no sigue un enlace simbólico cuando se lo das como punto de partida, lo que importa porque xcrun --sdk iphoneos --show-sdk-path devuelve uno: find "$SDK" -name '*.modulemap' no encuentra nada, mientras que find -H "$SDK" encuentra 266 archivos.9 Pon comillas a tus patrones, pasa -H y confirma que el comando encuentra algo que sabes que está ahí antes de fiarte de un cero.
Un target mantenido a mano guarda la opción en project.pbxproj o en un .xcconfig; un proyecto con CocoaPods también puede recibirla desde un bloque post_install, que no queda registrado en ningún archivo editado por quien desarrolla. El conjunto que audité no tiene ninguna de las dos cosas, así que esa última vía quedó sin verificar.9
La regla de nombres de módulo y las dos salvedades de Apple
La segunda ruptura llega disfrazada de mejora de rendimiento, archivada bajo New Features y no bajo Deprecations. La entrada de Apple sobre el compilador de Swift, completa:
El escáner de dependencias de Swift se ha optimizado para evitar trabajo de configuración y búsquedas de cabeceras redundantes al resolver módulos Clang durante una sola acción de escaneo de dependencias, lo que mejora sustancialmente el rendimiento del escaneo. Como consecuencia de este cambio, cada módulo Clang alcanzable desde una sola acción de escaneo de dependencias de Swift debe tener un nombre de módulo único. Si dos mapas de módulos visibles para el mismo escaneo declaran un módulo Clang con el mismo nombre, el escaneo puede reportar un error. Antes, el escáner puede haber tolerado nombres duplicados. Los casos más comunes son proyectos o SDK que proveen el mismo nombre de módulo Clang desde más de una ubicación de la ruta de búsqueda de cabeceras, y fuentes de terceros incorporadas que traen un module.modulemap que vuelve a declarar un módulo del SDK.2
Ahí hay cuatro cosas que conviene separar. Apple acota el requisito a lo que alcanza un solo escaneo, no a todo tu disco. Apple enuncia la consecuencia como «puede reportar un error» y matiza igual el comportamiento anterior. Apple nombra las dos formas que lo disparan: un mismo nombre de módulo provisto desde dos ubicaciones de la ruta de búsqueda de cabeceras, y un module.modulemap incorporado que vuelve a declarar un módulo del SDK. Y el detonante es la cadena de herramientas, porque nada menciona un objetivo de despliegue ni una versión del SDK.
La regla es anterior a la optimización. El lenguaje de mapas de módulos de Clang lo dice sin rodeos: «Cada módulo debe tener una única definición».6 Lo que la documentación nunca dice es qué pasa si rompes la regla, y ese silencio resulta estar bien ganado: la respuesta de la cadena de herramientas no es un comportamiento, sino varios.
Qué hace realmente una colisión
Las salvedades de Apple me dieron ganas de ver el fallo, así que armé la versión más pequeña posible: dos directorios, cada uno con un module.modulemap que declara el mismo módulo.8
A/module.modulemap B/module.modulemap
module Widget { module Widget {
header "widget.h" header "widget.h"
export * export *
} }
Con ambos directorios en la ruta de búsqueda, una simple verificación de tipos falla igual siempre, en 20 de 20 ejecuciones: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 es la cadena que hay que buscar en un log de compilación, y el clang de Xcode 26.6 ya la emite. Quitar una de las rutas de búsqueda hace que esa misma compilación tenga éxito, lo que confirma que el detonante es la visibilidad dentro de una compilación y no la presencia en el disco.8
El escáner de dependencias se comporta de otra manera, y esa diferencia es toda la razón por la que Apple escribió «puede». Pasar los mismos dos mapas de módulos por swiftc -scan-dependencies 20 veces produjo tres resultados distintos: nueve caídas con SIGSEGV, cinco abortos y seis ejecuciones limpias.8 Las trazas de pila de las ejecuciones que se caen pasan por performParallelClangModuleLookup, lo que encaja con una condición de carrera en la búsqueda paralela que Apple dice haber reemplazado. Atribuir el no determinismo a esa carrera es mi lectura de la traza, no una afirmación de Apple.
El segundo caso que nombra Apple es el silencioso. Escribí un mapa de módulos incorporado que declara SQLite3, un módulo real del SDK de iPhoneOS, lo puse en la ruta de búsqueda y compilé contra él:8
Vendor/module.modulemap
module SQLite3 {
header "shim.h"
export *
}
La compilación tuvo éxito. Código 0, sin advertencia, sin nota, sin diagnóstico de ningún tipo. Después le pedí sqlite3_open a esa misma configuración:8
error: cannot find 'sqlite3_open' in scope
Sin el directorio incorporado en la ruta de búsqueda, ese mismo archivo compila. El mapa de módulos incorporado eclipsó por completo el SQLite3 del SDK, y la cadena de herramientas no dijo nada. Así que hoy el caso de la redeclaración incorporada no falla a gritos: falla como la ausencia de API en un módulo que creías haber importado. La nota de Apple dice que en Xcode 27 el escaneo «puede reportar un error» ahí, lo que convertiría un eclipsamiento silencioso en un fallo de compilación.
Yo compilo con Xcode 26.6 (build 17F113), así que todos los diagnósticos anteriores vienen de la cadena de herramientas previa.78 No puedo reportar el texto de error de Xcode 27 y no me he inventado ninguno. La maquinaria de diagnóstico, la cadena que hay que buscar con grep y la tolerancia que Apple dice estar retirando existen todas hoy.
Auditar nombres de módulo duplicados
Enumerar los nombres de módulo declarados parece un grep de una línea, pero dos construcciones del lenguaje de mapas de módulos dan respuestas equivocadas.
extern module Foo "Foo.modulemap" es una referencia anticipada, no una definición, y el SDK de Apple usa 79 de ellas en un solo mapa de módulos.7 Un escaneo ingenuo cuenta la referencia y la definición real como dos declaraciones de Foo. Segunda: module Darwin.C { ... } usa un module-id con punto para extender un módulo declarado en otra parte; 13 mapas de módulos del SDK abren module Darwin.something, y leer el primer componente como declaración de nivel superior mete los 13 en el mismo saco que la declaración real de Darwin, como una colisión de 14 archivos.7 Los dos falsos positivos aparecieron en mi primer borrador.
Lo que sobrevive maneja comentarios, la profundidad de llaves para que los submódulos explicit module anidados queden fuera, y rutas con espacios:
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
El control negativo más fuerte disponible es el propio SDK de Apple, que tiene que cumplir la regla para que la cadena de herramientas funcione siquiera. Apuntado al SDK de iPhoneOS 26.5, el comando encuentra 1.099 declaraciones de nivel superior bajo usr/include y 205 en los mapas de módulos de los frameworks, con cero duplicados en ambos casos.7 Apuntado a casos de prueba construidos para colisionar, nombra el duplicado y los dos archivos.8
Las exclusiones pesan. -not -path '*/build/*' no excluye .build/, porque el patrón necesita el nombre literal del directorio, y SwiftPM escribe mapas de módulos generados dentro de .build por docenas. Dejar .build dentro produjo mis únicos «duplicados» en siete proyectos: GrappleCore en seis archivos y GrappleRender en tres, todos salida de SwiftPM, repartidos entre dos targets de Swift en distintas raíces de compilación.9 Los nueve ni siquiera son idénticos byte a byte, y la razón es instructiva: cada uno envuelve el mismo nombre de módulo alrededor de una ruta absoluta a la cabecera generada de su propia raíz de compilación, así que difieren en una cadena y en nada que importe. Una auditoría que los llame colisiones agota su credibilidad antes de llegar a algo real.
Xcode fabrica el mismo falso positivo con menos ambigüedad. Dentro de un solo árbol de DerivedData escribe dos veces el mapa de módulos generado de cada paquete Swift: en GeneratedModuleMaps-iphonesimulator/ y en los intermedios del propio target, con una ruta de cabecera relativa las dos veces. En el árbol de un proyecto aparecen 15 nombres dos veces cada uno, y los 15 pares son idénticos byte a byte.9 Acota la auditoría al código fuente, no a la salida de compilación.
Qué contienen siete proyectos
Ejecuté las dos auditorías contra siete proyectos de Xcode, en Xcode 26.6. El resultado honesto es un cero limpio.9
| Proyecto | Archivos Swift | Objective-C / C++ / C | Dependencias | -ld_classic |
Mapas de módulos en fuentes |
|---|---|---|---|---|---|
| Reps | 77 | 0 | 2 SPM locales | 0 | 0 |
| Return | 57 | 0 | ninguna | 0 | 0 |
| Banana List | 55 | 0 | ninguna | 0 | 0 |
| Ace Citizenship | 26 | 0 | ninguna externa | 0 | 0 |
| Water | 34 | 0 (2 .metal) |
ninguna | 0 | 0 |
| ResumeGeni | 71 | 0 | 3 SPM remotos, 9 pins | 0 | 0 |
| Yawara | 143 | 0 | 2 SPM locales | 0 | 0 |
| Total | 463 | 0 | solo SPM | 0 | 0 |
OTHER_LDFLAGS está sin definir en los siete proyectos, no existe ningún archivo .xcconfig en todo el conjunto y no hay CocoaPods, ni Carthage, ni ningún .framework o .xcframework incorporado.9 La auditoría de nombres de módulo examinó 391 mapas de módulos —204 dentro de los directorios de los proyectos y 187 en el DerivedData compartido donde caen las compilaciones de la GUI de Xcode— y todos son salida de compilación.9 Ni un solo repositorio contiene un mapa de módulos escrito a mano.
Los dos ceros tienen la misma causa, y es el hallazgo que vale la pena llevarse: la población expuesta son los proyectos multilenguaje, y estos no lo son. Entre 463 archivos Swift, el conjunto no tiene un solo fuente en Objective-C, C++ ni C, y los dos shaders Metal de Water son el único código compilado que no es Swift.9 Un resultado nulo en una base de código que nunca estuvo en riesgo es evidencia débil sobre los peligros y evidencia fuerte sobre quién necesita auditar.
Dos detalles importan más que los ceros. Los únicos mapas de módulos escritos a mano en los paquetes resueltos pertenecen a swift-crypto, que provee cuatro shims de C llamados CCryptoBoringSSL, CCryptoBoringSSLShims, CXKCP y CXKCPShims, todos distintos.9 Y ningún nombre declarado por el conjunto aparece en el espacio de nombres de módulos Clang del SDK, comprobado contra los 1.318 nombres de nivel superior que el comando encuentra en el SDK de iPhoneOS 26.5.9 Incluso los nombres genéricos que llegan a través de Supabase pasan de largo: el SDK declara módulos Clang llamados Foundation, UIKit y SQLite3, y ninguno llamado Crypto, Storage ni Auth.
El piso de despliegue de C++ que se mueve por debajo
Otro cambio del mismo ciclo golpea a los mismos proyectos en C++ que conservan -ld_classic. Apple subió un piso, nombrando macOS y ninguna otra plataforma: «El objetivo de despliegue mínimo compatible en macOS para la biblioteca estándar de C++ se ha elevado a 11.0».5
La misma entrada ofrece una vía de escape y le pone fecha de caducidad en la frase siguiente. libc++ cambió el resultado de lower_bound y upper_bound sobre std::map y std::set para comparadores que no son un orden débil estricto, y definir _LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND «revertirá a la implementación histórica de estas operaciones». Y luego: «Esa vía de escape se eliminará en una versión próxima (probablemente en la siguiente)».5 Un cambio relacionado hace que multimap::find y multiset::find ya no devuelvan necesariamente el primer elemento igual, un comportamiento que, según señala Apple, «nunca estuvo garantizado por el estándar» aunque libc++ siempre lo ofreció, y llega sin forma de desactivarlo.5 Trata la macro como una tarea de migración pendiente, no como una solución.
Preguntas frecuentes
¿Por qué mi proyecto tiene -ld_classic?
Casi con seguridad porque lo recetaron las notas de versión de Xcode 15. Dos problemas conocidos recomendaban ahí agregar -Wl,-ld_classic a OTHER_LDFLAGS: uno en el que los binarios que usaban símbolos con definición débil fallaban en tiempo de ejecución en iOS 14 y macOS 12 o anteriores —Apple señaló que «afecta sobre todo a los proyectos en C++»— y otro en el que los archivos objeto con LTO enlazaban las importaciones de símbolos débiles como no débiles.3 Apple marcó la opción como obsoleta en Xcode 16 y eliminó el enlazador que había detrás en Xcode 27.14 Si la opción está ahí, confirma que el error original todavía se reproduce antes de salir a buscar un reemplazo.
¿Cómo encuentro nombres de módulo Clang duplicados en mi proyecto?
Enumera las declaraciones de módulo de nivel superior en todos los mapas de módulos, busca un nombre que aparezca en dos archivos y descarta los directorios de compilación. Tres cosas hacen que el trabajo sea más que un grep: extern module Foo "path" es una referencia y no una definición, module Foo.Bar extiende un módulo declarado en otra parte en lugar de declarar Foo, y los submódulos explicit module anidados no colisionan con los nombres de nivel superior.6 Excluye .build, build y DerivedData de forma explícita, porque -not -path '*/build/*' se salta .build y tanto SwiftPM como Xcode duplican mapas de módulos generados de forma rutinaria.9
¿Cómo se ve el error en Xcode 27?
No lo puedo decir, ni tampoco nadie que compile con Xcode 26. Mi máquina corre Xcode 26.6 (build 17F113), así que no reporto ninguna salida de Xcode 27.78 Lo que Xcode 26.6 emite ante dos mapas de módulos que declaran el mismo nombre es error: redefinition of module 'Widget' junto con note: previously defined here, en cada ejecución de una verificación de tipos simple.8 La redacción de Apple para Xcode 27 es que el escaneo «puede reportar un error», así que busca en los logs redefinition of module y no una cadena que alguien haya adivinado.
¿Un fallo por nombre de módulo no único depende de mi objetivo de despliegue?
No. Apple plantea el requisito frente a una sola acción de escaneo de dependencias de Swift y no nombra ninguna versión de sistema operativo, ni SDK, ni objetivo de despliegue en la entrada.2 El cambio del enlazador se lee igual, como una eliminación de la cadena de herramientas.1 Los dos aterrizan en la primera compilación con Xcode 27, junto con la macro @State y no con los requisitos disparados por el SDK del mismo ciclo.
Puntos clave
Para desarrolladores de iOS:
- Consulta OTHER_LDFLAGS con xcodebuild -showBuildSettings -configuration Release -sdk iphoneos en vez de buscar -ld_classic con grep. La ausencia en los ajustes resueltos significa que está sin definir; la ausencia en una búsqueda de texto no significa nada.9
- Busca en los logs de compilación redefinition of module, el diagnóstico que Xcode 26.6 ya emite, no un texto de error de Xcode 27 inventado.8
Para equipos con dependencias C o C++ incorporadas:
- Audita primero los archivos module.modulemap incorporados contra el espacio de nombres del SDK: es el caso que Apple menciona y hoy falla en silencio. Mi shim SQLite3 incorporado compiló con código 0 e hizo desaparecer sqlite3_open.8
- Acota la auditoría al código fuente y excluye .build, build y DerivedData. Cada duplicado aparente entre 391 mapas de módulos era salida de compilación generada para un solo target de Swift.9
Para responsables de lanzamiento: - Trata ambos cambios como disparados por la cadena de herramientas y prográmalos para la primera compilación con Xcode 27, no para la migración de SDK. Ninguno menciona un objetivo de despliegue ni tiene componente en tiempo de ejecución.12 - Deja las salvedades de Apple escritas en el ticket. El escaneo «puede reportar un error», y 20 ejecuciones de una misma colisión dieron tres resultados distintos, así que una compilación que pasa una vez no prueba nada.28
El ciclo 27 sigue fallando en lugares distintos: la clave de la pantalla de lanzamiento detiene un envío, el mandato del ciclo de vida de escenas detiene un arranque y la macro @State detiene una compilación al nivel del código fuente. La eliminación del enlazador y la regla de nombres de módulo la detienen una capa más abajo, en las partes de la cadena de herramientas que nadie configura a propósito. El centro de la serie completa es la Serie del ecosistema Apple.
Referencias
-
Apple, Xcode 27 Release Notes, sección Linking, Deprecations in Xcode 27 Beta (radar 165165518). Fuente de la eliminación, citada completa: «El enlazador ld64 se ha eliminado y la opción
-ld_classicya no es compatible». Verificado contra el JSON de la documentación de Apple el 26 de julio de 2026, ya que la página HTML renderiza su contenido mediante JavaScript. El título de la página en esa fecha es «Xcode 27 Beta 4 Release Notes». ↩↩↩↩↩ -
Apple, Xcode 27 Release Notes, sección Swift Compiler, New Features in Xcode 27 Beta (radar 136303612). Fuente de la entrada sobre el escáner de dependencias, citada textualmente y completa en el cuerpo de este artículo, incluidas las dos salvedades («el escaneo puede reportar un error» y «Antes, el escáner puede haber tolerado nombres duplicados») y los dos casos nombrados. Verificado contra el JSON de la documentación de Apple el 26 de julio de 2026. Se hace notar que está archivada bajo New Features y no bajo Deprecations ni Known Issues. ↩↩↩↩↩↩
-
Apple, Xcode 15 Release Notes, sección Linking. New Features (radar 108915312) es la fuente de «Se ha escrito un nuevo enlazador para acelerar significativamente el enlazado estático. Es el predeterminado para todos los binarios de macOS, iOS, tvOS y visionOS y para cualquiera que use la función “Mergeable Libraries”. El enlazador clásico todavía se puede solicitar explícitamente con -ld64, y se eliminará en una versión futura». Known Issues es la fuente de las dos soluciones alternas que recomiendan la opción: radar 114813650 (FB13097713), «Los binarios que usan símbolos con una definición débil fallan en tiempo de ejecución en iOS 14/macOS 12 o anteriores. Esto afecta sobre todo a los proyectos en C++ por su uso extensivo de símbolos débiles», con la solución alterna de subir el objetivo de despliegue «o agregar
-Wl,-ld_classical ajuste de compilaciónOTHER_LDFLAGS»; y radar 115521975 (FB13171424), «Las importaciones de símbolos débiles se enlazan como importaciones no débiles cuando se usan desde archivos objeto con LTO», con la solución alterna «Agregar las opciones-Wl,-weak_reference_mismatches,weako-Wl,-ld_classical ajuste de compilaciónOTHER_LDFLAGS». Verificado contra el JSON de la documentación de Apple el 26 de julio de 2026. ↩↩↩↩↩↩ -
Apple, Xcode 16 Release Notes, sección Linking, Deprecations (radar 128502299): «La opción de enlazador
-ld_classicestá obsoleta y se eliminará en una versión futura». Verificado contra el JSON de la documentación de Apple el 26 de julio de 2026. ↩↩↩ -
Apple, Xcode 27 Release Notes, sección C++ Standard Library, Deprecations in Xcode 27 Beta. Apple imprime un único radar para todo el bloque, 178191050, al final del último punto y no junto a cada uno. Fuente de «El objetivo de despliegue mínimo compatible en macOS para la biblioteca estándar de C++ se ha elevado a 11.0», del cambio en
multi{map,set}::find(«el código que dependa de quefinddevuelva el primer elemento quedará roto, y en su lugar deberían usarselower_boundoequal_range») y de la vía de escape y su caducidad: «Como esto puede ser difícil de sortear en algunos casos, en esta versión se ofrece una vía de escape: definir_LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUNDrevertirá a la implementación histórica de estas operaciones. Esa vía de escape se eliminará en una versión próxima (probablemente en la siguiente)». Verificado contra el JSON de la documentación de Apple el 26 de julio de 2026. ↩↩↩ -
Equipo de Clang, documentación de Clang Modules, Module Map Language. Fuente de «Cada módulo debe tener una única definición», de la regla del module-id y de la declaración
extern module, de «El calificadorexplicitsolo puede aplicarse a un submódulo, es decir, a un módulo anidado dentro de otro módulo», y del descubrimiento de mapas de módulos por el nombre de archivomodule.modulemap, conmodule.mapbuscado por compatibilidad. Citado como la referencia de la propia cadena de herramientas para el lenguaje de mapas de módulos, no como documento para desarrolladores de Apple; el clang de Apple deriva de esta implementación. ↩↩ -
Pruebas del autor en macOS 26.5.2 (build 25F84) con Xcode 26.6 (build 17F113), Apple clang 21.0.0, 26 de julio de 2026. La salida de los comandos se reproduce textualmente.
xcrun clang hello.c -o hello -Wl,-ld_classicimprime «ld: warning: -ld_classic is deprecated and will be removed in a future release» y sale con 0; sustituir por-Wl,-ld64imprime la misma advertencia, que nombra a-ld_classic.xcrun ld -vreportaPROJECT:ld-1267y las listas de arquitecturas citadas arriba.ld-classicestá presente en/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld-classic, con su página de manual al lado. Las arquitecturas del SDK salen deSDKSettings.plist: iPhoneOS 26.5 declaraarm64eyarm64; WatchOS 26.5 declaraarm64,arm64eyarm64_32. Los conteos de mapas de módulos del SDK (1.099 declaraciones de nivel superior enusr/include, 205 enSystem/Library/Frameworks, cero duplicados en ambos) salen de ejecutar el comando publicado arriba contra el SDK de iPhoneOS 26.5; las 79 declaracionesextern moduleestán enusr/include/module.modulemap, y 13 mapas de módulos del mismo directorio abren unmodule Darwin.*con punto (bank,Darwin_C,Darwin_Mach,Darwin_Mach_machine,Darwin_machine,Darwin_POSIX,Darwin_sys,device,mach_debug,net,netinet,netinet6yuuid), que una lectura por primer componente agrupa con la declaración real deDarwinenDarwin.modulemapcomo una sola colisión de 14 archivos. El comportamiento bajo Xcode 27 no se probó, porque Xcode 27 no estaba instalado en la máquina utilizada. ↩↩↩↩↩↩↩↩↩↩ -
Reproducción del autor en la misma máquina y cadena de herramientas, 26 de julio de 2026. Dos directorios, cada uno con un
module.modulemapque declaramodule Widget, compilados con ambos directorios en la ruta de búsqueda medianteswiftc.-typecheckprodujoerror: redefinition of module 'Widget'connote: previously defined hereen 20 de 20 ejecuciones, con código de salida 1; quitar una ruta-Ihizo que ese mismo archivo compilara con código 0.-scan-dependenciessobre la misma entrada, en 20 ejecuciones, produjo nueve salidas con la señal 11 (SIGSEGV), cinco con la señal 6 (SIGABRT) y seis salidas con estado 0; las trazas de pila de las ejecuciones caídas pasaban porswift::ModuleDependencyScanner::performParallelClangModuleLookup. Por separado, un mapa de módulos incorporado que declaraSQLite3(un módulo del SDK de iPhoneOS 26.5), colocado en la ruta de búsqueda, pasó la verificación de tipos con código 0 y sin diagnóstico alguno, mientras que un archivo que llamaba alsqlite3_opendel SDK bajo la misma configuración falló con «error: cannot find ‘sqlite3_open’ in scope» y compiló sin problemas en cuanto se quitó el directorio incorporado de la ruta de búsqueda. Los casos de prueba incluyeron una ruta con un espacio para verificar el comando publicado. Ese comando compara nombres entre archivos, así que un nombre declarado dos veces dentro de un mismo mapa de módulos queda fuera de su alcance por diseño. En ninguna parte de este artículo se reporta salida de Xcode 27. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Auditoría del autor sobre siete proyectos de Xcode (Reps, Return, Banana List, Ace Citizenship, Water, ResumeGeni y Yawara) en macOS 26.5.2 con Xcode 26.6 (build 17F113), 26 de julio de 2026.
-ld_classicyld64no aparecen ni una sola vez en ninguno de los árboles de trabajo, yOTHER_LDFLAGSestá sin definir en todos los proyectos, sin archivos.xcconfig, sin Podfile, sin Carthage y sin ningún.frameworko.xcframeworkincorporado en todo el conjunto; la búsqueda cubrió únicamente los árboles de trabajo actuales, no el historial de git ni logs de compilación archivados, así que el resultado es «ausente ahora» y no «nunca se usó». La líneaSUPPORTED_PLATFORMScitada para Reps viene dexcodebuild -showBuildSettings -configuration Release -sdk iphoneos. Totales de mapas de módulos: 391 examinados, 204 bajo los directorios de los siete proyectos y 187 en los árboles propios de esos siete proyectos dentro del~/Library/Developer/Xcode/DerivedDatacompartido (el directorio compartido completo contiene muchos más, pertenecientes a otros proyectos), todos salida de compilación, con cero mapas de módulos escritos a mano o incorporados en cualquiera de los siete repositorios. Los duplicados aparentes se comprobaron por hash de contenido, y los dos generadores se comportan distinto. Los 15 pares de nombres de Xcode en el árbol auditado de ResumeGeni (elbuild/DerivedDatadentro del proyecto), escritos una vez bajoGeneratedModuleMaps-iphonesimulator/y otra bajo los intermedios del target, son idénticos byte a byte las 15 veces, porque Xcode emite una ruta de cabecera relativa. El conteo es por árbol y no por proyecto: el árbol de la misma app bajo el~/Library/Developer/Xcode/DerivedDatacompartido tiene 16, y su varianteIndex.noindex, 22. Lo que generaliza es el mecanismo, no el número. Las copias de SwiftPM no lo son: los seis archivosGrappleCorey los tresGrappleRenderbajo los directorios.buildde Yawara tienen seis y tres hashes distintos y van de 171 a 185 bytes, porque cada uno incrusta una ruta absoluta al-Swift.hgenerado dentro de su propia raíz de compilación y por lo demás es idéntico. Ningún nombre de módulo declarado en el conjunto aparece entre los 1.318 nombres de módulo Clang de nivel superior distintos que el comando publicado encuentra al apuntarlo al SDK completo de iPhoneOS 26.5, de los cuales 1.099 están bajousr/includey 205 bajoSystem/Library/Frameworks; la intersección con los nombres declarados por el conjunto es vacía. El mismo escaneo reporta cero duplicados en todo el SDK, que es el control negativo que la regla exige.CryptoKitestá ausente de esa lista porque se distribuye como un framework solo de Swift, sin mapa de módulos Clang, así que la ausencia de una colisión conCryptodescansa en que el SDK no declara ningún módulo Clang con ese nombre y no en queCryptoKitlo ocupe. swift-crypto 4.4.0 provee los únicos mapas de módulos escritos a mano del grafo de dependencias resuelto (CCryptoBoringSSL,CCryptoBoringSSLShims,CXKCP,CXKCPShims, una declaración cada uno). Las variantes deSymbolKitpara herramienta de host y para target están en el paquete local compartido 941Kit, no en las siete apps. Los conteos de fuentes excluyen los virtualenvs de Python, una corrección que sí importó: un conteo ingenuo le atribuía a Reps archivos C y cabeceras que resultaron estar todos dentro de.venvysite-packagesy que ningún target de Xcode compila. Water no tiene árbol de compilación local, así que su cero en mapas de módulos refleja un proyecto sin compilar y no un grafo de compilación verificado como limpio. Las tres trampas de falso limpio se confirmaron directamente:timeoutestá ausente en un macOS de fábrica y sale con 127; zsh expande un--include=*.pbxprojsin comillas y aborta con «no matches found»; yxcrun --sdk iphoneos --show-sdk-pathdevuelve un enlace simbólico, contra el cualfindsin-Hreporta cero mapas de módulos dondefind -Hreporta 266. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, Xcode 26 Release Notes. Buscados
ld_classic,ld64y cualquier entrada que describa el enlazador clásico el 26 de julio de 2026; no aparece ninguna, así que entre el aviso de obsolescencia de Xcode 16 y la eliminación en Xcode 27 no hay ninguna reformulación intermedia. ↩↩ -
Apple, Xcode 27 Release Notes, Overview: «Xcode 27 beta 4 incluye Swift 6.4 y los SDK para iOS 27, iPadOS 27, tvOS 27, watchOS 27, macOS 27 y visionOS 27». Las mismas notas registran, bajo Intel Deprecation (radar 162138432), que «Xcode 27 solo se instalará y ejecutará en Macs con Apple silicon». Consultado el 26 de julio de 2026. ↩