För att länka ett inbyggt macOS-program måste varje vald biblioteksskiva matcha dess CPU-arkitektur och Apple-plattform, med ett kompatibelt språk-ABI. Den här guiden visar hur du bygger och granskar ett litet C-bibliotek som används av ett C++-kommandoradsverktyg för datorer.
Den konkreta uppgiften är att skapa en macOS-körbar fil från lokal källkod och diagnostisera ett inkompatibelt inbyggt beroende. Exemplet använder syntetisk heltalsaritmetik, kräver inga nedladdade bibliotek och håller arkitekturspecifika utdata åtskilda.
Vad måste stämma innan ett inbyggt bibliotek kan länkas?
Kontrollera tre saker var för sig: arkitektur, plattform och det binära gränssnittet mellan språk. Clangs målspecifikation omfattar mer än processorn; målet och miljön påverkar kodgenerering och biblioteksval. Beroenden måste också byggas för det avsedda målet. Korskompilering med Clang.
| Kontroll | Fråga för den här macOS-uppgiften | Vad en avvikelse innebär |
|---|---|---|
| CPU-arkitektur | Innehåller beroendet den arm64- eller x86_64-skiva som länkas? |
Skaffa eller bygg den saknade arkitekturen. |
| Apple-plattform | Är skivan byggd för inbyggt macOS? | Skaffa en macOS-version; en iOS- eller Simulator-version är en separat variant. |
| Språk-ABI | Stämmer deklarationer, anropskonventioner och nödvändiga körningsmiljöer överens? | Korrigera gränssnittet eller använd språkets stödda interoperabilitetsväg. |
En arkitekturlista kan inte besvara plattformsfrågan. Apples Mach-O-format lagrar CPU-information separat från plattformens bygginformation och definierar olika plattformsidentifierare för macOS, iOS, Mac Catalyst och iOS Simulator. Ett beroende som bara beskrivs som arm64 behöver därför granskas ytterligare. Apples definitioner av Mach-O-formatet.
I det här exemplet är byggkontraktet inbyggt macOS, macOS 13.0 som distributionsmål och en byggnad per arkitektur. C-biblioteket har ett C-gränssnitt; den körbara filen använder C++17. Versionen är ett illustrativt val, inte ett universellt minimikrav för Mac-programvara.
Bygg ett komplett macOS-exempel med explicita mål
Använd en Mac med en aktuell Xcode-installation vald för kommandoradsutveckling. Apple beskriver xcrun som mekanismen för att hitta eller köra verktyg i den valda utvecklarinstallationen. Kontrollera valet innan du undersöker ändringar i källkoden. Vanliga frågor om att bygga från kommandoraden med Xcode.
Kommandona nedan är avsedda för Bash på macOS. De har granskats men inte körts här eftersom forskningsmiljön är Linux och saknar Apples verktygskedja.
xcode-select --print-path
xcrun --sdk macosx --find clang
xcrun --sdk macosx --find clang++
xcrun --sdk macosx --show-sdk-path
Om dessa kontroller misslyckas löser du först valet av utvecklarverktyg eller installationen. Bygget behöver macOS-SDK:n såväl som en kompilator. En SDK tillhandahåller plattformens headers och bibliotek som produkten byggs mot. Referens för Xcodes bygginställningar: Base SDK.
Skapa källfilerna
Kör det här inställningsblocket i Bash. Det skapar en ny tillfällig katalog och skriver ut dess plats, så befintliga projektfiler lämnas orörda. Fortsätt med de senare blocken i samma skal.
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
Headern deklarerar C-språklänkning när en C++-översättningsenhet inkluderar den. Det hindrar C++-anroparen från att begära ett C++-funktionsnamn med en annan namnförändring. C++ FAQ rekommenderar också att C++-kompilatorn styr den slutliga länkningen, så att dess nödvändiga bibliotek inkluderas. Standard C++ FAQ: Så blandar du C och C++.
Kompilera, arkivera och länka varje arkitektur
Följande block bygger båda macOS-varianterna. Det skickar samma mål och SDK till bibliotekskompileringen och den körbara filens länkning för varje arkitektur. Clang beskriver -c som ett alternativ som skapar en objektfil; utan ett alternativ för val av steg fortsätter dess drivrutin till länkning. Clangs guide till kompilatorkommandon.
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
Här väljer -target arkitekturen och macOS-distributionsmålet, medan -isysroot anger SDK-rotkatalogen. Det är separata indata. Den explicita arkivsökvägen gör också det valda beroendet synligt i kommandot. Clangs vägledning om korskompilering, Referens för Clangs kommandoradsargument.
Kommandot ar rcs paketerar det kompilerade objektet i ett statiskt arkiv och tillhandahåller ett symbolindex. Arkivering paketerar befintliga objekt; den kompilerar dem inte för ett annat mål. LLVM dokumenterar arkivåtgärderna och deras kompatibilitet med andra ar-implementationer. LLVM:s dokumentation för arkiveringsverktyget.
Indataexemplet 3 och 4 ska ge Tiles: 12. Den här lilla funktionen är bara ett länkningsexempel; gränssnittet definierar inte beteende för godtyckliga dimensioner vars produkt överskrider intervallet för int.
Hur granskar du arkitektur, plattform och symboler?
Granska själva den körbara filen, biblioteket eller objektfilen. Apple dokumenterar lipo -archs för att lista arkitekturer och påpekar att en apps körbara fil finns i dess paketkatalog Contents/MacOS/. Bygg en universell macOS-binärfil.
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 två första kommandona ska identifiera respektive arkitektur. För plattformskontrollen granskar du laddningskommandona som otool -l skriver ut; LLVM:s kompatibla llvm-otool dokumenterar samma alternativ. LLVM:s verktyg för att dumpa Mach-O.
Leta efter LC_BUILD_VERSION. Dess platform ska identifiera macOS, och minos anger den lägsta operativsystemsversionen. Fältet sdk är separat. Vissa äldre binärfiler använder i stället ett kommando av typen LC_VERSION_MIN_*. Apples formatdefinitioner dokumenterar båda representationerna, inklusive plattformsidentifieraren 1 för macOS. Mach-O-strukturer för byggversioner.
För ett statiskt bibliotek granskar du medlemsobjekten i stället för att betrakta arkivomslaget som bevis på plattformskompatibilitet. I det här exemplet behålls tile.o för det ändamålet. För ett okänt arkiv använder du xcrun ar t för att lista medlemmarna och granskar dem i en separat arbetskatalog. LLVM dokumenterar arkivlistning och extrahering. LLVM:s dokumentation för arkiveringsverktyget.
Kontrollera slutligen symbolerna. Exempelbiblioteket ska definiera _tile_total på Mach-O. I traditionell nm-utdata anger T en global kodsymbol och U en odefinierad referens. En definition och en referens är olika slags belägg. LLVM:s dokumentation om symboltabeller.
Använd den här diagnostiksekvensen för att välja nästa åtgärd:
- CPU-skivan som krävs saknas: bygg om beroendet för den arkitekturen eller skaffa en matchande binärfil.
- CPU:n matchar men plattformen skiljer sig: skaffa en inbyggd macOS-variant. Att ändra anroparens arkitektur fastställer inte plattformskompatibilitet.
- CPU och plattform matchar men funktionen är olöst: jämför den deklarerade funktionen med bibliotekets exporterade symbol och bekräfta att det avsedda arkivet finns med i länkningskommandot.
- Namnen skiljer sig mellan C och C++: korrigera språklänkningen i den gemensamma headern och bygg sedan om anroparen.
- Distributionsversionerna skiljer sig: granska beroendets minimiversion och bygg om det för det avsedda distributionsmålet där det stöds, eller höj medvetet programmets krav.
Den här sekvensen är ett beslutsstöd som härletts från dokumentationen ovan om mål, Mach-O och länkning. Den skiljer saknad maskinkod från inkompatibla plattformsmetadata och språkgränssnittsproblem, så att varje ombyggnad åtgärdar ett specifikt fynd.
Vad ändras när Swift anropar det inbyggda biblioteket?
En matchande CPU och plattform gör inte alla språkgränssnitt anropbara. Swift stöder C-interoperabilitet som standard; direkt C++-interoperabilitet måste aktiveras och stöder en dokumenterad delmängd av C++-funktioner. Den C-inriktade headern i det här exemplet erbjuder ett stödt gränssnittsalternativ, förutsatt att Swift-bygget importerar den via sin konfigurerade header- eller modulmekanism. Blanda Swift och C++.
För ett direkt C++-gränssnitt dokumenterar Swift -cxx-interoperability-mode=default för kommandoradsbyggen, Clang-moduler för import av headers och -Xcc för att skicka inställningar till C++-importören. Importkonfiguration och länkning av den kompilerade implementationen är separata byggkrav. Konfigurera Swift- och C++-projekt med blandade språk.
Följ de dokumenterade begränsningarna: C++-interoperabilitet kräver Swift 5.9 eller senare och C++14 eller senare. Kod med blandad Swift och C++ måste använda samma C++-standardbibliotek; Apple-plattformar använder libc++. Att anropa Swift från C++ kräver det genererade gränssnittet och en Clang-kompilator som stöds. Importerade C++-referenstyper har dessutom ett distributionskrav på macOS 13.3. Kontrollera det exakta API:t mot den aktuella kompatibilitetsdokumentationen innan du väljer den här vägen. Funktioner och begränsningar för C++-interoperabilitet som stöds.
När bör du använda en universell binärfil eller XCFramework?
För den här macOS-specifika körbara filen sammanfogar du de två färdigbyggda macOS-versionerna till en universell binärfil. Apple dokumenterar hur du kompilerar och länkar varje arkitektur innan du kombinerar de resulterande körbara filerna med lipo. Bygg en universell macOS-binärfil.
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
På en Mac som stöds och kör macOS 13.0 eller senare ska det sista kommandot skriva ut Tiles: 12 och avslutas utan fel. Att bygga båda skivorna är inte samma sak som att testa båda: en Intel-Mac kan inte köra skivan arm64. Apples vägledning om universella binärfiler.
För ett bibliotek som stöder flera Apple-plattformar eller plattformsvarianter använder du ett XCFramework för att paketera separat byggda varianter. Apple instruerar uttryckligen utvecklare att hålla statiska bibliotek för iOS och iOS Simulator åtskilda och stöder att inbyggda macOS- och Mac Catalyst-varianter ingår i samma XCFramework. För Xcode-arkiveringsflöden använder den dokumenterade metoden ett mål för att välja plattform och SDK. Skapa ett binärt ramverkspaket för flera plattformar.
Vid nästa länkningsfel på macOS dokumenterar du det avsedda målet, granskar den valda beroendets arkitektur och plattform och kontrollerar sedan språkgränssnittet. Spara dessa fynd tillsammans med det exakta länkningskommandot. Då har du en konkret grund för att välja rätt binärfil eller bygga om den från källkod.
Källor
Korskompilering med Clang — LLVM Project.
Vanliga frågor om att bygga från kommandoraden med Xcode — Apple.
Referens för bygginställningar — Apple.
clang — Clang-kompilatorn för C, C++ och Objective-C — LLVM Project.
Referens för Clangs kommandoradsargument — LLVM Project.
Standard C++: Så blandar du C och C++ — Standard C++ Foundation.
llvm-ar — LLVM:s arkiveringsverktyg — LLVM Project.
llvm-otool — verktyg för att dumpa Mach-O — LLVM Project.
llvm-nm — lista LLVM-bitkod och objektfilens symboltabell — LLVM Project.
Blanda Swift och C++ — Swift.org.
Konfigurera Swift- och C++-projekt med blandade språk — Swift.org.
Funktioner och begränsningar för C++-interoperabilitet som stöds — Swift.org.
Bygg en universell macOS-binärfil — Apple.
Skapa ett binärt ramverkspaket för flera plattformar — Apple.