Damit sich ein natives macOS-Programm verknüpfen lässt, muss jeder ausgewählte Bibliotheks-Slice zur CPU-Architektur und Apple-Plattform passen und eine kompatible Sprach-ABI haben. Diese Anleitung zeigt, wie Sie eine kleine C-Bibliothek erstellen und untersuchen, die von einem C++-Desktop-Kommandozeilenprogramm verwendet wird.
Die konkrete Aufgabe besteht darin, aus lokalem Quellcode eine macOS- ausführbare Datei zu erzeugen und eine inkompatible native Abhängigkeit zu diagnostizieren. Das Beispiel verwendet synthetische Ganzzahlarithmetik, benötigt keine heruntergeladenen Bibliotheken und hält architekturspezifische Ausgaben getrennt.
Was muss übereinstimmen, bevor sich eine native Bibliothek verknüpfen lässt?
Prüfen Sie drei Dinge unabhängig voneinander: Architektur, Plattform und die Binärschnittstelle zwischen Sprachen. Die Zielspezifikation von Clang umfasst mehr als nur den Prozessor; Ziel und Umgebung beeinflussen die Codegenerierung und die Auswahl der Bibliotheken. Auch Abhängigkeiten müssen für das vorgesehene Ziel erstellt werden. Cross-Kompilierung mit Clang.
| Prüfung | Frage für diese macOS-Aufgabe | Bedeutung einer Abweichung |
|---|---|---|
| CPU-Architektur | Enthält die Abhängigkeit den Slice arm64 oder x86_64, der verknüpft wird? |
Beschaffen oder erstellen Sie die fehlende Architektur. |
| Apple-Plattform | Wurde dieser Slice für natives macOS erstellt? | Beschaffen Sie einen macOS-Build; ein iOS- oder Simulator-Build ist eine separate Variante. |
| Sprach-ABI | Stimmen Deklarationen, Aufrufkonventionen und erforderliche Laufzeitumgebungen überein? | Korrigieren Sie die Schnittstelle oder verwenden Sie den unterstützten Interoperabilitätsweg der Sprache. |
Eine Architekturliste kann die Plattformfrage nicht beantworten. Apples Mach-O-Format speichert CPU-Informationen getrennt von den Plattform-Build-Informationen und definiert eigene Plattformkennungen für macOS, iOS, Mac Catalyst und den iOS-Simulator. Eine Abhängigkeit, die nur als arm64 beschrieben wird, muss daher genauer untersucht werden. Definitionen des Mach-O-Formats von Apple.
Für dieses Beispiel lautet der vorgesehene Build-Vertrag: natives macOS, macOS-13.0-Bereitstellungsziel und ein Build pro Architektur. Die C-Bibliothek stellt eine C-Schnittstelle bereit; die ausführbare Datei verwendet C++17. Die Version ist eine beispielhafte Wahl und keine allgemeingültige Mindestversion für Mac-Software.
Ein vollständiges macOS-Beispiel mit expliziten Zielen erstellen
Verwenden Sie einen Mac mit einer aktuellen Xcode-Installation, die für die Kommandozeilenentwicklung ausgewählt ist. Apple beschreibt xcrun als Mechanismus, um Werkzeuge in der ausgewählten Entwicklerinstallation zu finden oder auszuführen. Prüfen Sie die Auswahl, bevor Sie Änderungen am Quellcode untersuchen. Xcode-FAQ: Erstellen über die Kommandozeile.
Die folgenden Befehle sind für Bash unter macOS gedacht. Sie wurden geprüft, aber hier nicht ausgeführt, da die Forschungsumgebung Linux ist und keine Apple-Werkzeugkette enthält.
xcode-select --print-path
xcrun --sdk macosx --find clang
xcrun --sdk macosx --find clang++
xcrun --sdk macosx --show-sdk-path
Schlagen diese Prüfungen fehl, beheben Sie zuerst die Auswahl oder Installation der Entwicklerwerkzeuge. Für den Build werden neben einem Compiler auch das macOS-SDK benötigt. Ein SDK stellt die Plattform-Header und Bibliotheken bereit, gegen die das Produkt erstellt wird. Referenz zu Xcode-Build-Einstellungen: Basis-SDK.
Quelldateien erstellen
Führen Sie diesen Einrichtungsblock in Bash aus. Er erstellt ein neues temporäres Verzeichnis und gibt dessen Speicherort aus, sodass vorhandene Projektdateien unberührt bleiben. Führen Sie die späteren Blöcke in derselben Shell aus.
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
Der Header deklariert C-Sprachbindung, wenn ihn eine C++-Übersetzungseinheit einbindet. So wird verhindert, dass der C++-Aufrufer einen anders dekorierten C++-Funktionsnamen anfordert. Die C++-FAQ empfiehlt außerdem, den C++-Compiler den abschließenden Link-Schritt steuern zu lassen, damit die benötigten Bibliotheken eingebunden werden. Standard-C++-FAQ: C und C++ mischen.
Jede Architektur kompilieren, archivieren und verknüpfen
Der folgende Block erstellt beide macOS-Varianten. Für jede Architektur werden bei der Kompilierung der Bibliothek und beim Verknüpfen der ausführbaren Datei dasselbe Ziel und SDK angegeben. Clang beschreibt -c als Option, die eine Objektdatei erzeugt; ohne eine Option zur Auswahl der Build-Phase fährt der Treiber mit dem Verknüpfen fort. Clang-Anleitung für Compilerbefehle.
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
Hier legt -target die Architektur und das macOS-Bereitstellungsziel fest, während -isysroot das SDK-Stammverzeichnis angibt. Dies sind getrennte Eingaben. Der explizite Archivpfad macht außerdem die ausgewählte Abhängigkeit im Befehl sichtbar. Clang-Anleitung zur Cross-Kompilierung, Referenz zu Clang-Kommandozeilenargumenten.
Der Befehl ar rcs packt das kompilierte Objekt in ein statisches Archiv und fügt einen Symbolindex hinzu. Beim Archivieren werden vorhandene Objekte gepackt; sie werden dadurch nicht für ein anderes Ziel kompiliert. LLVM dokumentiert die Archivoperationen und ihre Kompatibilität mit anderen ar-Implementierungen. LLVM-Dokumentation zum Archivierer.
Die Beispielwerte 3 und 4 sollten Tiles: 12 ergeben. Diese kleine Funktion dient nur als Verknüpfungsbeispiel; ihre Schnittstelle definiert kein Verhalten für beliebige Abmessungen, deren Produkt den Wertebereich von int überschreitet.
Wie lassen sich Architektur, Plattform und Symbole untersuchen?
Untersuchen Sie die tatsächliche ausführbare Datei, Bibliothek oder Objektdatei. Apple dokumentiert lipo -archs zum Auflisten von Architekturen und weist darauf hin, dass sich die ausführbare Datei einer App im Verzeichnis Contents/MacOS/ ihres Bundles befindet. Universelle macOS-Binärdatei erstellen.
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
Die ersten beiden Befehle sollten die jeweiligen Architekturen angeben. Prüfen Sie die Plattform, indem Sie die von otool -l ausgegebenen Load Commands untersuchen; LLVMs kompatibles Werkzeug llvm-otool dokumentiert dieselbe Option. LLVM-Werkzeug zum Anzeigen von Mach-O-Dateien.
Suchen Sie nach LC_BUILD_VERSION. Das Feld platform sollte macOS angeben, und minos enthält die Mindestversion des Betriebssystems. Das Feld sdk ist davon getrennt. Bei älteren Binärdateien wird stattdessen manchmal ein LC_VERSION_MIN_*-Befehl verwendet. Apples Formatdefinitionen dokumentieren beide Darstellungen, einschließlich der macOS-Plattformkennung 1. Mach-O-Strukturen für Build-Versionen.
Untersuchen Sie bei einer statischen Bibliothek die enthaltenen Objektdateien, statt den Archiv-Wrapper als Beleg für Plattformkompatibilität zu betrachten. Dieses Beispiel behält tile.o zu diesem Zweck bei. Bei einem unbekannten Archiv können Sie mit xcrun ar t dessen Mitglieder auflisten und sie in einem separaten Arbeitsverzeichnis untersuchen. LLVM dokumentiert das Auflisten und Extrahieren von Archiven. LLVM-Dokumentation zum Archivierer.
Prüfen Sie zuletzt die Symbole. Die Beispielbibliothek sollte auf Mach-O _tile_total definieren. In der herkömmlichen Ausgabe von nm kennzeichnet T ein globales Codesymbol und U einen undefinierten Verweis. Eine Definition und ein Verweis sind unterschiedliche Belege. LLVM-Dokumentation zu Symboltabellen.
Nutzen Sie diese Diagnosesequenz, um den nächsten Schritt auszuwählen:
- Der erforderliche CPU-Slice fehlt: Erstellen Sie die Abhängigkeit für diese Architektur neu oder beschaffen Sie die passende Binärdatei.
- Die CPU stimmt überein, aber die Plattform nicht: Beschaffen Sie eine native macOS-Variante. Eine Änderung der Architektur des Aufrufers stellt keine Plattformkompatibilität her.
- CPU und Plattform stimmen überein, aber die Funktion ist nicht aufgelöst: Vergleichen Sie die deklarierte Funktion mit dem exportierten Symbol der Bibliothek und bestätigen Sie, dass das vorgesehene Archiv im Link-Befehl erscheint.
- Die Namen unterscheiden sich zwischen C und C++: Korrigieren Sie die Sprachbindung im gemeinsamen Header und erstellen Sie dann den Aufrufer neu.
- Die Bereitstellungsversionen unterscheiden sich: Prüfen Sie die Mindestversion der Abhängigkeit und erstellen Sie sie, sofern unterstützt, für das vorgesehene Bereitstellungsziel neu oder erhöhen Sie bewusst die Anforderung der Anwendung.
Diese Sequenz ist eine Entscheidungshilfe, die aus der obigen Dokumentation zu Zielen, Mach-O und Verknüpfung abgeleitet wurde. Sie unterscheidet fehlenden Maschinencode von inkompatiblen Plattformmetadaten und Sprachschnittstellenproblemen, sodass jeder erneute Build auf einen konkreten Befund reagiert.
Was ändert sich, wenn Swift die native Bibliothek aufruft?
Eine passende CPU und Plattform machen nicht jede Sprachschnittstelle aufrufbar. Swift unterstützt standardmäßig die C-Interoperabilität; direkte C++-Interoperabilität muss aktiviert werden und unterstützt eine dokumentierte Teilmenge der C++-Funktionen. Der C-Header in diesem Beispiel bietet eine unterstützte Schnittstellenoption, sofern der Swift-Build ihn über den konfigurierten Header- oder Modulmechanismus importiert. Swift und C++ mischen.
Für eine direkte C++-Schnittstelle dokumentiert Swift -cxx-interoperability-mode=default für Kommandozeilen-Builds, Clang-Module zum Importieren von Headern und -Xcc zum Übergeben von C++-Importer-Einstellungen. Die Importkonfiguration und die Verknüpfung der kompilierten Implementierung sind getrennte Build-Anforderungen. Gemischtsprachige Swift- und C++-Projekte einrichten.
Beachten Sie die dokumentierten Einschränkungen: C++-Interoperabilität erfordert Swift 5.9 oder neuer und C++14 oder neuer. Gemischter Swift- und C++-Code muss dieselbe C++-Standardbibliothek verwenden; auf Apple-Plattformen ist dies libc++. Aufrufe von Swift aus C++ erfordern die generierte Schnittstelle und einen unterstützten Clang-Compiler. Importierte C++-Referenztypen haben außerdem eine zusätzliche Bereitstellungsanforderung: macOS 13.3. Prüfen Sie die genaue API anhand der aktuellen Kompatibilitätsdokumentation, bevor Sie diesen Weg wählen. Unterstützte Funktionen und Einschränkungen der C++-Interoperabilität.
Wann sollten Sie eine universelle Binärdatei oder ein XCFramework verwenden?
Führen Sie für diese ausschließlich für macOS bestimmte ausführbare Datei die beiden fertigen macOS-Builds zu einer universellen Binärdatei zusammen. Apple dokumentiert, wie jede Architektur kompiliert und verknüpft wird, bevor die resultierenden ausführbaren Dateien mit lipo zusammengeführt werden. Universelle macOS-Binärdatei erstellen.
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
Auf einem unterstützten Mac mit macOS 13.0 oder neuer sollte der letzte Befehl Tiles: 12 ausgeben und erfolgreich abgeschlossen werden. Beide Slices zu erstellen ist etwas anderes, als beide zu testen: Ein Intel-Mac kann den Slice arm64 nicht ausführen. Anleitung von Apple zu universellen Binärdateien.
Verwenden Sie für eine Bibliothek, die mehrere Apple-Plattformen oder Plattformvarianten unterstützt, ein XCFramework, um separat erstellte Varianten zu bündeln. Apple weist Entwickler ausdrücklich an, statische iOS- und iOS-Simulator-Bibliotheken getrennt zu halten, und ermöglicht, native macOS- und Mac-Catalyst-Varianten in dasselbe XCFramework aufzunehmen. Für Xcode-Archivierungsabläufe wird in der dokumentierten Vorgehensweise ein Ziel verwendet, um Plattform und SDK auszuwählen. Ein Framework-Bundle für mehrere Plattformen erstellen.
Halten Sie bei Ihrem nächsten Verknüpfungsfehler unter macOS das vorgesehene Ziel fest, untersuchen Sie Architektur und Plattform der ausgewählten Abhängigkeit und prüfen Sie anschließend die Sprachschnittstelle. Bewahren Sie diese Befunde zusammen mit dem genauen Link-Befehl auf. So erhalten Sie eine konkrete Grundlage, um die passende Binärdatei auszuwählen oder sie aus dem Quellcode neu zu erstellen.
Quellen
Cross-Kompilierung mit Clang — LLVM-Projekt.
Referenz zu Build-Einstellungen — Apple.
clang — der Clang-Compiler für C, C++ und Objective-C — LLVM-Projekt.
Referenz zu Clang-Kommandozeilenargumenten — LLVM-Projekt.
Standard-C++: C und C++ mischen — Standard-C++-Stiftung.
llvm-ar — LLVM-Archivierer — LLVM-Projekt.
llvm-otool — Werkzeug zum Anzeigen von Mach-O-Dateien — LLVM-Projekt.
llvm-nm — LLVM-Bitcode und Symboltabelle von Objektdateien auflisten — LLVM-Projekt.
Swift und C++ mischen — Swift.org.
Gemischtsprachige Swift- und C++-Projekte einrichten — Swift.org.
Unterstützte Funktionen und Einschränkungen der C++-Interoperabilität — Swift.org.
Ein Framework-Bundle für mehrere Plattformen erstellen — Apple.