Pour éditer les liens d’un programme macOS natif, chaque tranche de bibliothèque sélectionnée doit correspondre à l’architecture du processeur et à la plateforme Apple, avec une ABI de langage compatible. Ce guide montre comment compiler et inspecter une petite bibliothèque C utilisée par un outil en ligne de commande pour ordinateur de bureau écrit en C++.
La tâche concrète consiste à produire un exécutable macOS à partir du code source local et à diagnostiquer une dépendance native incompatible. L’exemple utilise des opérations arithmétiques entières synthétiques, ne nécessite aucune bibliothèque téléchargée et conserve les sorties propres à chaque architecture séparées.
Que faut-il faire correspondre avant de pouvoir éditer les liens d’une bibliothèque native ?
Vérifiez séparément trois éléments : l’architecture, la plateforme et l’interface binaire entre les langages. La spécification de cible de Clang inclut davantage que le processeur ; sa cible et son environnement influent sur la génération du code et la sélection des bibliothèques. Les dépendances doivent également être compilées pour la cible prévue. Compilation croisée avec Clang.
| Vérification | Question pour cette tâche macOS | Signification d’une incompatibilité |
|---|---|---|
| Architecture du processeur | La dépendance contient-elle la tranche arm64 ou x86_64 à lier ? |
Obtenez ou compilez l’architecture manquante. |
| Plateforme Apple | Cette tranche est-elle compilée pour macOS natif ? | Obtenez une version macOS ; une version iOS ou Simulateur est une variante distincte. |
| ABI de langage | Les déclarations, conventions d’appel et environnements d’exécution requis sont-ils compatibles ? | Corrigez l’interface ou utilisez la méthode d’interopérabilité prise en charge par le langage. |
Une liste d’architectures ne permet pas de répondre à la question de la plateforme. Le format Mach-O d’Apple enregistre séparément les informations sur le processeur et celles sur la plateforme de compilation, et définit des identifiants de plateforme distincts pour macOS, iOS, Mac Catalyst et le Simulateur iOS. Une dépendance décrite uniquement comme arm64 nécessite donc une inspection plus approfondie. Définitions du format Mach-O d’Apple.
Pour cet exemple, le contrat de compilation prévu est macOS natif, une cible de déploiement macOS 13.0 et une compilation par architecture. La bibliothèque C expose une interface C ; l’exécutable est en C++17. La version choisie sert d’exemple et ne constitue pas une exigence minimale universelle pour les logiciels Mac.
Compiler un exemple macOS complet avec des cibles explicites
Utilisez un Mac sur lequel une version actuelle de Xcode est sélectionnée pour le développement en ligne de commande. Apple décrit xcrun comme le mécanisme permettant de trouver ou d’exécuter les outils de l’installation de développement sélectionnée. Vérifiez cette sélection avant d’examiner les modifications du code source. FAQ sur la compilation en ligne de commande avec Xcode.
Les commandes ci-dessous sont conçues pour Bash sur macOS. Elles ont été examinées, mais pas exécutées ici, car l’environnement de recherche est Linux et ne dispose pas de la chaîne d’outils Apple.
xcode-select --print-path
xcrun --sdk macosx --find clang
xcrun --sdk macosx --find clang++
xcrun --sdk macosx --show-sdk-path
Si ces vérifications échouent, commencez par résoudre le problème de sélection ou d’installation des outils de développement. La compilation nécessite le SDK macOS ainsi qu’un compilateur. Un SDK fournit les en-têtes et les bibliothèques de la plateforme utilisés pour compiler le produit. Référence des réglages de compilation Xcode : SDK de base.
Créer les fichiers source
Exécutez ce bloc de configuration dans Bash. Il crée un nouveau répertoire temporaire et affiche son emplacement, afin de ne pas toucher aux fichiers existants du projet. Continuez avec les blocs suivants dans le même shell.
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
L’en-tête déclare une liaison au langage C lorsqu’une unité de traduction C++ l’inclut. Ainsi, l’appelant C++ ne demande pas un nom de fonction C++ décoré différemment. La FAQ C++ recommande également de laisser le compilateur C++ diriger l’édition de liens finale afin d’inclure les bibliothèques nécessaires. FAQ C++ standard : comment mélanger C et C++.
Compiler, archiver et lier chaque architecture
Le bloc suivant compile les deux variantes macOS. Pour chaque architecture, il transmet la même cible et le même SDK à la compilation de la bibliothèque et à l’édition de liens de l’exécutable. Clang indique que -c produit un fichier objet ; sans option de sélection d’étape, son pilote poursuit jusqu’à l’édition de liens. Guide des commandes du compilateur 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
Ici, -target choisit l’architecture et la cible de déploiement macOS, tandis que -isysroot fournit la racine du SDK. Ce sont des paramètres distincts. Le chemin explicite de l’archive rend également visible la dépendance sélectionnée dans la commande. Guide de compilation croisée de Clang, Référence des arguments de ligne de commande de Clang.
La commande ar rcs regroupe l’objet compilé dans une archive statique et y ajoute un index de symboles. L’archivage regroupe des objets existants ; il ne les compile pas pour une autre cible. LLVM documente les opérations d’archivage et leur compatibilité avec les autres implémentations de ar. Documentation de l’archiveur LLVM.
Les valeurs d’entrée de l’exemple 3 et 4 doivent produire Tiles: 12. Cette minuscule fonction sert uniquement d’exemple d’édition de liens ; son interface ne définit pas le comportement pour des dimensions quelconques dont le produit dépasse la plage de int.
Comment inspecter l’architecture, la plateforme et les symboles ?
Inspectez l’exécutable, la bibliothèque ou le fichier objet réel. Apple documente lipo -archs pour lister les architectures et indique que l’exécutable d’une app se trouve dans le répertoire Contents/MacOS/ de son bundle. Créer un binaire macOS universel.
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
Les deux premières commandes doivent identifier leurs architectures respectives. Pour vérifier la plateforme, inspectez les commandes de chargement affichées par otool -l ; l’outil compatible d’LLVM, llvm-otool, documente la même option. Outil LLVM d’inspection de Mach-O.
Recherchez LC_BUILD_VERSION. Son platform doit indiquer macOS, et minos enregistre la version minimale du système d’exploitation. Le champ sdk est distinct. Certains binaires plus anciens utilisent plutôt une commande LC_VERSION_MIN_*. Les définitions du format Apple documentent les deux représentations, dont l’identifiant de plateforme macOS 1. Structures de version de compilation Mach-O.
Pour une bibliothèque statique, inspectez les objets membres au lieu de considérer l’enveloppe de l’archive comme une preuve de compatibilité de plateforme. Cet exemple conserve tile.o à cette fin. Pour une archive inconnue, utilisez xcrun ar t pour en lister les membres, puis inspectez-les dans un répertoire de travail distinct. LLVM documente les opérations de listage et d’extraction des archives. Documentation de l’archiveur LLVM.
Enfin, vérifiez les symboles. La bibliothèque d’exemple doit définir _tile_total sur Mach-O. Dans la sortie traditionnelle de nm, T indique un symbole de code global et U une référence non définie. Une définition et une référence constituent des preuves différentes. Documentation de la table des symboles LLVM.
Utilisez cette séquence de diagnostic pour choisir l’étape suivante :
- Tranche de processeur requise manquante : recompilez la dépendance pour cette architecture ou obtenez le binaire correspondant.
- Architecture correspondante, mais plateforme différente : obtenez une variante macOS native. Changer l’architecture de l’appelant ne garantit pas la compatibilité de plateforme.
- Architecture et plateforme correspondantes, mais fonction non résolue : comparez la fonction déclarée avec le symbole exporté par la bibliothèque et confirmez que l’archive prévue apparaît dans la commande d’édition de liens.
- Noms différents entre C et C++ : corrigez la liaison au langage dans l’en-tête partagé, puis recompilez l’appelant.
- Versions de déploiement différentes : examinez la version minimale de la dépendance et recompilez-la pour la cible de déploiement prévue si cette option est prise en charge, ou relevez délibérément les exigences de l’application.
Cette séquence est un outil d’aide à la décision fondé sur la documentation relative aux cibles, à Mach-O et à la liaison présentée ci-dessus. Elle distingue le code machine manquant des métadonnées de plateforme incompatibles et des problèmes d’interface de langage, afin que chaque recompilation réponde à un constat précis.
Qu’est-ce qui change lorsque Swift appelle la bibliothèque native ?
Une architecture et une plateforme correspondantes ne rendent pas toutes les interfaces de langage appelables. Swift prend en charge l’interopérabilité avec C par défaut ; l’interopérabilité directe avec C++ doit être activée et se limite à un sous-ensemble documenté des fonctionnalités C++. L’en-tête destiné à C dans cet exemple offre une interface prise en charge, à condition que la compilation Swift l’importe au moyen du mécanisme d’en-tête ou de module configuré. Mélanger Swift et C++.
Pour une interface C++ directe, Swift documente -cxx-interoperability-mode=default pour les compilations en ligne de commande, les modules Clang pour importer les en-têtes et -Xcc pour transmettre les paramètres de l’importateur C++. La configuration de l’importation et l’édition de liens de l’implémentation compilée restent des exigences de compilation distinctes. Configuration de projets Swift et C++ multilingues.
Respectez les limites documentées : l’interopérabilité C++ nécessite Swift 5.9 ou une version ultérieure et C++14 ou une version ultérieure. Le code Swift et C++ mélangé doit utiliser la même bibliothèque standard C++ ; les plateformes Apple utilisent libc++. Appeler Swift depuis C++ nécessite l’interface générée et un compilateur Clang pris en charge. Les types référence C++ importés imposent également une cible de déploiement macOS 13.3. Vérifiez l’API exacte dans la documentation de compatibilité actuelle avant de choisir cette voie. Fonctionnalités prises en charge et contraintes de l’interopérabilité C++.
Quand faut-il utiliser un binaire universel ou un XCFramework ?
Pour cet exécutable destiné uniquement à macOS, fusionnez les deux compilations macOS terminées en un binaire universel. Apple explique qu’il faut compiler et lier chaque architecture avant de combiner les exécutables obtenus avec lipo. Créer un binaire macOS universel.
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
Sur un Mac compatible exécutant macOS 13.0 ou une version ultérieure, la dernière commande doit afficher Tiles: 12 et se terminer avec succès. Compiler les deux tranches ne revient pas à tester les deux : un Mac Intel ne peut pas exécuter la tranche arm64. Guide d’Apple sur les binaires universels.
Pour une bibliothèque prenant en charge plusieurs plateformes Apple ou variantes de plateforme, utilisez un XCFramework pour regrouper des variantes compilées séparément. Apple recommande explicitement de conserver séparées les bibliothèques statiques iOS et Simulateur iOS, et permet d’inclure des variantes macOS natives et Mac Catalyst dans le même XCFramework. Pour les processus d’archivage Xcode, l’approche documentée consiste à sélectionner la plateforme et le SDK au moyen d’une destination. Créer un bundle de framework binaire multiplateforme.
Lors de votre prochain échec d’édition de liens macOS, consignez la cible prévue, inspectez l’architecture et la plateforme de la dépendance sélectionnée, puis vérifiez l’interface de langage. Conservez ces constats avec la commande exacte d’édition de liens. Vous disposerez ainsi d’une base concrète pour choisir le bon binaire ou le recompiler à partir du code source.
Sources
Compilation croisée avec Clang — LLVM Project.
FAQ sur la compilation en ligne de commande avec Xcode — Apple.
clang — le compilateur Clang pour C, C++ et Objective-C — LLVM Project.
Référence des arguments de ligne de commande de Clang — LLVM Project.
C++ standard : comment mélanger C et C++ — Standard C++ Foundation.
llvm-ar — archiveur LLVM — LLVM Project.
llvm-otool — outil d’inspection de Mach-O — LLVM Project.
llvm-nm — liste la table des symboles du bitcode LLVM et des fichiers objet — LLVM Project.
Mélanger Swift et C++ — Swift.org.
Configuration de projets Swift et C++ multilingues — Swift.org.
Fonctionnalités prises en charge et contraintes de l’interopérabilité C++ — Swift.org.
Créer un binaire macOS universel — Apple.
Créer un bundle de framework binaire multiplateforme — Apple.