Перед началом майских праздников я провел 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 мин без проверки, но это уже мелочи.

Поделиться с друзьями
-->

Комментарии (6)


  1. iSage
    10.05.2016 11:44

    https://github.com/auriamg/macdylibbundler


    1. s1dd0k
      10.05.2016 20:27

      Поставил звездочку, буду знать, спасибо!


  1. creker
    10.05.2016 12:00

    Странно, что на это не наткнулись http://stackoverflow.com/questions/31654459/how-to-attach-library-to-ios-project/31655448#31655448 Может теги iOS помешали


  1. 1101_debian
    10.05.2016 12:35

    Что мешало собрать OpenCV в статику и слинковать со статической либой?


    1. DeXPeriX
      10.05.2016 18:36

      Я не автор, но у меня со статической линковкой есть проблемы. Если статически линкуюсь с SDL, то приложение крэшится при использовании звука. В Windows и Linux всё работает хорошо. Правда, я кросскомпилирую из Linux. Но это не мешает тому же OpenGL работать корректно…


    1. s1dd0k
      10.05.2016 20:34

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