Begin voor een klein native Mac-hulpprogramma met een statische bibliotheek als de code met het uitvoerbare bestand wordt meegeleverd. Kies een dynamische bibliotheek wanneer een afzonderlijk runtimeonderdeel nuttig is, en een framework wanneer je de bundelverpakking van Apple nodig hebt. In deze handleiding bouw je dezelfde C++-helper op beide manieren.
De concrete taak is het verpakken van een herbruikbare rasterberekening voor een lokaal macOS-bureaubladhulpprogramma. Het voorbeeld gebruikt synthetische invoer en terminalopdrachten, zodat je compilatie, koppeling en laden afzonderlijk kunt bekijken voordat je een formaat voor een app-project kiest.
Waar kies je eigenlijk voor?
Er zijn twee keuzes: hoe code wordt gekoppeld en hoe het resultaat wordt verpakt. Een statische bibliotheek, doorgaans met de extensie .a, is een archief met objectbestanden. Een dynamische bibliotheek, met de extensie .dylib, blijft een afzonderlijke runtime-image. Een framework is een bundel die een bibliotheek en verwant materiaal ordent. Apple beschrijft deze onderliggende formaten in An Apple Library Primer.
Een framework kan ook een statische bibliotheek bevatten. Apple ondersteunt het maken van statische frameworks in Xcode 15 en hoger, inclusief resources. Daarom zegt de extensie .framework op zichzelf niet of de bibliotheek statisch of dynamisch wordt gekoppeld. Zie Creating a static framework.
Gebruik deze keuzehulp voor een hulpcomponent die bij de app hoort en die je vanuit de broncode bouwt:
| Je vereiste | Startkeuze | Verantwoordelijkheid die dit met zich meebrengt |
|---|---|---|
| Eén uitvoerbaar bestand en een helper die samen opnieuw worden gebouwd | Statische .a |
Koppel het uitvoerbare bestand opnieuw wanneer de helper verandert |
| Een helper die een afzonderlijk runtimeonderdeel blijft | Dynamische .dylib |
Lever de bibliotheek en een werkend runtimezoekpad mee |
| Een Xcode-component met openbare headers en gebundelde resources | Framework | Kies de koppeling afzonderlijk en configureer de targetlidmaatschappen |
| Binaire varianten voor meerdere Apple-platforms | XCFramework | Bouw en verpak de vereiste platformvarianten |
Dit zijn praktische aanbevelingen op basis van de gedocumenteerde formaten. Ze wijzen geen prestatiekampioen aan. Voor dit rasterhulpprogramma is een statisch archief het logische startpunt: er is één client, geen resourcebundel en geen vereiste om een afzonderlijke bibliotheek te vervangen.
Een XCFramework verpakt binaire varianten; het vervangt de keuze tussen statisch en dynamisch niet. Apple staat statische of dynamische frameworks en bibliotheken binnen dat distributieformaat toe. Zie Creating a multiplatform binary framework bundle.
Controleer de compatibiliteit voordat je een container kiest
Een overeenkomende bestandsnaam of CPU-architectuur is niet voldoende. macOS, Mac Catalyst en iOS zijn afzonderlijke bouwplatforms. Een arm64-bibliotheek die voor het ene platform is gecompileerd, kan niet zomaar worden gekoppeld aan het uitvoerbare bestand van een ander platform. Apple maakt in An Apple Library Primer expliciet onderscheid tussen platform en architectuur.
Houd voor deze taak de volgende invoer consistent: het macOS-doel, de vereiste CPU-architectuur, de minimale macOS-versie en compatibele taal- en runtime-instellingen. Een universeel Mac-resultaat vereist passende dependency-slices voor elke ondersteunde architectuur. Apple behandelt het opnieuw bouwen van bibliotheken én uitvoerbare bestanden in Building a universal macOS binary.
C++-compatibiliteit vraagt om afzonderlijke aandacht. Met -std=c++17 selecteer je een taalmodus; dat garandeert geen binaire compatibiliteit tussen onafhankelijk gebouwde componenten. LLVM documenteert ABI-garanties en configuratiekeuzes van leveranciers voor libc++, waaronder instellingen die de indeling van typen wijzigen. Voor een dependency die bij de app hoort, is het een bruikbaar uitgangspunt om die met de toolchain van de app en consistente instellingen opnieuw te bouwen. Zie libc++’s ABI Guarantees.
Het voorbeeld stelt één functie beschikbaar via een C-compatibele header en implementeert die in C++. De afgeschermde declaratie extern "C" geeft C-taalkoppeling aan voor C++-compilatie. Hiermee worden willekeurige C++-klassen niet omgezet in C-interfaces en wordt een binair bestand niet platformonafhankelijk. Het C++-werkdocument beschrijft deze grens in Linkage specifications.
Bouw dezelfde helper als statische en dynamische bibliotheek
Je hebt een Mac nodig met Apples ontwikkelaarstools en een macOS SDK. De volledige versie van Xcode bevat de opdrachtregeltools; Apple biedt ook een afzonderlijk pakket met Command Line Tools aan. Als geen van beide is geïnstalleerd, voer je xcode-select --install uit en voltooi je het installatieprogramma voordat je verdergaat. Zie Installing the command-line tools.
Het volledige script hieronder maakt een nieuwe tijdelijke map en gebruikt alleen synthetische bronbestanden. Voer het uit in Terminal. Het stelt expliciet de architectuur van de huidige shellomgeving en macOS 11.0 in als illustratieve minimale implementatieversie. Dit is een oefening voor één architectuur; gebruik een andere minimumversie als je project dat vereist.
Ik heb het voorbeeld bekeken, maar kon de Mac-build niet uitvoeren: de onderzoeksomgeving draait op Linux en bevat geen clang++. De getoonde resultaten zijn verwachte resultaten, geen vastgelegde testuitvoer.
bash <<'SH'
set -eu
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/mac-library-demo.XXXXXX")
cd "$work_dir"
printf 'Example directory: %s\n' "$work_dir"
mkdir include src build
mkdir -p build/static build/dynamic/lib
cat > include/grid.h <<'EOF'
#ifndef GRID_H
#define GRID_H
#ifdef __cplusplus
extern "C" {
#endif
/* Inputs: positive dimensions whose product fits in int. */
int grid_cells(int rows, int columns);
#ifdef __cplusplus
}
#endif
#endif
EOF
cat > src/grid.cpp <<'EOF'
#include "grid.h"
int grid_cells(int rows, int columns) {
return rows * columns;
}
EOF
cat > src/main.cpp <<'EOF'
#include "grid.h"
#include <cstdio>
int main() {
const int cells = grid_cells(3, 4);
std::printf("Grid cells: %d\n", cells);
return cells == 12 ? 0 : 1;
}
EOF
sdk_path=$(xcrun --sdk macosx --show-sdk-path)
build_arch=$(uname -m)
case "$build_arch" in
arm64|x86_64) ;;
*) printf 'Unexpected architecture: %s\n' "$build_arch" >&2; exit 1 ;;
esac
xcrun --sdk macosx clang++ \
-std=c++17 -arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -Iinclude \
-c src/grid.cpp -o build/grid.o
xcrun --sdk macosx ar rcs build/static/libGrid.a build/grid.o
xcrun --sdk macosx clang++ \
-std=c++17 -arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -Iinclude \
src/main.cpp build/static/libGrid.a -o build/static/grid-demo
xcrun --sdk macosx clang++ \
-arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -dynamiclib build/grid.o \
-Wl,-install_name,@rpath/libGrid.dylib \
-o build/dynamic/lib/libGrid.dylib
xcrun --sdk macosx clang++ \
-std=c++17 -arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -Iinclude \
src/main.cpp build/dynamic/lib/libGrid.dylib \
-Wl,-rpath,@loader_path/lib -o build/dynamic/grid-demo
./build/static/grid-demo
./build/dynamic/grid-demo
xcrun --sdk macosx ar t build/static/libGrid.a
xcrun --sdk macosx otool -L build/static/grid-demo
xcrun --sdk macosx otool -L build/dynamic/grid-demo
xcrun --sdk macosx otool -l build/dynamic/grid-demo
xcrun --sdk macosx lipo -archs build/dynamic/grid-demo
SH
Beide uitvoeringen moeten Grid cells: 12 afdrukken. Deze berekening is bewust klein: drie rijen vermenigvuldigd met vier kolommen. Het opgegeven invoercontract van de helper sluit dimensies uit waarvan het product niet in int past; dit voorbeeld implementeert geen algemene API voor invoervalidatie.
Wat de statische build laat zien
De aanroep met -c maakt grid.o zonder een uitvoerbaar bestand te koppelen. Met de archiefopdracht wordt dat object verpakt in libGrid.a; de opties r, c en s vragen om invoegen/vervangen, het maken van een archief en een symbolenindex. Clang beschrijft de compilatiestappen in de compiler command guide, en LLVM legt archiefbewerkingen uit in llvm-ar — LLVM archiver.
De client wordt gekoppeld met het expliciete pad naar het archief. Zo is de gekozen invoer ondubbelzinnig wanneer zowel een statische als dynamische versie bestaat. De verwachte archieflijst bevat grid.o, terwijl de lijst met afhankelijkheden van het uitvoerbare bestand geen runtimeafhankelijkheid van libGrid.a hoort te bevatten.
Statische koppeling geldt hier voor de Grid-helper. Dat betekent niet dat het resulterende uitvoerbare bestand onafhankelijk is van macOS-runtimesbibliotheken; inspecteer de overige vermeldingen in de uitvoer van otool -L. Apple maakt in An Apple Library Primer onderscheid tussen de linker tijdens het bouwen en de loader tijdens runtime.
Wat de dynamische build laat zien
De aanroep met -dynamiclib maakt een afzonderlijke bibliotheek. De installatienaam is @rpath/libGrid.dylib. Het uitvoerbare bestand registreert een zoekpad van @loader_path/lib, dat voor de run-path-vermelding van dit uitvoerbare bestand verwijst naar de naastgelegen map lib. Clang vermeldt de relevante driveropties in de command-line argument reference.
De verwachte uitvoer van otool -L voor de dynamische client bevat @rpath/libGrid.dylib; de uitvoer van de load commands hoort een vermelding LC_RPATH met @loader_path/lib te bevatten. Apple beschrijft hoe installatienamen en zoekpaden van uitvoerbare bestanden samenwerken in Run-Path Dependent Libraries.
Hiermee krijg je een concrete verpakkingstest: verplaats de volledige map build/dynamic in zijn geheel en behoud de interne indeling; voer het uitvoerbare bestand daarna vanaf de nieuwe locatie uit. Als je alleen het uitvoerbare bestand verplaatst, blijft de Grid-dependency achter. Een geslaagde koppeling bevestigt dat de bouwinvoer is gevonden; ze bevestigt niet dat het runtimepakket compleet is.
Wanneer moet de helper een framework worden?
Kies een framework wanneer de bundelstructuur het project dient, bijvoorbeeld als de component openbare headers en resources nodig heeft die samen in Xcode worden beheerd. Voor deze helper die alleen berekeningen uitvoert, voegt een framework verpakkingswerk toe zonder in een aanvullende vereiste te voorzien.
Voor een statisch framework in Xcode 15 of hoger documenteert Apple deze stappen:
- Kies
File > New > Target, selecteer macOS en gebruik de templateFramework. - Selecteer
Build Settingsvan de nieuwe target en wijzigMach-O TypeinStatic Library. - Voeg implementatiebestanden toe aan
Compile Sourcesen resources aanCopy Bundle Resources. - Voeg geëxporteerde headers toe aan de buildfase
Headersen neem openbare headers op in de umbrella-header. - Voeg het framework toe aan de app die het gebruikt en volg Apples gedocumenteerde configuratie voor insluiten.
Voor een statisch framework met resources schrijft Apple Embed & Sign voor. Xcode laat het al gekoppelde hoofdbinaire bestand weg uit de ingesloten bundel, maar behoudt het bundelmateriaal. Volg Creating a static framework en ga er niet van uit dat elk statisch framework als een archief geen insluiting nodig heeft.
Gebruik een XCFramework als je binaire varianten distribueert. Voor een bestaand zelfstandig .a-bestand documenteert Apple de invoeropties -library en -headers voor xcodebuild -create-xcframework; Apple raadt af het archief handmatig als framework te vermommen. Voor xcodebuild is de volledige versie van Xcode vereist. Zie Creating a multiplatform binary framework bundle en Installing the command-line tools.
Welke invloed heeft Swift op de keuze?
Swift voegt vereisten voor modulegebruik en evolutie toe aan de onderliggende keuze voor koppeling. ABI-stabiliteit, modulestabiliteit en bibliotheek-evolutie behandelen verschillende compatibiliteitskwesties; een container met extensie .framework schakelt ze niet automatisch in.
Volgens de richtlijnen van Swift moet bibliotheek-evolutie doorgaans uitgeschakeld blijven voor frameworks die samen met hun clients worden gebouwd en gedistribueerd. Bepaal voor een binair framework dat afzonderlijk wordt gebouwd en bijgewerkt vanaf de eerste release of je evolutieondersteuning nodig hebt. In Xcode schakelt BUILD_LIBRARY_FOR_DISTRIBUTION bibliotheek-evolutie en modulestabiliteit in. Deze keuzes beïnvloeden prestatiekenmerken en toegestane API-wijzigingen, zoals uitgelegd in Library Evolution in Swift.
Houd deze garanties binnen het gedocumenteerde platformbereik. De documentatie van Swift beschrijft binaire compatibiliteit tussen compilers op Apple-platforms; breid die belofte niet uit naar elk platform dat Swift ondersteunt en interpreteer haar niet als toestemming om willekeurige API-wijzigingen aan te brengen. De C-compatibele header in dit voorbeeld vormt ook geen complete Swift-moduleconfiguratie.
Spoor een mislukte build op in de volgorde waarin die plaatsvindt
Gebruik de fase waarin de fout optreedt om de volgende controle te bepalen:
| Symptoom | Eerste controle | Volgende actie |
|---|---|---|
Compiler kan grid.h niet vinden |
Header staat onder include |
Controleer de werkmap en -Iinclude |
| Undefined reference naar de Grid-functie | Implementatie en declaratie komen overeen | Controleer of het bedoelde archief of de dynamische bibliotheek in de linkopdracht staat |
| Architectuur- of platformmismatch | Werkelijke dependency-slices en bouwplatform | Bouw de dependency opnieuw voor het macOS-doel van de client |
| Uitvoerbaar bestand wordt gekoppeld maar kan Grid niet laden | Geregistreerde dependency en zoekpad | Vergelijk otool -L, otool -l en de indeling van de verpakte map |
| Intel- en Apple silicon-builds gedragen zich verschillend | Architectuurdekking van elke dependency | Inspecteer elk binair bestand en test de beoogde architectuur |
De compiler command guide van Clang scheidt compilatie van koppeling. Apples Run-Path Dependent Libraries legt runtimeoplossing uit, terwijl Building a universal macOS binary architectuurinspectie met lipo -archs documenteert. Door dat onderscheid is deze diagnostische volgorde nuttig: wijzigingen in headerpaden kunnen een ontbrekende runtimebibliotheek niet herstellen.
Behoud voor het synthetische rasterhulpprogramma het statische archief totdat een concrete verpakkings- of runtimevereiste een andere keuze rechtvaardigt. Als die vereiste zich voordoet, gebruik dan dezelfde kleine berekening als bouwcontrole en verifieer vervolgens afzonderlijk de gekozen bibliotheek, de compatibiliteit van het doel en de uiteindelijke mapindeling.
Bronnen
- An Apple Library Primer — Apple Developer Technical Support.
- Creating a static framework — Apple.
- Creating a multiplatform binary framework bundle — Apple.
- Building a universal macOS binary — Apple.
- Installing the command-line tools — Apple.
- Run-Path Dependent Libraries — Apple Documentation Archive.
- clang — the Clang C, C++, and Objective-C compiler — LLVM Project.
- Clang command line argument reference — LLVM Project.
- llvm-ar — LLVM archiver — LLVM Project.
- libc++’s ABI Guarantees — LLVM Project.
- Linkage specifications — C++ Working Draft, rendered by eel.is.
- Library Evolution in Swift — Swift.org.