Xcode 27 usuwa ld64 i wymaga unikalnych nazw modułów

Apple wycofało linker jednym zdaniem: „Linker ld64 został usunięty, a opcja -ld_classic nie jest już obsługiwana.”1 Flaga, która właśnie zniknęła, to flaga, której dodanie Apple samo zalecało. W informacjach o wydaniu Xcode 15 -Wl,-ld_classic figurowało jako obejście dwóch błędów linkera.3

TL;DR

  • Xcode 27 usuwa ld64. W finalnym toolchainie -ld_classic jest ignorowane z ostrzeżeniem, a linkowanie kończy się kodem wyjścia 0, natomiast drugi zapis, który Apple kiedyś udokumentowało, -ld64, powoduje niepowodzenie linkowania.112 Notatki Apple do Xcode 15 zalecały tę flagę przy awariach związanych ze słabymi symbolami i przy błędach importu w LTO, Xcode 16 oznaczył ją jako przestarzałą, a Xcode 26 nie powiedział o niej ani słowa.3410
  • Drugie pęknięcie tkwi w kompilatorze Swift. Apple uczyniło skanowanie zależności jedną wspólną operacją, więc „każdy moduł Clang osiągalny z pojedynczej operacji skanowania zależności Swift musi mieć unikalną nazwę.”2 Apple asekuruje się dwukrotnie: skanowanie „może zgłosić błąd”, a wcześniej skaner „mógł tolerować powtarzające się nazwy.”2
  • Oba ujawniają się przy aktualizacji toolchaina, a nie przy zmianie deployment targetu czy wyborze SDK, i żadne nie ma komponentu w środowisku wykonawczym. Narażona populacja: dostarczone binaria, CocoaPods albo duża, mieszana baza kodu Swift/Objective-C/C++.
  • To „może” u Apple nie jest ostrożnością redakcyjną. Na Xcode 26.6 umieściłem na jednej ścieżce wyszukiwania dwie mapy modułów deklarujące tę samą nazwę i uruchomiłem skaner 20 razy: dziewięć przebiegów zakończyło się awarią SIGSEGV, pięć przerwaniem, a sześć powodzeniem.8 Na finalnym toolchainie 20 przebiegów na tym samym wejściu za każdym razem wypisuje błąd redefinicji i za każdym razem kończy się kodem wyjścia 0, podczas gdy dostarczona mapa modułów z następnego punktu nie wypisuje niczego; szczegóły zawiera druga aktualizacja z 15 września.13
  • Dostarczona mapa modułu redeklarująca moduł z SDK — przypadek, który Apple wymienia z nazwy — kompiluje się dziś po cichu i przesłania prawdziwy moduł. Moja podkładka SQLite3 przeszła typecheck z kodem wyjścia 0, a sqlite3_open przestało istnieć.8
  • W siedmiu przeanalizowanych projektach: zero -ld_classic, OTHER_LDFLAGS nigdzie nieustawione i zero zduplikowanych nazw modułów wśród 391 map modułów — bo żadne repozytorium nie zawiera mapy pisanej ręcznie.9

Obie notatki trafiły najpierw do informacji o wydaniu Xcode 27 beta 4, obok Swift 6.4 i SDK z cyklu 27, i obie pozostały bez zmian w informacjach o wydaniu finalnym z 14 września.1112

Aktualizacja, 15 września: co finalny toolchain robi z każdą z flag

Xcode 27 (27A266a) ukazał się 14 września, a zawarty w nim linker rozstrzyga pytanie, którego ten artykuł nie mógł sprawdzić w lipcu. xcrun ld -v zgłasza teraz:12

@(#)PROGRAM:ld PROJECT:ld-27037.1
BUILD 21:17:51 Aug 25 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
LTO support using: LLVM version 21.0.0 (static support for 30, runtime is 30)

Wiersz „will use ld-classic for:”, który wypisywał Xcode 26.6, zniknął, a wraz z nim plik binarny ld-classic: w usr/bin toolchainu jest jeden linker, ld. Dwa zapisy, które w lipcu trafiały w tę samą ścieżkę kodu, teraz się rozchodzą. Z tym samym jednowierszowym hello.c:12

xcrun clang hello.c -o hello -Wl,-ld_classic
ld: warning: -ld_classic is no longer supported and will be ignored

Kod wyjścia zero, plik binarny się linkuje. Następnie:

xcrun clang hello.c -o hello -Wl,-ld64
ld: library 'd64' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Kod wyjścia jeden, brak pliku binarnego. Nowy linker nie rozpoznaje już -ld64 jako przełącznika wyboru linkera, więc czyta go jako -l d64, czyli żądanie biblioteki o nazwie d64, i zawodzi, bo takiej nie ma. Lipcowe zdanie „przestaje akceptować -ld_classic” było więc prawdziwe w połowie: flaga, którą Apple wymienia, jest tolerowana z ostrzeżeniem, a flaga, którą Apple wymieniło w Xcode 15, -ld64, jest tą, która psuje kompilację. Projekt z -Wl,-ld64 w OTHER_LDFLAGS po aktualizacji zawodzi z komunikatem błędu, który ani razu nie wspomina o linkerze; projekt z -Wl,-ld_classic buduje się dalej i dostaje ostrzeżenie przy każdym linkowaniu. Wpis o premierze Xcode 27 zestawia obie flagi z pozostałymi zmianami, które odpalają się przy aktualizacji.

Aktualizacja, 15 września: skaner z finalnego toolchainu na tych samych dwóch kolizjach

Poniższe eksperymenty z mapami modułów przeprowadziłem w lipcu na Xcode 26.6. Na 27A266a, z tymi samymi dwoma katalogami deklarującymi module Widget na jednej ścieżce wyszukiwania, swiftc -typecheck nadal zawodzi w 20 przebiegach na 20 z error: redefinition of module 'Widget' i notą previously defined here, więc kompilacja nadal się zatrzymuje. Zmienił się skaner. swiftc -scan-dependencies na tym samym wejściu kończy się kodem 0 we wszystkich 20 przebiegach, we wszystkich 20 wypisuje na stderr identyczny błąd redefinition of module 'Widget' wraz z notą i we wszystkich 20 zapisuje kompletny graf zależności, który rozwiązuje Widget do mapy modułu z pierwszego katalogu -I. Tam, gdzie Xcode 26.6 dziewięć razy kończył się awarią, a pięć razy przerwaniem, skaner z finalnego toolchainu za każdym razem zgłasza kolizję i mimo to kończy pracę. Lipcowy odczyt „może” u Apple, jako asekuracji wobec niestabilnego skanera, na finalnym toolchainie się nie broni.813 Na finalnym toolchainie skaner wypisuje błąd i kończy się kodem 0, co oznacza, że krok budowania, który kieruje się tym kodem wyjścia, widzi udane skanowanie, a niepowodzenie przychodzi krok później, przy kompilacji. Nie sprawdzałem, jak własny system budowania Xcode traktuje wypisany błąd ze skanowania zakończonego kodem zero.13

Przypadek z dostarczoną mapą pozostaje cichy. Vendor/module.modulemap redeklarujący SQLite3 na ścieżce wyszukiwania skanuje się z kodem wyjścia 0 i niczym na stderr, w 20 przebiegach na 20, a graf rozwiązuje SQLite3 do dostarczonej mapy, a nie do usr/include/SQLite3.modulemap z SDK, czyli pliku, do którego linia 7 w usr/include/module.modulemap odsyła jako extern module SQLite3 "SQLite3.modulemap"; plik importujący ten moduł przechodzi typecheck czysto, a plik wywołujący sqlite3_open nadal zawodzi z error: cannot find 'sqlite3_open' in scope, dopóki dostarczony katalog nie opuści ścieżki wyszukiwania. Na finalnym toolchainie drugi z kształtów, które Apple wymienia z nazwy, nie daje żadnej diagnostyki, która nazywałaby tę kolizję.13

Te same dwie mapy modułów deklarujące module Widget, skanowane 20 razy: Xcode 26.6 uległ awarii 9 razy, przerwał działanie 5 razy i zakończył się powodzeniem 6 razy; Xcode 27 we wszystkich 20 przebiegach wypisał błąd redefinicji i zakończył się kodem 0; sprawdzanie typów zawiodło we wszystkich 20 przebiegach w obu łańcuchach narzędzi.

Flaga, którą Apple kazało dodać

Historia zaczyna się od napisania linkera od nowa. Xcode 15 ogłosił nowy linker, uczynił go domyślnym „dla wszystkich binariów macOS, iOS, tvOS i visionOS” i w jednym zdaniu podrzędnym ustalił warunki wycofania poprzednika: „O klasyczny linker nadal można poprosić jawnie za pomocą -ld64; zostanie on usunięty w przyszłym wydaniu.”3

Nowy linker przyszedł z błędami, a Apple udokumentowało wyjście awaryjne dwukrotnie na tej samej stronie. Pierwszy wpis w Known Issues: „Binaria używające symboli ze słabą definicją ulegają awarii w czasie działania na iOS 14/macOS 12 lub starszych. Dotyczy to przede wszystkim projektów C++ ze względu na intensywne wykorzystanie słabych symboli.” Obejściem proponowanym przez Apple było podniesienie deployment targetu „albo dodanie -Wl,-ld_classic do ustawienia kompilacji OTHER_LDFLAGS.”3 Drugi, dotyczący plików obiektowych LTO, które linkowały słabe importy symboli jako niesłabe, oferował w tym samym ustawieniu „-Wl,-weak_reference_mismatches,weak lub -Wl,-ld_classic.”3

Oba błędy najmocniej uderzały w projekty C++ i to tłumaczy, kto nadal nosi tę flagę. Nikt nie wraca do ustawienia, które zatrzymało awarię.

Xcode 16 uprzedził jednym zdaniem: „Opcja linkera -ld_classic jest przestarzała i zostanie usunięta w przyszłym wydaniu.”4 Potem cisza. Przeszukałem informacje o wydaniu Xcode 26 pod kątem ld_classic, ld64 i klasycznego linkera — nie ma tam nic.10

Obecny toolchain wciąż akceptuje flagę i wciąż na nią narzeka. Na Xcode 26.6 zlinkowałem z nią trywialny program w 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

Kod wyjścia zero. Plik binarny się linkuje. Podstawienie -Wl,-ld64 daje identyczne ostrzeżenie z nazwą -ld_classic, więc oba zapisy, których Apple używało, trafiają dziś w tę samą ścieżkę kodu.7 Notatka Apple do Xcode 27 wymienia wyłącznie -ld_classic. W finalnym toolchainie oba zapisy się rozchodzą, jak pokazuje aktualizacja powyżej: -ld_classic jest ignorowane z ostrzeżeniem, a -ld64 powoduje niepowodzenie linkowania.12

Jeden szczegół komplikuje słowo „usunięty”. Linker z Xcode 26.6, poproszony o przedstawienie się przez xcrun ld -v, raportuje, które architektury deleguje dalej: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 istnieje w toolchainie jako prawdziwe binarium, z własną stroną man, a linker nadal kieruje do niego osiem architektur.7 Żadna z nich nie przetrwała w aktualnym SDK dla aplikacji: iPhoneOS 26.5 deklaruje wyłącznie arm64e i arm64, a SDK watchOS dokłada arm64_32.7 Ta lista to 32-bitowe ARM-y, i386 oraz wbudowane cele Cortex-M. Odczytanie ich nieobecności w SDK jako powodu, dla którego usunięcie jest bezpieczne dla twórców aplikacji, to mój wniosek, a nie twierdzenie Apple.

Jak znaleźć flagę, nie ufając grepowi

-ld_classic mieszka w OTHER_LDFLAGS, więc zamiast przeszukiwać pliki pod kątem tekstu, warto zapytać system budowania, do czego to ustawienie się rozwija:

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

W projekcie Reps wraca jedna linia:9

    SUPPORTED_PLATFORMS = appletvos appletvsimulator iphoneos iphonesimulator macosx

OTHER_LDFLAGS nie pojawia się wcale i to właśnie jest odpowiedź: ustawienie nie ma żadnej wartości, więc nie trafiają z niego do etapu linkowania żadne flagi. Nieobecność w rozwiniętych ustawieniach kompilacji niesie informację, której nieobecność w wyszukiwaniu tekstowym nie niesie. Pytanie o platformy również warto rozstrzygać przez SUPPORTED_PLATFORMS, nigdy przez *_DEPLOYMENT_TARGET, które Xcode zapisuje niezależnie od tego, czy dany cel jest realny.

W trakcie audytu trzy pułapki dały fałszywie czyste wyniki, a każda z nich nic nie wypisuje i kończy się powodzeniem. timeout nie istnieje w standardowym macOS, więc opakowanie w niego xcodebuild zwraca kod 127 i puste wyjście, które czyta się jak projekt bez żadnych flag linkera. W zsh nieujęte w cudzysłów --include=*.pbxproj zostaje rozwinięte przez globbing, zanim zobaczy je grep, więc polecenie umiera z „no matches found” zamiast zaraportować zero trafień. A find nie podąża za dowiązaniem symbolicznym podanym jako punkt startowy, co ma znaczenie, bo xcrun --sdk iphoneos --show-sdk-path zwraca właśnie dowiązanie: find "$SDK" -name '*.modulemap' nie znajduje nic, podczas gdy find -H "$SDK" znajduje 266 plików.9 Wzorce trzeba ujmować w cudzysłów, przekazywać -H i upewnić się, że polecenie trafia w coś, o czym wiadomo, że tam jest, zanim zaufa się zeru.

Ręcznie utrzymywany target trzyma flagę w project.pbxproj albo w .xcconfig; projekt CocoaPods może ją dostać także z punktu zaczepienia post_install, którego nie odnotowuje żaden plik edytowany przez programistę. Flota, którą audytowałem, nie ma ani jednego, ani drugiego, więc ta ostatnia ścieżka pozostała niesprawdzona.9

Reguła unikalnych nazw modułów i dwa zastrzeżenia Apple

Drugie pęknięcie przychodzi w przebraniu zysku wydajnościowego, wpisane do New Features, a nie do Deprecations. Wpis Apple w sekcji Swift Compiler, w całości:

Skaner zależności Swift został zoptymalizowany tak, by unikać zbędnej pracy konfiguracyjnej i zbędnego wyszukiwania nagłówków przy odnajdywaniu modułów Clang w ramach jednej operacji skanowania zależności, co istotnie poprawia wydajność skanowania. W konsekwencji tej zmiany każdy moduł Clang osiągalny z pojedynczej operacji skanowania zależności Swift musi mieć unikalną nazwę. Jeżeli dwie mapy modułów widoczne dla tego samego skanowania deklarują moduł Clang o tej samej nazwie, skanowanie może zgłosić błąd. Wcześniej skaner mógł tolerować powtarzające się nazwy. Najczęstsze przypadki to projekty lub SDK, które udostępniają tę samą nazwę modułu Clang z więcej niż jednej lokalizacji na ścieżce wyszukiwania nagłówków, oraz dostarczane źródła firm trzecich, które zawierają module.modulemap redeklarujący moduł z SDK.2

Cztery rzeczy warto tu rozdzielić. Apple ogranicza wymóg do tego, co osiągalne w ramach jednego skanowania, a nie do całego dysku. Konsekwencję formułuje jako „może zgłosić błąd” i tak samo asekuruje opis dawnego zachowania. Wymienia z nazwy dwa kształty wyzwalające problem: jedną nazwę modułu udostępnianą z dwóch lokalizacji na ścieżce wyszukiwania nagłówków oraz dostarczony module.modulemap redeklarujący moduł z SDK. Wyzwalaczem jest zaś toolchain, skoro nic w tym wpisie nie odwołuje się do deployment targetu ani do wersji SDK.

Sama reguła jest starsza od optymalizacji. Język map modułów Clanga stawia sprawę wprost: „Każdy moduł musi mieć pojedynczą definicję.”6 Dokumentacja nigdzie nie mówi, co się dzieje po złamaniu tej reguły, a to milczenie okazuje się w pełni zasłużone: odpowiedzią toolchaina nie jest jedno zachowanie, lecz kilka.

Co faktycznie robi kolizja

Zastrzeżenia Apple sprawiły, że chciałem tę awarię zobaczyć, więc zbudowałem jej najmniejszą wersję: dwa katalogi, w każdym module.modulemap deklarujący ten sam moduł.8

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

Gdy oba katalogi znajdą się na ścieżce wyszukiwania, zwykły typecheck zawodzi za każdym razem tak samo, w 20 przebiegach na 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 to ciąg, którego należy szukać w logu kompilacji — clang z Xcode 26.6 już go wypisuje. Usunięcie jednej ścieżki wyszukiwania sprawia, że ta sama kompilacja się udaje, co potwierdza, że wyzwalaczem jest widoczność w obrębie jednej kompilacji, a nie obecność na dysku.8

Skaner zależności zachowuje się inaczej i w lipcu właśnie ta różnica wyglądała na cały powód, dla którego Apple napisało „może”; druga aktualizacja z 15 września wraca do tej kwestii. Przepuszczenie tych samych dwóch map modułów przez swiftc -scan-dependencies 20 razy dało trzy wyniki: dziewięć awarii z SIGSEGV, pięć przerwań i sześć czystych powodzeń.8 Ślady stosu z przebiegów zakończonych awarią prowadzą przez performParallelClangModuleLookup, co pasuje do wyścigu w równoległym wyszukiwaniu, którego wymianę Apple opisuje. Przypisanie tego niedeterminizmu właśnie takiemu wyścigowi to mój odczyt śladu, a nie twierdzenie Apple.

Drugi przypadek wymieniony przez Apple jest tym cichym. Napisałem mapę modułu taką, jaką dostarcza zależność zewnętrzna — deklarującą SQLite3, prawdziwy moduł w SDK iPhoneOS — umieściłem ją na ścieżce wyszukiwania i skompilowałem względem niej kod:8

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

Kompilacja się powiodła. Kod wyjścia 0, żadnego ostrzeżenia, żadnej noty, żadnej diagnostyki jakiegokolwiek rodzaju. Potem poprosiłem tę samą konfigurację o sqlite3_open:8

error: cannot find 'sqlite3_open' in scope

Bez dostarczonego katalogu na ścieżce wyszukiwania identyczny plik kompiluje się bez problemu. Dostarczona mapa modułu całkowicie przesłoniła SQLite3 z SDK, a toolchain nie powiedział ani słowa. Dziś zatem przypadek redeklaracji w dostarczonych źródłach nie zawodzi głośno — zawodzi jako brakujące API w module, który rzekomo został zaimportowany. Notatka Apple mówi, że skanowanie w Xcode 27 „może zgłosić błąd”, co zamieniłoby ciche przesłonięcie w błąd kompilacji.

Buduję na Xcode 26.6 (build 17F113), więc każda powyższa diagnostyka pochodzi z poprzedniego toolchaina.78 Nie mogę podać treści błędu z Xcode 27 i żadnej nie wymyśliłem. Maszyneria diagnostyczna, ciąg do wyszukania i tolerancja, której wycofanie Apple opisuje, istnieją już dziś. (15 września: ten akapit, a także przewidywanie zamykające powyższy akapit, opisują lipiec; na finalnym toolchainie przypadek z dostarczoną mapą nadal zawodzi po cichu, a wyjście dla obu kształtów znajduje się w drugiej aktualizacji na początku tego wpisu.13)

Audyt zduplikowanych nazw modułów

Wyliczenie zadeklarowanych nazw modułów wygląda na jednolinijkowego grepa, ale dwie konstrukcje języka map modułów dają błędne odpowiedzi.

extern module Foo "Foo.modulemap" to odwołanie zapowiadające, a nie definicja, a SDK Apple używa 79 takich odwołań w jednej mapie modułów.7 Naiwne skanowanie liczy odwołanie i prawdziwą definicję jako dwie deklaracje Foo. Po drugie, module Darwin.C { ... } używa kropkowanego identyfikatora modułu, żeby rozszerzyć moduł zadeklarowany gdzie indziej; 13 map modułów z SDK otwiera module Darwin.something, a odczytanie pierwszego członu jako deklaracji najwyższego poziomu wrzuca całą trzynastkę do jednego worka z prawdziwą deklaracją Darwin jako kolizję obejmującą 14 plików.7 Oba fałszywe alarmy pojawiły się w moim pierwszym podejściu.

Wersja, która przetrwała, radzi sobie z komentarzami, z głębokością nawiasów klamrowych (dzięki czemu zagnieżdżone podmoduły explicit module zostają poza wynikiem) oraz ze ścieżkami zawierającymi spacje:

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

Najmocniejszą dostępną kontrolą negatywną jest własny SDK Apple, który musi spełniać tę regułę, żeby toolchain w ogóle działał. Skierowane na SDK iPhoneOS 26.5 polecenie znajduje 1099 deklaracji najwyższego poziomu w usr/include i 205 w mapach modułów frameworków — w obu przypadkach zero duplikatów.7 Skierowane na dane testowe zbudowane tak, by kolidowały, wskazuje duplikat i oba pliki.8

Wykluczenia mają swoją wagę. -not -path '*/build/*' nie wyklucza .build/, bo wzorzec potrzebuje dosłownej nazwy katalogu, a SwiftPM zapisuje wygenerowane mapy modułów do .build tuzinami. Pozostawienie .build dało jedyne „duplikaty” w siedmiu projektach: GrappleCore w sześciu plikach i GrappleRender w trzech, wszystkie będące wyjściem SwiftPM, obejmujące dwa targety Swift w różnych katalogach kompilacji.9 Ta dziewiątka nie jest nawet identyczna bajt po bajcie, a powód jest pouczający: każdy plik opakowuje tę samą nazwę modułu wokół bezwzględnej ścieżki do wygenerowanego nagłówka we własnym katalogu kompilacji, więc różnią się jednym ciągiem znaków i niczym, co ma znaczenie. Audyt, który nazywa je kolizjami, wyczerpuje swoją wiarygodność, zanim dotrze do czegokolwiek prawdziwego.

Xcode produkuje ten sam fałszywy alarm w sposób jeszcze mniej dwuznaczny. W obrębie jednego drzewa DerivedData zapisuje wygenerowaną mapę modułu każdego pakietu Swift dwukrotnie — do GeneratedModuleMaps-iphonesimulator/ oraz do plików pośrednich samego targetu — za każdym razem ze względną ścieżką nagłówka. W drzewie jednego projektu 15 nazw pojawia się po dwa razy, a wszystkie 15 par jest identycznych bajt po bajcie.9 Audyt należy zawęzić do źródeł, nie do wyjścia kompilacji.

Co zawiera siedem projektów

Oba audyty przepuściłem przez siedem projektów Xcode na Xcode 26.6. Uczciwym wynikiem jest czyste zero.9

Projekt Pliki Swift Objective-C / C++ / C Zależności -ld_classic Mapy modułów w źródłach
Reps 77 0 2 lokalne SPM 0 0
Return 57 0 brak 0 0
Banana List 55 0 brak 0 0
Ace Citizenship 26 0 brak zewnętrznych 0 0
Water 34 0 (2 .metal) brak 0 0
ResumeGeni 71 0 3 zdalne SPM, 9 przypięć 0 0
Yawara 143 0 2 lokalne SPM 0 0
Razem 463 0 tylko SPM 0 0

OTHER_LDFLAGS jest nieustawione we wszystkich siedmiu projektach, w całej flocie nie ma nigdzie pliku .xcconfig, nie ma też CocoaPods, Carthage ani dostarczonych .framework czy .xcframework.9 Audyt nazw modułów objął 391 map modułów — 204 w katalogach projektów i 187 we współdzielonym DerivedData, gdzie lądują kompilacje z GUI Xcode — i każda z nich jest wyjściem kompilacji.9 Ani jedno repozytorium nie zawiera ręcznie napisanej mapy modułu.

Oba zera mają tę samą przyczynę i to właśnie ten wniosek warto zabrać ze sobą: narażoną populacją są projekty mieszające języki programowania, a te takie nie są. W 463 plikach Swift flota nie ma ani jednego źródła Objective-C, C++ czy C — jedynym skompilowanym kodem spoza Swifta są dwa shadery Metal w projekcie Water.9 Zerowy wynik z bazy kodu, która nigdy nie była zagrożona, jest słabym dowodem na temat samych zagrożeń, a mocnym na temat tego, kto powinien przeprowadzić audyt.

Dwa szczegóły znaczą więcej niż same zera. Jedyne ręcznie pisane mapy modułów w rozwiązanych pakietach należą do swift-crypto, które udostępnia cztery podkładki w C o nazwach CCryptoBoringSSL, CCryptoBoringSSLShims, CXKCP i CXKCPShims — wszystkie różne.9 Żadna nazwa deklarowana przez flotę nie pojawia się też nigdzie w przestrzeni nazw modułów Clang z SDK, co sprawdzono wobec wszystkich 1318 nazw najwyższego poziomu, jakie polecenie znajduje w SDK iPhoneOS 26.5.9 Nie trafiają nawet te generyczne, przychodzące przez Supabase: SDK deklaruje moduły Clang o nazwach Foundation, UIKit i SQLite3, a żadnego o nazwie Crypto, Storage czy Auth.

Próg deployment targetu dla C++, który przesuwa się pod spodem

Jedna sąsiadująca zmiana uderza w te same projekty C++, które trzymają -ld_classic. Apple podniosło próg i wymieniło przy tym tylko jedną platformę — macOS: „Minimalny obsługiwany deployment target na macOS dla biblioteki standardowej C++ został podniesiony do 11.0.”5

Ten sam wpis oferuje wyjście awaryjne i w następnym zdaniu datuje jego usunięcie. libc++ zmieniło wynik lower_bound i upper_bound na std::map oraz std::set dla komparatorów niebędących ścisłym porządkiem słabym, a zdefiniowanie _LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND „przywróci historyczną implementację tych operacji”. A dalej: „To wyjście awaryjne zostanie usunięte w jednym z nadchodzących wydań (prawdopodobnie w kolejnym).”5 Powiązana zmiana sprawia, że multimap::find i multiset::find nie muszą już zwracać pierwszego równego elementu — zachowanie, o którym Apple pisze, że „nigdy nie było gwarantowane przez Standard”, choć libc++ zawsze je zapewniało — i wchodzi w życie bez możliwości rezygnacji.5 Makro warto traktować jak zgłoszenie migracyjne, a nie jak poprawkę.

FAQ

Dlaczego mój projekt zawiera -ld_classic?

Niemal na pewno dlatego, że zalecały ją informacje o wydaniu Xcode 15 od Apple. Dwa wpisy Known Issues zalecały tam dodanie -Wl,-ld_classic do OTHER_LDFLAGS: jeden dotyczył binariów używających słabo zdefiniowanych symboli, które ulegały awarii w czasie działania na iOS 14 i macOS 12 lub starszych — co, jak zaznaczyło Apple, „dotyczy przede wszystkim projektów C++” — a drugi plików obiektowych LTO linkujących słabe importy symboli jako niesłabe.3 Apple oznaczyło opcję jako przestarzałą w Xcode 16, a w Xcode 27 usunęło stojący za nią linker.14 Jeśli flaga tam jest, przed szukaniem zamiennika warto potwierdzić, że pierwotny błąd nadal się odtwarza.

Jak znaleźć zduplikowane nazwy modułów Clang w projekcie?

Należy wyliczyć deklaracje modułów najwyższego poziomu we wszystkich mapach modułów, poszukać nazwy występującej w dwóch plikach i usunąć z wyników katalogi kompilacji. Trzy rzeczy sprawiają, że to więcej niż grep: extern module Foo "path" jest odwołaniem, a nie definicją; module Foo.Bar rozszerza moduł zadeklarowany gdzie indziej, zamiast deklarować Foo; a zagnieżdżone podmoduły explicit module nie kolidują z nazwami najwyższego poziomu.6 .build, build i DerivedData trzeba wykluczyć jawnie, bo -not -path '*/build/*' nie łapie .build, a zarówno SwiftPM, jak i Xcode rutynowo duplikują generowane mapy modułów.9

Jak wygląda ten błąd w Xcode 27?

Na Xcode 27.0 (27A266a) treść jest ta sama, którą Xcode 26.6 już wypisuje: error: redefinition of module 'Widget' wraz z note: previously defined here. Zwykły -typecheck wypisuje ją i kończy się kodem 1; -scan-dependencies wypisuje ją i kończy się kodem 0.13 W lipcu na moim komputerze działał Xcode 26.6 (build 17F113), więc lipcowy tekst nie raportował żadnego wyjścia z Xcode 27;78 dodaje je druga aktualizacja z 15 września na początku wpisu.13 Apple pisze o Xcode 27, że skanowanie „może zgłosić błąd”, więc w logach warto szukać redefinition of module, a nie ciągu, który ktoś zgadł.

Czy awaria związana z unikalnością nazw modułów zależy od deployment targetu?

Nie. Apple formułuje ten wymóg wobec pojedynczej operacji skanowania zależności Swift i nie wymienia we wpisie żadnej wersji systemu, żadnego SDK ani deployment targetu.2 Zmiana w linkerze czyta się tak samo — jako usunięcie z toolchaina.1 Oba trafiają w pierwszą kompilację w Xcode 27, razem z makrem @State, a nie razem z wymogami z tego samego cyklu wyzwalanymi przez SDK.

Najważniejsze wnioski

Dla twórców aplikacji na iOS: - Zamiast szukać -ld_classic grepem, warto odpytać OTHER_LDFLAGS przez xcodebuild -showBuildSettings -configuration Release -sdk iphoneos. Nieobecność w rozwiniętych ustawieniach oznacza, że ustawienie nie ma żadnej wartości; nieobecność w wyszukiwaniu tekstowym nie oznacza nic.9 - W logach kompilacji należy szukać redefinition of module — diagnostyki, którą Xcode 26.6 już wypisuje — a nie zmyślonej treści błędu z Xcode 27.8

Dla zespołów z dostarczanymi zależnościami C lub C++: - Dostarczone pliki module.modulemap warto najpierw sprawdzić wobec przestrzeni nazw SDK: Apple wymienia ten przypadek z nazwy, a dziś zawodzi on po cichu. Moja dostarczona podkładka SQLite3 skompilowała się z kodem wyjścia 0 i sprawiła, że sqlite3_open zniknęło.8 - Audyt należy zawęzić do źródeł i wykluczyć .build, build oraz DerivedData. Każdy pozorny duplikat wśród 391 map modułów okazał się wygenerowanym wyjściem kompilacji dla pojedynczego targetu Swift.9

Dla osób odpowiedzialnych za wydania: - Obie zmiany warto traktować jako wyzwalane przez toolchain i zaplanować na pierwszą kompilację w Xcode 27, a nie na migrację SDK. Żadna nie odwołuje się do deployment targetu ani nie ma komponentu w środowisku wykonawczym.12 - Zastrzeżenia Apple warto zachować w zgłoszeniu. Skanowanie „może zgłosić błąd”, a 20 przebiegów jednej kolizji na Xcode 26.6 dało trzy różne wyniki — jedna udana kompilacja nie dowodzi więc niczego.28 Na finalnym toolchainie skanowanie wypisuje błąd, ale kończy się kodem 0, więc zielony krok skanowania również niczego nie dowodzi.13


Cykl 27 zawodzi wciąż w innych miejscach: klucz ekranu startowego zatrzymuje wysyłkę do sklepu, obowiązkowy cykl życia sceny zatrzymuje uruchomienie aplikacji, a makro @State zatrzymuje kompilację na poziomie kodu źródłowego. Usunięcie linkera i reguła unikalnych nazw modułów zatrzymują ją warstwę niżej — w tych częściach toolchaina, których nikt nie konfiguruje celowo. Centrum całej serii to Seria Ekosystem Apple.

Źródła


  1. Apple, Xcode 27 Release Notes, sekcja Linking, Deprecations in Xcode 27 Beta (radar 165165518). Źródło informacji o usunięciu, cytowane w całości: „Linker ld64 został usunięty, a opcja -ld_classic nie jest już obsługiwana.” Zweryfikowane wobec JSON dokumentacji Apple 26 lipca 2026, ponieważ strona HTML renderuje swoją treść przez JavaScript. Tytuł strony w tym dniu brzmi „Xcode 27 Beta 4 Release Notes”. ↩↩↩↩↩

  2. Apple, Xcode 27 Release Notes, sekcja Swift Compiler, New Features in Xcode 27 Beta (radar 136303612). Źródło wpisu o skanerze zależności, cytowanego dosłownie i w całości w treści tego artykułu, wraz z oboma zastrzeżeniami („skanowanie może zgłosić błąd” oraz „Wcześniej skaner mógł tolerować powtarzające się nazwy”) i dwoma wymienionymi przypadkami. Zweryfikowane wobec JSON dokumentacji Apple 26 lipca 2026. Odnotowane jako wpisane do New Features, a nie do Deprecations czy Known Issues. ↩↩↩↩↩↩

  3. Apple, Xcode 15 Release Notes, sekcja Linking. New Features (radar 108915312) jest źródłem zdania: „Napisano nowy linker, który istotnie przyspiesza statyczne linkowanie. Jest domyślny dla wszystkich binariów macOS, iOS, tvOS i visionOS oraz dla każdego, kto korzysta z funkcji »Mergeable Libraries«. O klasyczny linker nadal można poprosić jawnie za pomocą -ld64; zostanie on usunięty w przyszłym wydaniu.” Known Issues jest źródłem obu obejść zalecających tę flagę: radar 114813650 (FB13097713), „Binaria używające symboli ze słabą definicją ulegają awarii w czasie działania na iOS 14/macOS 12 lub starszych. Dotyczy to przede wszystkim projektów C++ ze względu na intensywne wykorzystanie słabych symboli”, z obejściem polegającym na podniesieniu deployment targetu „albo dodaniu -Wl,-ld_classic do ustawienia kompilacji OTHER_LDFLAGS”; oraz radar 115521975 (FB13171424), „Słabe importy symboli są linkowane jako importy niesłabe, gdy pochodzą z plików obiektowych LTO”, z obejściem „Dodaj opcje -Wl,-weak_reference_mismatches,weak lub -Wl,-ld_classic do ustawienia kompilacji OTHER_LDFLAGS.” Zweryfikowane wobec JSON dokumentacji Apple 26 lipca 2026. ↩↩↩↩↩↩

  4. Apple, Xcode 16 Release Notes, sekcja Linking, Deprecations (radar 128502299): „Opcja linkera -ld_classic jest przestarzała i zostanie usunięta w przyszłym wydaniu.” Zweryfikowane wobec JSON dokumentacji Apple 26 lipca 2026. ↩↩↩

  5. Apple, Xcode 27 Release Notes, sekcja C++ Standard Library, Deprecations in Xcode 27 Beta. Apple podaje jeden radar dla całego bloku, 178191050, umieszczony za jego ostatnim punktem, a nie przy każdym z osobna. Źródło zdania „Minimalny obsługiwany deployment target na macOS dla biblioteki standardowej C++ został podniesiony do 11.0”, zmiany w multi{map,set}::find („kod polegający na tym, że find zwraca pierwszy element, przestanie działać; zamiast tego należy użyć lower_bound lub equal_range”) oraz wyjścia awaryjnego i terminu jego wygaśnięcia: „Ponieważ w niektórych przypadkach obejście tego może być trudne, w tym wydaniu udostępniono wyjście awaryjne: zdefiniowanie _LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND przywróci historyczną implementację tych operacji. To wyjście awaryjne zostanie usunięte w jednym z nadchodzących wydań (prawdopodobnie w kolejnym).” Zweryfikowane wobec JSON dokumentacji Apple 26 lipca 2026. ↩↩↩

  6. Zespół Clang, dokumentacja Clang Modules, Module Map Language. Źródło zdania „Każdy moduł musi mieć pojedynczą definicję”, reguły dotyczącej identyfikatora modułu i deklaracji extern module, zdania „Kwalifikator explicit można zastosować wyłącznie do podmodułu, czyli modułu zagnieżdżonego w innym module”, a także zasady odnajdywania map modułów po nazwie pliku module.modulemap, przy czym module.map jest wyszukiwany ze względu na zgodność wsteczną. Cytowane jako własna dokumentacja toolchaina dla języka map modułów, a nie jako dokument dla deweloperów Apple; clang od Apple wywodzi się z tej implementacji. ↩↩

  7. Testy autora na macOS 26.5.2 (build 25F84) z Xcode 26.6 (build 17F113), Apple clang 21.0.0, 26 lipca 2026. Wyjście poleceń odtworzone dosłownie. xcrun clang hello.c -o hello -Wl,-ld_classic wypisuje „ld: warning: -ld_classic is deprecated and will be removed in a future release” i kończy się kodem 0; podstawienie -Wl,-ld64 wypisuje identyczne ostrzeżenie z nazwą -ld_classic. xcrun ld -v raportuje PROJECT:ld-1267 oraz cytowane wyżej listy architektur. ld-classic jest obecny w /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld-classic, a obok niego leży strona man. Architektury SDK pochodzą z SDKSettings.plist: iPhoneOS 26.5 deklaruje arm64e i arm64, WatchOS 26.5 deklaruje arm64, arm64e i arm64_32. Liczby map modułów w SDK (1099 deklaracji najwyższego poziomu w usr/include, 205 w System/Library/Frameworks, w obu przypadkach zero duplikatów) pochodzą z uruchomienia opublikowanego wyżej polecenia na SDK iPhoneOS 26.5; 79 deklaracji extern module znajduje się w usr/include/module.modulemap, a 13 map modułów w tym samym katalogu otwiera kropkowany module Darwin.* (bank, Darwin_C, Darwin_Mach, Darwin_Mach_machine, Darwin_machine, Darwin_POSIX, Darwin_sys, device, mach_debug, net, netinet, netinet6 oraz uuid), które odczyt po pierwszym członie grupuje z prawdziwą deklaracją Darwin w Darwin.modulemap jako jedną kolizję obejmującą 14 plików. Zachowania pod Xcode 27 nie testowano, ponieważ Xcode 27 nie był zainstalowany na użytej maszynie. ↩↩↩↩↩↩↩↩↩↩

  8. Reprodukcja autora na tej samej maszynie i tym samym toolchainie, 26 lipca 2026. Dwa katalogi, z których każdy zawiera module.modulemap deklarujący module Widget, skompilowane przez swiftc z oboma katalogami na ścieżce wyszukiwania. -typecheck dawało error: redefinition of module 'Widget' wraz z note: previously defined here w 20 przebiegach na 20, kod wyjścia 1; usunięcie jednej ścieżki -I sprawiało, że ten sam plik kompilował się z kodem wyjścia 0. -scan-dependencies na tym samym wejściu w 20 przebiegach dało dziewięć zakończeń sygnałem 11 (SIGSEGV), pięć sygnałem 6 (SIGABRT) i sześć zakończeń ze statusem 0, przy czym ślady stosu z przebiegów zakończonych awarią prowadziły przez swift::ModuleDependencyScanner::performParallelClangModuleLookup. Osobno: dostarczona mapa modułu deklarująca SQLite3 (moduł obecny w SDK iPhoneOS 26.5), umieszczona na ścieżce wyszukiwania, przeszła typecheck z kodem wyjścia 0 i bez żadnej diagnostyki, podczas gdy plik wywołujący sqlite3_open z SDK przy tej samej konfiguracji zawiódł z komunikatem „error: cannot find ‘sqlite3_open’ in scope” i skompilował się czysto, gdy tylko dostarczony katalog usunięto ze ścieżki wyszukiwania. Wśród danych testowych znalazła się ścieżka ze spacją, aby zweryfikować opublikowane polecenie. Polecenie to porównuje nazwy pomiędzy plikami, więc nazwa zadeklarowana dwa razy w obrębie jednej mapy modułu z założenia leży poza jego zakresem. Nigdzie w tym artykule nie raportuję wyjścia z Xcode 27. Lipcowy tekst nie raportował żadnego wyjścia z Xcode 27; zawiera je druga aktualizacja z 15 września.13 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  9. Audyt autora obejmujący siedem projektów Xcode (Reps, Return, Banana List, Ace Citizenship, Water, ResumeGeni i Yawara) na macOS 26.5.2 z Xcode 26.6 (build 17F113), 26 lipca 2026. -ld_classic i ld64 nie występują w żadnym drzewie roboczym ani razu, a OTHER_LDFLAGS jest nieustawione w każdym projekcie; nie ma plików .xcconfig, nie ma Podfile, nie ma Carthage ani dostarczonych .framework czy .xcframework nigdzie we flocie. Wyszukiwanie objęło wyłącznie bieżące drzewa robocze, nie historię git ani zarchiwizowane logi kompilacji, więc wynik brzmi „nieobecne teraz”, a nie „nigdy nieużywane”. Cytowana dla projektu Reps linia SUPPORTED_PLATFORMS pochodzi z xcodebuild -showBuildSettings -configuration Release -sdk iphoneos. Sumy map modułów: przebadano 391, z czego 204 w katalogach siedmiu projektów i 187 w drzewach tych siedmiu projektów we współdzielonym ~/Library/Developer/Xcode/DerivedData (cały współdzielony katalog zawiera znacznie więcej, należących do innych projektów); wszystkie są wyjściem kompilacji, a w żadnym z siedmiu repozytoriów nie ma map modułów pisanych ręcznie ani dostarczonych. Pozorne duplikaty sprawdzono skrótami zawartości i okazało się, że oba generatory zachowują się inaczej. Piętnaście par nazw wygenerowanych przez Xcode w audytowanym drzewie ResumeGeni (wewnątrzprojektowy build/DerivedData), zapisanych raz w GeneratedModuleMaps-iphonesimulator/ i raz w plikach pośrednich targetu, jest identycznych bajt po bajcie 15 razy na 15, ponieważ Xcode zapisuje względną ścieżkę nagłówka. Liczba dotyczy drzewa, nie projektu: drzewo tej samej aplikacji we współdzielonym ~/Library/Developer/Xcode/DerivedData zawiera 16 par, a jego wariant Index.noindex — 22. Uogólnia się tu mechanizm, nie liczba. Kopie SwiftPM identyczne nie są: sześć plików GrappleCore i trzy GrappleRender w katalogach .build projektu Yawara mają odpowiednio sześć i trzy różne skróty oraz rozmiary od 171 do 185 bajtów, ponieważ każdy osadza bezwzględną ścieżkę do wygenerowanego -Swift.h we własnym katalogu kompilacji, a poza tym jest identyczny. Żadna nazwa modułu deklarowana w całej flocie nie występuje wśród 1318 odrębnych nazw modułów Clang najwyższego poziomu, jakie opublikowane polecenie znajduje po skierowaniu go na cały SDK iPhoneOS 26.5, z czego 1099 leży w usr/include, a 205 w System/Library/Frameworks; przecięcie z nazwami deklarowanymi przez flotę jest puste. To samo skanowanie raportuje zero duplikatów w całym SDK, co jest kontrolą negatywną, jakiej ta reguła wymaga. CryptoKit nie występuje na tej liście, ponieważ jest dostarczany jako framework wyłącznie Swiftowy, bez mapy modułu Clang, więc brak kolizji z Crypto wynika z tego, że SDK nie deklaruje modułu Clang o takiej nazwie, a nie z tego, że nazwę tę zajmuje CryptoKit. swift-crypto 4.4.0 dostarcza jedyne ręcznie pisane mapy modułów w rozwiązanym grafie zależności (CCryptoBoringSSL, CCryptoBoringSSLShims, CXKCP, CXKCPShims, po jednej deklaracji każda). Warianty SymbolKit — narzędziowy i targetowy — znajdują się we współdzielonym pakiecie lokalnym 941Kit, a nie w siedmiu aplikacjach. Liczniki plików źródłowych pomijają virtualenvy Python, i ta korekta miała znaczenie: naiwne zliczanie przypisało projektowi Reps pliki C i nagłówki, które w całości okazały się leżeć w .venv i site-packages i nie są kompilowane przez żaden target Xcode. Water nie ma lokalnego drzewa kompilacji, więc jego zerowa liczba map modułów odzwierciedla projekt niezbudowany, a nie zweryfikowany jako czysty graf kompilacji. Każdą z trzech pułapek fałszywej czystości potwierdzono bezpośrednio: timeout nie występuje w standardowym macOS i kończy się kodem 127; zsh rozwija nieujęte w cudzysłów --include=*.pbxproj i przerywa z „no matches found”; a xcrun --sdk iphoneos --show-sdk-path zwraca dowiązanie symboliczne, wobec którego find bez -H raportuje zero map modułów, podczas gdy find -H raportuje ich 266. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  10. Apple, Xcode 26 Release Notes. Przeszukane 26 lipca 2026 pod kątem ld_classic, ld64 oraz jakiegokolwiek wpisu opisującego klasyczny linker; nie występuje żaden, więc pomiędzy zapowiedzią wycofania w Xcode 16 a usunięciem w Xcode 27 nie ma żadnego powtórzenia komunikatu. ↩↩

  11. Apple, Xcode 27 Release Notes, Overview: „Xcode 27 beta 4 zawiera Swift 6.4 oraz SDK dla iOS 27, iPadOS 27, tvOS 27, watchOS 27, macOS 27 i visionOS 27.” Te same notatki odnotowują w sekcji Intel Deprecation (radar 162138432), że „Xcode 27 będzie się instalował i uruchamiał wyłącznie na Macach z układami Apple silicon”. Sprawdzone 26 lipca 2026. ↩

  12. Testy autora na macOS 26.6.2 (kompilacja 25G83) z Xcode 27.0 (kompilacja 27A266a), Apple clang 21.0.0 (clang-2100.3.34.2), 15 września 2026, z DEVELOPER_DIR wskazującym na pakiet Xcode 27. Wynik xcrun ld -v przytoczony wyżej dosłownie; ls w usr/bin toolchainu wypisuje ld i żadnego ld-classic. xcrun clang hello.c -o hello -Wl,-ld_classic z plikiem hello.c o treści int main(void){return 0;} wypisało „ld: warning: -ld_classic is no longer supported and will be ignored” i zakończyło się kodem 0; xcrun clang hello.c -o hello -Wl,-ld64 wypisało „ld: library ‘d64’ not found” oraz „clang: error: linker command failed with exit code 1 (use -v to see invocation)” i zakończyło się kodem 1 bez pliku wyjściowego. Finalne Xcode 27 Release Notes, pobrane tego samego dnia przez JSON dokumentacji Apple, zawierają wpis o wycofaniu w sekcji Linking (165165518) i wpis o skanerze zależności w sekcji Swift Compiler (136303612) w brzmieniu cytowanym w tym artykule, a Overview brzmi teraz „Xcode 27 includes Swift 6.4 and SDKs for iOS 27, iPadOS 27, tvOS 27, watchOS 27, macOS 27, and visionOS 27.” ↩↩↩↩↩

  13. Testy autora na macOS 26.6.2 (kompilacja 25G83) z Xcode 27.0 (kompilacja 27A266a), Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1), 15 września 2026, względem SDK iPhoneSimulator 27.0 z -target arm64-apple-ios27.0-simulator. Dane testowe: A/module.modulemap i B/module.modulemap, każdy deklarujący module Widget { header "widget.h" export * }, main.swift importujący Widget oraz Vendor/module.modulemap deklarujący module SQLite3 { header "shim.h" export * }. Z -I A -I B: swiftc -typecheck main.swift zakończyło się kodem 1 w 20 przebiegach na 20 z „error: redefinition of module ‘Widget’” i „note: previously defined here”; swiftc -scan-dependencies main.swift zakończyło się kodem 0 w 20 przebiegach na 20, w 20 na 20 wypisało na stderr ten sam błąd i notę, a w 20 na 20 zapisało graf, którego wpis Widget niesie moduleMapPath wskazujący A/module.modulemap. Z samym -I A oba polecenia zakończyły się kodem 0. Z -I Vendor: swiftc -scan-dependencies pliku importującego SQLite3 zakończyło się kodem 0 z pustym stderr w 20 przebiegach na 20 i rozwiązało SQLite3 do Vendor/module.modulemap; -typecheck tego importu zakończyło się kodem 0; -typecheck pliku wywołującego sqlite3_open(":memory:", &db) zakończyło się kodem 1 z „error: cannot find ‘sqlite3_open’ in scope”, a bez argumentu -I Vendor kodem 0. SDK odsyła do modułu w usr/include/module.modulemap w linii 7, extern module SQLite3 "SQLite3.modulemap", a definiuje go w usr/include/SQLite3.modulemap jako module SQLite3 [system]; bez -I Vendor skanowanie zakończyło się kodem 0 z pustym stderr i rozwiązało SQLite3 do tego pliku. Po odwróceniu kolejności obu argumentów ścieżki wyszukiwania, -I B -I A, skanowanie nadal kończyło się kodem 0, błąd przeniósł się do A/module.modulemap, nota do B/module.modulemap, a graf rozwiązał Widget do B/module.modulemap. ↩↩↩↩↩↩↩↩↩

Powiązane artykuły

Makro @State: co przestaje się kompilować w Xcode 27

Xcode 27 przepisuje @State ze SwiftUI na makro Swift. Łamie to zgodność przy zmianie toolchainu, nie deployment target; …

17 min czytania

Xcode 27 porzuca Intela: co się kończy, a co nadal da się wydać

Xcode 27 wymaga Apple silicon, nadal wydaje aplikacje Universal do macOS 12 i po cichu usuwa x86_64 z ARCHS_STANDARD prz…

21 min czytania

Światło w pixel art: dzień, noc, pogoda i cienie

Jak Pokémon Crystal i Emerald prowadzą dzień, deszcz i cień, zmierzone w ich kodzie źródłowym, oraz brief na dzień, pogo…

133 min czytania