Comment ajouter un framework Swift dynamique à un outil de ligne de commande

Passons en revue la façon dont j’ai essayé d’ajouter un cadre dynamique à mon outil de ligne de commande et discutons des problèmes rencontrés à chaque étape du processus.

Étape 1: ajoutez-le à la section "Frameworks and Libraries".

Voici ce qui se passe lorsque vous exécutez votre application:

dyld: la bibliothèque n'est pas chargée: @ rpath / libswiftAppKit.dylib
Référencé à partir de: /Users/seanberry/Library/Developer/Xcode/DerivedData/TestCommandLineTool-fnrmhjvmeaqvbklzwhqvuv/Build/Products/Debug/Tebird/ThirdParty.framework/Versions/A/Third
Raison: image non trouvée

ThirdParty.framework tente de trouver libswiftAppKit.dylib (qui fait partie des bibliothèques standard Swift) dans le répertoire @rpath.

Nous pouvons voir comment ThirdParty.framework définit @rpath en lançant

$ oTool -l ThirdParty.framework / Versions / Actuel / Troisième partie
Charger la commande 27
cmd LC_RPATH
cmdsize 48
path @executable_path /../ Frameworks (offset 12)
Charger la commande 28
cmd LC_RPATH
cmdsize 40
path @ loader_path / Frameworks (offset 12)

Bien tirer. Celles-ci ne sont pas pertinentes pour notre outil de ligne de commande. Nous n’avons aucun dossier nommé / Frameworks ni ../Frameworks. Pourquoi cherche-t-il les bibliothèques standard Swift ici?

Parce qu’il est conçu pour les applications iOS et Mac. Voici la structure de répertoires à l'intérieur d'une application Mac:

Cela explique path @executable_path /../ Frameworks

Et pour iOS, la structure de répertoires à l'intérieur de l'application est la suivante:

@loader_path est égal à @executable_path lorsque la structure est chargée à partir de l'exécutable

Et cela explique path @ loader_path / Frameworks (offset 12)

Mais qu'en est-il de nous, humble développeur d'outils de ligne de commande? Les bibliothèques standard Swift sont liées statiquement dans notre exécutable, mais nos structures tierces ne peuvent pas les trouver. Malheureusement, ils ne sont pas stockés dans un emplacement standard sur tous les Mac. Les développeurs peuvent y avoir accès enfouis dans Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx (mais n’obligez pas vos utilisateurs à installer Xcode).

Alors que faisons-nous? La pratique standard que j’ai vue consiste à créer un cadre personnalisé pour votre projet, à y importer toutes vos dépendances, puis à copier les bibliothèques standard swift.

Étape 2: Créez un cadre personnalisé et copiez les bibliothèques Swift standard

Cela copiera / collera les bibliothèques standard Swift dans FirstParty.framework / Versions / Current / Frameworks /Assurez-vous de copier également dans votre dépendance tierceMaintenant, vos cadres sont tous ensemble!

Parfait! Maintenant, allons de l'avant et exécutons notre outil de ligne de commande…

objem et / Users / seanberry / Library / Developer / Xcode / DerivedData / TestCommandLineTool-fnrmhjvjmugvqueaqvbklzwhqvuv / Build / Products / Debug / TestCommandLineTool (0x1005c7698). Un des deux sera utilisé. Lequel n'est pas défini.
RÉPÉTEZ L'ERREUR CI-DESSUS DE 20 FAÇONS DIFFÉRENTES

Votre application voit maintenant deux copies différentes des bibliothèques standard Swift: celles reliées statiquement dans votre exécutable et celles du dossier / Frameworks.

Il y a deux solutions à partir d'ici.

Étape 3 (option A): créez une application Mac à la place et extrayez le fichier exécutable.

J'ai étudié les outils de ligne de commande connus, Carthage et SwiftLint, pour voir comment ils géraient ce problème. Il s'avère qu'ils ne sont pas configurés en tant qu'outils de ligne de commande! Ce sont des applications Mac! Pourquoi? Car une application Mac ne lie pas statiquement les bibliothèques standard. Ils se déploient eux-mêmes en tant qu'outils de ligne de commande en ajoutant une phase d'exécution permettant d'extraire l'exécutable du package d'application.

#! / bin / bash
## Extrait l’outil CLI de carthage de son lot d’applications. Destiné à être exécuté
# dans le cadre d'une phase de construction du script d'exécution Xcode.
cp -v "$ {BUILT_PRODUCTS_DIR} / $ {EXECUTABLE_PATH}" "$ {BUILT_PRODUCTS_DIR} / $ {EXECUTABLE_NAME}"

Vous pouvez y aller et le faire de cette façon, pas de problème. Mais j'ai trouvé un autre moyen de contourner ce problème.

Étape 3 (option B): Désactiver la liaison statique

Ajoutez-les à vos paramètres de construction définis par l'utilisateur:

SWIFT_FORCE_DYNAMIC_LINK_STDLIB OUI
SWIFT_FORCE_STATIC_LINK_STDLIB NO

Cela forcera votre exécutable à lier dynamiquement toutes les bibliothèques. Assurez-vous d’indiquer à Xcode où les trouver en ajoutant le répertoire framework aux ‘chemins de recherche de chemins d’accès’.

@ executable_path / FirstParty.framework / Versions / Actuel / Frameworks

Bonne chance avec votre outil de ligne de commande!

Sean essaie de faire compiler Xcode sur Livefront