Давайте рассмотрим, как я пытался добавить динамическую структуру в свой инструмент командной строки, и обсудим, что пошло не так на каждом этапе этого пути.

Шаг 1. Добавьте его в раздел «Связанные платформы и библиотеки».

Вот что происходит, когда вы запускаете приложение:

dyld: Library not loaded: @rpath/libswiftAppKit.dylib
Referenced from: /Users/seanberry/Library/Developer/Xcode/DerivedData/TestCommandLineTool-fnrmhjvjmugvqueaqvbklzwhqvuv/Build/Products/Debug/ThirdParty.framework/Versions/A/ThirdParty
Reason: image not found

ThirdParty.framework пытается найти libswiftAppKit.dylib (который является частью стандартных библиотек Swift) в каталоге @rpath.

Мы можем увидеть, как ThirdParty.framework определяет @rpath, запустив

$ oTool -l ThirdParty.framework/Versions/Current/ThirdParty
Load command 27
cmd LC_RPATH
cmdsize 48
path @executable_path/../Frameworks (offset 12)
Load command 28
cmd LC_RPATH
cmdsize 40
path @loader_path/Frameworks (offset 12)

Хорошо стреляйте. Они не имеют отношения к нашему инструменту командной строки. У нас нет папок с именами / Frameworks или ../Frameworks. Почему он ищет там стандартные библиотеки Swift?

Потому что он создан для приложений iOS и Mac. Вот структура каталогов внутри приложения Mac:

Это объясняет path @executable_path/../Frameworks

А для iOS структура каталогов внутри приложения следующая:

И это объясняет path @loader_path/Frameworks (offset 12)

Но как насчет нас, скромных разработчиков инструментов командной строки? Стандартные библиотеки Swift статически связаны внутри нашего исполняемого файла, но наши сторонние платформы не могут их найти. К сожалению, они не хранятся в стандартном месте на каждом Mac. Разработчики могут получить к ним доступ, скрытый внутри Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx (но, пожалуйста, не требуйте от пользователей установки Xcode).

Так что же нам делать? Стандартная практика, которую я видел, - это создание пользовательского фреймворка для вашего проекта, импорт в него всех ваших зависимостей, а затем также копирование быстрых стандартных библиотек.

Шаг 2. Создайте собственный фреймворк и скопируйте стандартные библиотеки Swift.

Идеально! Теперь давайте запустим наш инструмент командной строки ...

objc[2335]: Class _TtC8Dispatch16DispatchWorkItem is implemented in both /Users/seanberry/Library/Developer/Xcode/DerivedData/TestCommandLineTool-fnrmhjvjmugvqueaqvbklzwhqvuv/Build/Products/Debug/FirstParty.framework/Versions/A/Frameworks/libswiftDispatch.dylib (0x1016c8530) and /Users/seanberry/Library/Developer/Xcode/DerivedData/TestCommandLineTool-fnrmhjvjmugvqueaqvbklzwhqvuv/Build/Products/Debug/TestCommandLineTool (0x1005c7698). One of the two will be used. Which one is undefined.
REPEAT THE ABOVE ERROR IN 20 DIFFERENT WAYS

Теперь ваше приложение видит две разные копии стандартных библиотек Swift: те, которые статически связаны внутри вашего исполняемого файла, и те, которые находятся в папке / Frameworks.

Отсюда есть два решения.

Шаг 3 (вариант A): вместо этого создайте приложение для Mac и извлеките исполняемый файл.

Я исследовал известные инструменты командной строки Carthage и SwiftLint, чтобы увидеть, как они справляются с этой проблемой. Оказывается, они не настроены как инструменты командной строки! Это приложения для Mac! Почему? Потому что приложение Mac не связывает статически стандартные библиотеки. Они развертываются как инструменты командной строки, добавляя этап выполнения, который извлекает исполняемый файл из пакета приложения.

#!/bin/bash
## Extracts the carthage CLI tool from its application bundle. Meant to be run
# as part of an Xcode Run Script build phase. 
cp -v "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME}"

Вы можете пойти дальше и сделать это так, без проблем. Но я нашел другой способ обойти эту проблему.

Шаг 3 (вариант B): отключить статическое связывание

Добавьте их в свои пользовательские настройки сборки:

SWIFT_FORCE_DYNAMIC_LINK_STDLIB YES
SWIFT_FORCE_STATIC_LINK_STDLIB NO

Это заставит ваш исполняемый файл динамически связывать все библиотеки. Обязательно сообщите Xcode, где их найти, добавив каталог фреймворка в «Пути поиска пути выполнения».

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

Удачи вам с вашим инструментом командной строки!

Шон пытается заставить Xcode скомпилировать в Livefront