Voor het linken van een native macOS-programma moet elke geselecteerde library-slice overeenkomen met de CPU-architectuur en het Apple-platform, en een compatibele taal-ABI hebben. Deze handleiding laat zien hoe je een kleine C-library bouwt en inspecteert die wordt gebruikt door een C++-desktoptool voor de opdrachtregel.
De concrete taak is om vanuit lokale broncode een macOS-uitvoerbaar bestand te maken en een incompatibele native dependency te diagnosticeren. Het voorbeeld gebruikt synthetische gehele-getallenrekenkunde, vereist geen gedownloade libraries en houdt architectuurspecifieke uitvoer gescheiden.
Wat moet overeenkomen voordat een native library kan worden gelinkt?
Controleer drie zaken afzonderlijk: architectuur, platform en de binaire interface tussen talen. De doelspecificatie van Clang omvat meer dan alleen de processor; het target en de omgeving beïnvloeden de codegeneratie en de selectie van libraries. Dependencies moeten ook voor het beoogde target zijn gebouwd. Crosscompilatie met Clang.
| Controle | Vraag voor deze macOS-taak | Wat een afwijking betekent |
|---|---|---|
| CPU-architectuur | Bevat de dependency de arm64- of x86_64-slice die wordt gelinkt? |
Verkrijg of bouw de ontbrekende architectuur. |
| Apple-platform | Is die slice gebouwd voor native macOS? | Verkrijg een macOS-build; een iOS- of Simulator-build is een aparte variant. |
| Taal-ABI | Komen declaraties, aanroepconventies en vereiste runtimes overeen? | Corrigeer de interface of gebruik het interoperabiliteitspad dat de taal ondersteunt. |
Een architectuuroverzicht geeft geen antwoord op de platformvraag. Mach-O, het binaire formaat van Apple, slaat CPU-informatie apart op van platforminformatie en definieert verschillende platformidentificaties voor macOS, iOS, Mac Catalyst en iOS Simulator. Een dependency die alleen als arm64 wordt beschreven, moet daarom verder worden onderzocht. Mach-O-formaatdefinities van Apple.
Voor dit voorbeeld is het beoogde buildcontract native macOS, een macOS 13.0-implementatiedoel en één build per architectuur. De C-library biedt een C-interface; het uitvoerbare bestand gebruikt C++17. De versie is een illustratieve keuze, geen universele minimumversie voor Mac-software.
Bouw een volledig macOS-voorbeeld met expliciete targets
Gebruik een Mac waarop een actuele Xcode-installatie is geselecteerd voor commandoregelontwikkeling. Apple documenteert xcrun als het mechanisme om tools in de geselecteerde ontwikkelaarsinstallatie te vinden of uit te voeren. Controleer de selectie voordat je wijzigingen in de broncode onderzoekt. Veelgestelde vragen over bouwen vanaf de opdrachtregel met Xcode.
De onderstaande opdrachten zijn bedoeld voor Bash op macOS. Ze zijn gecontroleerd, maar hier niet uitgevoerd omdat de onderzoeksomgeving Linux is en geen Apple-toolchain heeft.
xcode-select --print-path
xcrun --sdk macosx --find clang
xcrun --sdk macosx --find clang++
xcrun --sdk macosx --show-sdk-path
Als deze controles mislukken, los dan eerst de selectie of installatie van ontwikkelaarstools op. De build heeft naast een compiler ook de macOS SDK nodig. Een SDK levert de platformheaders en -libraries waartegen het product wordt gebouwd. Naslaggids voor Xcode-buildinstellingen: Base SDK.
Maak de bronbestanden aan
Voer dit setupblok uit in Bash. Het maakt een nieuwe tijdelijke map aan en toont de locatie, zodat bestaande projectbestanden onaangeroerd blijven. Ga in dezelfde shell verder met de volgende blokken.
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
De header declareert C-taalkoppeling wanneer een C++-vertaaleenheid deze includeert. Zo voorkomt de header dat de C++-aanroeper een anders gemangelde C++-functienaam verwacht. De C++-FAQ raadt ook aan om de C++-compiler de uiteindelijke link te laten aansturen, zodat de benodigde libraries worden opgenomen. Veelgestelde vragen over Standard C++: C en C++ combineren.
Compileer, archiveer en link elke architectuur
Het volgende blok bouwt beide macOS-varianten. Voor elke architectuur geeft het dezelfde target en SDK mee aan de compilatie van de library en het linken van het uitvoerbare bestand. Clang documenteert -c als een optie die een objectbestand oplevert; zonder een optie die een specifieke fase selecteert, voert de driver ook het linken uit. Clang-handleiding voor compileropdrachten.
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 kiest -target de architectuur en het macOS-implementatiedoel, terwijl -isysroot de hoofdmap van de SDK opgeeft. Dit zijn afzonderlijke invoerwaarden. Het expliciete archiefpad maakt ook zichtbaar welke dependency in de opdracht wordt geselecteerd. Richtlijnen voor crosscompilatie met Clang, Naslaggids voor Clang-opdrachtregelargumenten.
De opdracht ar rcs verpakt het gecompileerde object in een statisch archief en levert een symboolindex. Archiveren verpakt bestaande objecten; het compileert ze niet voor een ander target. LLVM documenteert de archiefbewerkingen en de compatibiliteit met andere ar-implementaties. Documentatie van de LLVM-archiver.
De voorbeeldinvoer 3 en 4 moet Tiles: 12 opleveren. Deze kleine functie dient alleen als linkvoorbeeld; de interface definieert geen gedrag voor willekeurige dimensies waarvan het product buiten het bereik van int valt.
Hoe inspecteer je architectuur, platform en symbolen?
Inspecteer het daadwerkelijke uitvoerbare bestand, de library of het objectbestand. Apple documenteert lipo -archs voor het weergeven van architecturen en vermeldt dat het uitvoerbare bestand van een app in de map Contents/MacOS/ van de app-bundel staat. Een universeel macOS-binair bestand bouwen.
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
De eerste twee opdrachten moeten elk de bijbehorende architectuur identificeren. Controleer voor het platform de load commands die door otool -l worden afgedrukt; LLVM documenteert dezelfde optie voor zijn compatibele llvm-otool. LLVM-tool voor het weergeven van Mach-O-inhoud.
Zoek naar LC_BUILD_VERSION. De platform moet macOS aangeven en minos bevat de minimale versie van het besturingssysteem. Het veld sdk staat daar los van. Sommige oudere binaire bestanden gebruiken in plaats daarvan een opdracht LC_VERSION_MIN_*. De formaatdefinities van Apple documenteren beide weergaven, waaronder platformidentificatie 1 voor macOS. Mach-O-structuren voor buildversies.
Inspecteer bij een statische library de objectbestanden in het archief in plaats van de archiefcontainer als bewijs van platformcompatibiliteit te beschouwen. Dit voorbeeld bewaart daarvoor tile.o. Gebruik bij een onbekend archief xcrun ar t om de leden weer te geven en inspecteer ze in een aparte werkmap. LLVM documenteert de bewerkingen voor het weergeven en uitpakken van archieven. Documentatie van de LLVM-archiver.
Controleer ten slotte de symbolen. De voorbeeldlibrary moet op Mach-O _tile_total definiëren. In traditionele nm-uitvoer geeft T een globaal codesymbool aan en U een ongedefinieerde verwijzing. Een definitie en een verwijzing zijn verschillend bewijs. Documentatie van de LLVM-symbooltabel.
Gebruik deze diagnostische volgorde om de volgende stap te bepalen:
- Vereiste CPU-slice ontbreekt: bouw de dependency opnieuw voor die architectuur of verkrijg het passende binaire bestand.
- CPU komt overeen, maar platform verschilt: verkrijg een native macOS-variant. De architectuur van de aanroeper wijzigen maakt een platform niet compatibel.
- CPU en platform komen overeen, maar de functie blijft onopgelost: vergelijk de gedeclareerde functie met het geëxporteerde symbool van de library en bevestig dat het bedoelde archief in de linkopdracht staat.
- Namen verschillen tussen C en C++: corrigeer de taalkoppeling in de gedeelde header en bouw de aanroeper daarna opnieuw.
- Implementatieversies verschillen: controleer de minimale versie van de dependency en bouw deze, indien ondersteund, opnieuw voor het beoogde implementatiedoel, of verhoog bewust de vereiste versie van de applicatie.
Deze volgorde is een hulpmiddel bij het nemen van beslissingen, afgeleid uit de bovenstaande documentatie over targets, Mach-O en koppelingen. Ze onderscheidt ontbrekende machinecode van incompatibele platformmetadata en problemen met de taalinterface, zodat elke nieuwe build een specifieke bevinding aanpakt.
Wat verandert er wanneer Swift de native library aanroept?
Een overeenkomende CPU en platform maken niet elke taalinterface aanroepbaar. Swift ondersteunt standaard interoperabiliteit met C; voor directe interoperabiliteit met C++ moet je deze inschakelen, waarna een gedocumenteerde subset van C++-functies wordt ondersteund. De C-header in dit voorbeeld biedt een ondersteunde interfacekeuze, mits de Swift-build deze importeert via het geconfigureerde header- of modulemechanisme. Swift en C++ combineren.
Voor een directe C++-interface documenteert Swift -cxx-interoperability-mode=default voor builds via de opdrachtregel, Clang-modules voor het importeren van headers en -Xcc voor het doorgeven van instellingen aan de C++-importer. De importconfiguratie en het linken van de gecompileerde implementatie zijn afzonderlijke buildvereisten. Projecten instellen die Swift en C++ combineren.
Houd rekening met de gedocumenteerde beperkingen: interoperabiliteit met C++ vereist Swift 5.9 of later en C++14 of nieuwer. Gemengde Swift- en C++-code moet dezelfde C++-standaardbibliotheek gebruiken; op Apple-platforms is dat libc++. Om Swift vanuit C++ aan te roepen, zijn de gegenereerde interface en een ondersteunde Clang-compiler vereist. Geïmporteerde C++-referentietypen vereisen bovendien een implementatiedoel van macOS 13.3. Controleer de specifieke API in de actuele compatibiliteitsdocumentatie voordat je deze route kiest. Ondersteunde functies en beperkingen van interoperabiliteit met C++.
Wanneer gebruik je een universeel binair bestand of een XCFramework?
Combineer voor dit uitvoerbare bestand dat alleen voor macOS bedoeld is de twee voltooide macOS-builds tot een universeel binair bestand. Apple documenteert hoe je elke architectuur compileert en linkt voordat je de resulterende uitvoerbare bestanden combineert met lipo. Een universeel macOS-binair bestand bouwen.
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
Op een ondersteunde Mac met macOS 13.0 of later moet de laatste opdracht Tiles: 12 afdrukken en met succes eindigen. Beide slices bouwen is iets anders dan beide testen: een Intel Mac kan de slice arm64 niet uitvoeren. Richtlijnen van Apple voor universele binaire bestanden.
Gebruik voor een library die meerdere Apple-platforms of platformvarianten ondersteunt een XCFramework om afzonderlijk gebouwde varianten te verpakken. Apple draagt ontwikkelaars nadrukkelijk op om statische iOS- en iOS Simulator-libraries gescheiden te houden en staat toe dat native macOS- en Mac Catalyst-varianten in hetzelfde XCFramework worden opgenomen. Voor Xcode-archiefworkflows selecteert de gedocumenteerde aanpak het platform en de SDK via een bestemming. Een binair frameworkpakket voor meerdere platforms maken.
Noteer bij de volgende linkfout op macOS het beoogde target, inspecteer de architectuur en het platform van de geselecteerde dependency en controleer daarna de taalinterface. Bewaar die bevindingen bij de exacte linkopdracht. Zo heb je een concrete basis om het juiste binaire bestand te selecteren of het vanuit de broncode opnieuw te bouwen.
Bronnen
Crosscompilatie met Clang — LLVM Project.
Veelgestelde vragen over bouwen vanaf de opdrachtregel met Xcode — Apple.
Naslaggids voor buildinstellingen — Apple.
clang — de Clang-compiler voor C, C++ en Objective-C — LLVM Project.
Naslaggids voor Clang-opdrachtregelargumenten — LLVM Project.
Standard C++: C en C++ combineren — Standard C++ Foundation.
llvm-ar — LLVM-archiver — LLVM Project.
llvm-otool — tool voor het weergeven van Mach-O-inhoud — LLVM Project.
llvm-nm — de symbooltabel van LLVM-bitcode en objectbestanden weergeven — LLVM Project.
Mach-O-formaatdefinities: loader.h — Apple.
Swift en C++ combineren — Swift.org.
Projecten instellen die Swift en C++ combineren — Swift.org.
Ondersteunde functies en beperkingen van interoperabiliteit met C++ — Swift.org.
Een binair frameworkpakket voor meerdere platforms maken — Apple.