Aby zlinkować natywny program macOS, każdy wybrany wariant biblioteki musi być zgodny z architekturą CPU i platformą Apple oraz mieć zgodny ABI języka. W tym przewodniku pokazano, jak zbudować i sprawdzić małą bibliotekę C używaną przez narzędzie wiersza poleceń w języku C++ na komputerze.
Celem jest utworzenie pliku wykonywalnego macOS z lokalnego kodu źródłowego i zdiagnozowanie niezgodnej zależności natywnej. Przykład wykorzystuje syntetyczne działania na liczbach całkowitych, nie wymaga pobierania bibliotek i przechowuje wyniki dla poszczególnych architektur osobno.
Co musi być zgodne, zanim można zlinkować bibliotekę natywną?
Sprawdź niezależnie trzy kwestie: architekturę, platformę i interfejs binarny między językami. Specyfikacja celu Clanga obejmuje więcej niż procesor; cel i środowisko wpływają na generowanie kodu oraz wybór bibliotek. Zależności również muszą być zbudowane dla zamierzonego celu. Kompilacja skrośna za pomocą Clanga.
| Sprawdzenie | Pytanie dotyczące tego zadania dla macOS | Znaczenie niezgodności |
|---|---|---|
| Architektura CPU | Czy zależność zawiera wariant arm64 lub x86_64, który jest linkowany? |
Uzyskaj lub zbuduj brakującą architekturę. |
| Platforma Apple | Czy ten wariant został zbudowany dla natywnego macOS? | Uzyskaj kompilację dla macOS; kompilacja dla iOS lub symulatora to osobny wariant. |
| ABI języka | Czy deklaracje, konwencje wywołań i wymagane środowiska uruchomieniowe są zgodne? | Popraw interfejs albo użyj obsługiwanej przez język ścieżki współdziałania. |
Lista architektur nie odpowiada na pytanie o platformę. Format Mach-O firmy Apple zapisuje informacje o CPU oddzielnie od informacji o platformie użytej podczas kompilacji i definiuje odrębne identyfikatory platform dla macOS, iOS, Mac Catalyst i symulatora iOS. Zależność opisana wyłącznie jako arm64 wymaga zatem dalszej inspekcji. Definicje formatu Mach-O firmy Apple.
W tym przykładzie założenia kompilacji to natywny macOS, minimalna wersja wdrożeniowa macOS 13.0 i osobna kompilacja dla każdej architektury. Biblioteka C udostępnia interfejs C, a plik wykonywalny używa C++17. Wersja jest przykładowym wyborem, a nie powszechnym minimum dla oprogramowania na Maca.
Zbuduj kompletny przykład dla macOS z jawnymi celami
Użyj Maca z aktualną instalacją Xcode wybraną do pracy z wiersza poleceń. Apple opisuje xcrun jako mechanizm wyszukiwania narzędzi w wybranej instalacji deweloperskiej lub ich uruchamiania. Przed badaniem zmian w kodzie źródłowym sprawdź wybraną instalację. Często zadawane pytania dotyczące kompilacji z wiersza poleceń za pomocą Xcode.
Poniższe polecenia są przeznaczone dla powłoki Bash w systemie macOS. Zostały sprawdzone, ale nie zostały tutaj wykonane, ponieważ środowisko badawcze to Linux bez narzędzi Apple.
xcode-select --print-path
xcrun --sdk macosx --find clang
xcrun --sdk macosx --find clang++
xcrun --sdk macosx --show-sdk-path
Jeśli te sprawdzenia się nie powiodą, najpierw rozwiąż problem wyboru lub instalacji narzędzi deweloperskich. Do kompilacji potrzebny jest zarówno SDK macOS, jak i kompilator. SDK udostępnia nagłówki i biblioteki platformy, względem których budowany jest produkt. Dokumentacja ustawień kompilacji Xcode: Base SDK.
Utwórz pliki źródłowe
Uruchom ten blok przygotowawczy w powłoce Bash. Tworzy on nowy katalog tymczasowy i wyświetla jego lokalizację, więc istniejące pliki projektu pozostają nietknięte. Kontynuuj, uruchamiając kolejne bloki w tej samej powłoce.
set -euo pipefail
example_dir="$(mktemp -d "${TMPDIR:-/tmp}/mac-link-demo.XXXXXX")"
cd "$example_dir"
printf 'Example directory: %s\n' "$example_dir"
mkdir -p include src build/arm64 build/x86_64
cat > include/tile.h <<'EOF'
#ifndef TILE_H
#define TILE_H
#ifdef __cplusplus
extern "C" {
#endif
int tile_total(int rows, int columns);
#ifdef __cplusplus
}
#endif
#endif
EOF
cat > src/tile.c <<'EOF'
#include "tile.h"
int tile_total(int rows, int columns)
{
return rows * columns;
}
EOF
cat > src/main.cpp <<'EOF'
#include <cstdio>
#include "tile.h"
int main()
{
const int total = tile_total(3, 4);
std::printf("Tiles: %d\n", total);
return total == 12 ? 0 : 1;
}
EOF
Nagłówek deklaruje powiązanie z językiem C, gdy dołącza go jednostka translacji C++. Dzięki temu wywołujący kod C++ nie żąda nazwy funkcji C++ z innym manglingiem. FAQ C++ zaleca również, aby ostateczne linkowanie prowadził kompilator C++, dzięki czemu dołączane są wymagane biblioteki. FAQ standardu C++: Jak łączyć kod C i C++.
Skompiluj, zarchiwizuj i zlinkuj każdą architekturę
Poniższy blok buduje oba warianty dla macOS. Dla każdej architektury przekazuje ten sam cel i SDK zarówno podczas kompilowania biblioteki, jak i linkowania pliku wykonywalnego. Dokumentacja Clanga opisuje -c jako opcję tworzącą plik obiektowy; bez opcji wyboru etapu sterownik przechodzi do linkowania. Przewodnik po poleceniach kompilatora Clang.
mac_sdk="$(xcrun --sdk macosx --show-sdk-path)"
for cpu in arm64 x86_64; do
mac_target="${cpu}-apple-macos13.0"
xcrun --sdk macosx clang \
-target "$mac_target" -isysroot "$mac_sdk" \
-std=c17 -Wall -Wextra -Werror \
-I include -c src/tile.c \
-o "build/$cpu/tile.o"
xcrun ar rcs "build/$cpu/libtile.a" "build/$cpu/tile.o"
xcrun --sdk macosx clang++ \
-target "$mac_target" -isysroot "$mac_sdk" \
-std=c++17 -stdlib=libc++ -Wall -Wextra -Werror \
-I include src/main.cpp "build/$cpu/libtile.a" \
-o "build/$cpu/tile-demo"
done
Tutaj -target wybiera architekturę i minimalną wersję wdrożeniową macOS, a -isysroot wskazuje katalog główny SDK. To odrębne parametry. Jawna ścieżka archiwum pozwala też zobaczyć w poleceniu, która zależność jest wybierana. Wskazówki Clanga dotyczące kompilacji skrośnej, Dokumentacja argumentów wiersza poleceń Clanga.
Polecenie ar rcs pakuje skompilowany plik obiektowy do archiwum statycznego i tworzy indeks symboli. Archiwizacja pakuje istniejące pliki obiektowe; nie kompiluje ich dla innego celu. LLVM dokumentuje operacje na archiwach i ich zgodność z innymi implementacjami ar. Dokumentacja archiwizatora LLVM.
Dane wejściowe przykładu 3 i 4 powinny dać wynik Tiles: 12. Ta niewielka funkcja służy wyłącznie jako przykład linkowania; jej interfejs nie określa zachowania dla dowolnych wymiarów, których iloczyn przekracza zakres typu int.
Jak sprawdzić architekturę, platformę i symbole?
Sprawdź rzeczywisty plik wykonywalny, bibliotekę lub plik obiektowy. Apple dokumentuje lipo -archs jako polecenie wyświetlające architektury i informuje, że plik wykonywalny aplikacji znajduje się wewnątrz jej pakietu, w katalogu Contents/MacOS/. Budowanie uniwersalnego pliku binarnego macOS.
xcrun lipo -archs build/arm64/tile-demo
xcrun lipo -archs build/x86_64/tile-demo
xcrun otool -l build/arm64/tile.o
xcrun otool -l build/arm64/tile-demo
xcrun nm -g build/arm64/libtile.a
Dwa pierwsze polecenia powinny wskazać odpowiednie architektury. Aby sprawdzić platformę, przejrzyj polecenia ładowania wyświetlone przez otool -l; zgodne z nim narzędzie LLVM llvm-otool dokumentuje tę samą opcję. Narzędzie LLVM do wyświetlania zawartości Mach-O.
Szukaj LC_BUILD_VERSION. Jego pole platform powinno wskazywać macOS, a minos zapisuje minimalną wersję systemu operacyjnego. Pole sdk jest odrębne. Niektóre starsze pliki binarne używają zamiast tego polecenia LC_VERSION_MIN_*. Definicje formatu Apple opisują obie reprezentacje, w tym identyfikator platformy macOS 1. Struktury wersji kompilacji Mach-O.
W przypadku biblioteki statycznej sprawdź obiektowe pliki składowe, zamiast traktować opakowanie archiwum jako dowód zgodności platformy. W tym przykładzie zachowano tile.o właśnie w tym celu. W przypadku nieznanego archiwum użyj xcrun ar t, aby wyświetlić jego składowe, a następnie sprawdź je w osobnym katalogu roboczym. LLVM dokumentuje operacje wyświetlania i rozpakowywania archiwów. Dokumentacja archiwizatora LLVM.
Na koniec sprawdź symbole. Przykładowa biblioteka powinna definiować _tile_total w Mach-O. W tradycyjnym wyniku nm symbol T oznacza globalny symbol kodu, a U — niezdefiniowane odwołanie. Definicja i odwołanie to różne dowody. Dokumentacja tabeli symboli LLVM.
Skorzystaj z tej sekwencji diagnostycznej, aby wybrać następne działanie:
- Brakuje wymaganej architektury CPU: przebuduj zależność dla tej architektury lub uzyskaj zgodny plik binarny.
- Architektura CPU jest zgodna, ale platforma jest inna: uzyskaj wariant dla natywnego macOS. Zmiana architektury programu wywołującego nie zapewnia zgodności platformy.
- Architektura CPU i platforma są zgodne, ale funkcja pozostaje nierozwiązana: porównaj deklarację funkcji z symbolem eksportowanym przez bibliotekę i potwierdź, że zamierzone archiwum znajduje się w poleceniu linkowania.
- Nazwy różnią się między C i C++: popraw powiązanie z językiem we wspólnym nagłówku, a następnie przebuduj program wywołujący.
- Wersje wdrożeniowe są różne: sprawdź minimalną wersję zależności i, jeśli to obsługiwane, przebuduj ją dla zamierzonego celu wdrożeniowego albo świadomie podnieś wymagania aplikacji.
Ta sekwencja jest pomocą w podejmowaniu decyzji opracowaną na podstawie opisanej wyżej dokumentacji celów, Mach-O i linkowania. Pozwala odróżnić brakujący kod maszynowy od niezgodnych metadanych platformy i problemów z interfejsem językowym, dzięki czemu każda kolejna kompilacja odpowiada konkretnemu ustaleniu.
Co się zmienia, gdy Swift wywołuje bibliotekę natywną?
Zgodna architektura CPU i platforma nie oznaczają, że można wywołać każdy interfejs językowy. Swift domyślnie obsługuje współdziałanie z C; bezpośrednie współdziałanie z C++ trzeba włączyć, a ponadto obsługuje ono udokumentowany podzbiór funkcji C++. Nagłówek C z tego przykładu udostępnia jeden z obsługiwanych interfejsów, pod warunkiem że kompilacja Swifta importuje go za pomocą skonfigurowanego nagłówka lub mechanizmu modułów. Łączenie Swifta i C++.
W przypadku bezpośredniego interfejsu C++ Swift dokumentuje -cxx-interoperability-mode=default do kompilacji z wiersza poleceń, moduły Clanga do importowania nagłówków oraz -Xcc do przekazywania ustawień importera C++. Konfiguracja importu i linkowanie skompilowanej implementacji pozostają odrębnymi wymaganiami kompilacji. Konfigurowanie projektów Swift i C++ z kodem w wielu językach.
Przestrzegaj udokumentowanych ograniczeń: współdziałanie z C++ wymaga Swifta 5.9 lub nowszego oraz C++14 lub nowszego. Kod łączący Swift i C++ musi korzystać z tej samej biblioteki standardowej C++; na platformach Apple jest to libc++. Wywoływanie Swifta z C++ wymaga wygenerowanego interfejsu i obsługiwanego kompilatora Clang. Importowane typy referencyjne C++ wymagają też wersji wdrożeniowej macOS 13.3 lub nowszej. Przed wyborem tej ścieżki sprawdź konkretne API w aktualnej dokumentacji zgodności. Obsługiwane funkcje i ograniczenia współdziałania z C++.
Kiedy używać pliku uniwersalnego lub XCFramework?
W przypadku tego pliku wykonywalnego przeznaczonego wyłącznie dla macOS połącz dwie ukończone kompilacje dla macOS w jeden plik uniwersalny. Apple opisuje kompilowanie i linkowanie każdej architektury przed połączeniem wynikowych plików wykonywalnych za pomocą lipo. Budowanie uniwersalnego pliku binarnego macOS.
xcrun lipo -create \
build/arm64/tile-demo \
build/x86_64/tile-demo \
-output build/tile-demo
xcrun lipo -archs build/tile-demo
./build/tile-demo
Na obsługiwanym Macu z systemem macOS 13.0 lub nowszym ostatnie polecenie powinno wypisać Tiles: 12 i zakończyć się powodzeniem. Zbudowanie obu wariantów różni się od ich przetestowania: Mac z procesorem Intel nie uruchomi wariantu arm64. Wskazówki Apple dotyczące plików binarnych uniwersalnych.
W przypadku biblioteki obsługującej kilka platform Apple lub wariantów platformy użyj XCFramework do spakowania osobno zbudowanych wariantów. Apple wyraźnie zaleca oddzielne przechowywanie bibliotek statycznych dla iOS i symulatora iOS, a w tym samym XCFramework można umieścić warianty dla natywnego macOS i Mac Catalyst. W przypadku kompilacji archiwizowanych w Xcode udokumentowana metoda wykorzystuje miejsce docelowe do wyboru platformy i SDK. Tworzenie wieloplatformowego pakietu binarnego frameworka.
Przy następnym błędzie linkowania na macOS zapisz zamierzony cel, sprawdź architekturę i platformę wybranej zależności, a następnie zweryfikuj interfejs językowy. Zachowaj te ustalenia wraz z dokładnym poleceniem linkowania. Dzięki temu wybór właściwego pliku binarnego lub ponowna kompilacja z kodu źródłowego będą oparte na konkretnych danych.
Źródła
Kompilacja skrośna za pomocą Clanga — projekt LLVM.
Często zadawane pytania dotyczące kompilacji z wiersza poleceń za pomocą Xcode — Apple.
Dokumentacja ustawień kompilacji — Apple.
clang — kompilator Clang języków C, C++ i Objective-C — projekt LLVM.
Dokumentacja argumentów wiersza poleceń Clanga — projekt LLVM.
Standard C++: Jak łączyć kod C i C++ — fundacja Standard C++.
llvm-ar — archiwizator LLVM — projekt LLVM.
llvm-otool — narzędzie do wyświetlania zawartości Mach-O — projekt LLVM.
llvm-nm — wyświetlanie bitcode LLVM i tabeli symboli pliku obiektowego — projekt LLVM.
Definicje formatu Mach-O: loader.h — Apple.
Łączenie Swifta i C++ — Swift.org.
Konfigurowanie projektów Swift i C++ z kodem w wielu językach — Swift.org.
Obsługiwane funkcje i ograniczenia współdziałania z C++ — Swift.org.
Tworzenie wieloplatformowego pakietu binarnego frameworka — Apple.