Встраиваем pre-built dylib в приложение

в 8:22, , рубрики: dylib, iOS, osx, xcode, разработка под iOS, Разработка под OS X

Перед началом майских праздников я провел 5 плотных дней за написанием приложения под iOS и Mac для конкурса. Разработка шла планомерно, к последнему дню работа приложения меня начала более-менее удовлетворять. Я решил отложить отправление на утро самого последнего дня, чтобы сделать это со свежой головой (там нужно было еще приложить текстовое описание). Утром, за несколько часов до поезда в отпуск, я сел и со спокойной душой сделал финальную обкатку основного функционала. И тут мне показалось хорошей идеей потестить, как будет вести себя приложение на абсолютно другой машине, на которой не велась разработка. Я архивирую свое приложение, перекидываю его на другой Mac и… оно не запускается. С супер-информативной ошибкой "Image not found" и путём явно указывающим на проблему с dylib. Итак: 3 часа до поезда и не запускающиеся по непонятным причинам приложение. Почему это произошло, как этого можно было бы избежать и как я справился с этой проблемой — обо всём этом под катом.

В суте проблемы я разобрался довольно быстро, а вот её решение заняло некоторое время. Давайте все же разбремся, что же случилось. В моём приложении использовалась OpenCV. Я собрал её из оригинальных сорсов с дефолтным конфигом прямиком с официального сайта, используя CMake. После этого, указал пару Search Path в XCode и разработка продвигалась, что называется, seamless. Как вы уже наверное поняли, проблема заключалась в том, что на рабочей машине мое приложение использовала библиотеки, которые лежали по адресу /usr/local/lib/.

Именно этот путь был указан для поиска в XCode и т.к. я слинковал их с приложением, я ошибочно полагал, что все необходимые библиотеки подтянуться во время архивации, теперь я уже знаю, что это не так. Первая мысль, которая пришла мне тогда: сделать инсталлятор. В принципе, это рабочее решение и многие именно так и поступают. Однако на это не было времени, т.к. помимо затрат времени на разработку самого инсталлятора, потребовалось еще тестирование и т.д., а поезд отъезжал через пару часов.

Я начал гуглить эту проблему и понял, что мне надо закинуть dylib в само приложение. Я недолго думая, закинул их в XCode проект, добавил linkage, заархивировал… на рабочей машине всё ок, на сторонней — всё та же ошибка. Проблема была в зашитих в dylib путях установки и поиска зависимостей. В моём случае это был монстроузный

/Users/s1ddok/Downloads/opencv-3.1.0/build/lib/libopencv_core.3.1.dylib

Это потому что я добавил в проект промежуточные dylib из папки build, это те, которые в последствии устанвливаются командой make install, но даже, если бы я взял их из /usr/local/lib/, это не решило проблему.

Как можно было бы избежать эту проблему? Указывать сразу необходимые пути установки во время компиляции dylib. Если Вы комплируете свои библиотеки, скорее всего у Вас уже всё правильно установлено, если же нет, это легко сделать в XCode. Конкретно в случае с OpenCV нужно было всего лишь изменить дефолтный конфиг CMake и добавить туда префикс для installation dir, но возможности сделать этого на тот момент у меня не было.

Итак мне нужно было как-то изменить install path в уже готовых dylib, чтобы они запускались. Я опущу подробные описания того, как я пришёл к результату, лишь расскажу, что же в итоге сработало.

Первым делом нам нужно, чтобы наши dylibs, которые мы добавили в проект, копировались в бандл с нашим приложением. В принципе, все равно куда, поидее можно хоть в main bundle их засунуть, но логичны кажется копирование в папку Frameworks. Для этого идем в XCode -> Target Settings -> Build Phases и добавляем новую фазу Copy Files. Мое приложение используёт только 2 библиотеки из OpenCV набора, поэтому у меня это выглядит вот так:

image

Ок, теперь внутри нашего приложения хранятся все нужные dylib, однако оно вываливается с той же ошибкой, потому что пути в них все еще старые. На помощь нам приходят такие утилиты как otool и install_name_tool. Первая нужна была для того, чтобы узнать какие install path "зашиты" в имеющиеся библиотеки, а вторая для того, чтобы их изменить. Добавляем новую фазу сборки в XCode, на этот раз Run Script. Сам скрипт получился вот таким:

# Говорим куда смотреть нашему приложению в поисках библиотек
install_name_tool -change lib/libopencv_core.3.1.dylib @executable_path/../Frameworks/libopencv_core.3.1.0.dylib $BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH
install_name_tool -change lib/libopencv_imgproc.3.1.dylib @executable_path/../Frameworks/libopencv_imgproc.3.1.0.dylib $BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH

# Переходим в папку Frameworks внутри нашего приложения
cd $BUILT_PRODUCTS_DIR
cd MyApp.app
cd Contents
cd Frameworks

# Здесь мы говорим библиотекам куда они установлены
install_name_tool -id @executable_path/../Frameworks/libopencv_core.3.1.0.dylib libopencv_core.3.1.0.dylib
install_name_tool -id @executable_path/../Frameworks/libopencv_imgproc.3.1.0.dylib libopencv_imgproc.3.1.0.dylib

# Следующей линией можно использоваться, чтобы посмотреть, какие зависимости имеет эта библиотека.
# otool -L libopencv_imgproc.3.1.0.dylib
# В моем случае она зависит от opencv-core и нам нужно сказать ей, где она лежит
install_name_tool -change /Users/s1ddok/Downloads/opencv-3.1.0/build/lib/libopencv_core.3.1.dylib @executable_path/../Frameworks/libopencv_core.3.1.0.dylib libopencv_imgproc.3.1.0.dylib

Это, конечно же, ужасное решение и скрипт придется переделывать под каждую библиотеку, он не универсален. На тот момент у меня не было времени разрабатывать generic-решение.

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

Автор: s1dd0k

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js