Собираем Акулу под iOS и OSX

    image
    Да, такой заголовок не очень-то наполнен смыслом, поэтому дам несколько пояснений, для тех кто еще думает «идти под кат или нет?».

    Shark — набор С++ библиотек машинного обучения (Machine Learning), а именно линейная и нелинейная оптимизация, нейронные сети, обучение с учителем и без, эволюционные алгоритмы и многое другое. Более детальное описание можно найти на сайте проекта.

    Если вас интересуют ответы на следующие вопросы
    • Как собрать статическую библиотеку для iOS?
    • Как создать фреймворк для iOS?
    • Как опубликовать iOS и OSX фреймворк через CocoaPods?


    Тогда вам под кат.
    .

    Зачем?


    Можно было бы просто ответить, — «Чтобы было больше библиотек хороших и полезных для iOS», но у меня была и другая причина. Работая над настольной игрой под iOS мне захотелось использовать машинное обучение для разработки Искусственного Интеллекта (ИИ) компьютерного противника.

    План был простой, сначала обучить ИИ, прогнав нейронную сеть через десятки тысяч игр, запуская их на мощном железе под Mac OS, затем взять обученную сеть и использовать ее в приложении на iPhone/iPad. Первую часть плана реализовать было сравнительно просто, хотя бы потому, что Shark версии 2.3.4 можно установить на мак с помощью brew install shark.

    Тут самое время отметить, что в статье идет речь о версии библиотеки 2.3.4. Сам проект уже далеко ушел от 2.3.4, на данный момент доступна бета версия 3.0. Код новой версии значительно переработан, использует boost в качестве сторонней библиотеки, так что это совсем другая история, тем более в контексте iOS.


    Вернемся ко второй части плана. После обучения у меня на руках остается файл с коэффициентами нейронной сети. Формат этого файла нигде не описан (или я плохо искал), поэтому наименее затратный способ его прочитать и использовать — с помощью того же Shark, а значит нужно портировать библиотеку под iOS.

    Как?


    Короткий ответ — взять исходники и собрать, подробный ответ — статья на Хабре.

    Понятное дело, прежде чем писать на Хабр, я сначала собрал библиотеку и оформил все это дело в виде репозитория на GitHub. Поэтому для наглядности буду приправлять пост кусками кода из скрипта сборки.

    Весь процесс можно разбить на следующие этапы
    • Скачать исходный код
    • Пропатчить исходный код
    • Сконфигурировать и собрать
    • Сделать «толстую» библиотеку
    • Упаковать в фреймворк
    • Опубликовать на CocoaPods


    Да, именно, мы не просто соберем «толстую» (aka «жирную» или fat) статическую библиотеку для разработки под iOS устройства и симулятор (и отдельно для OSX), мы также оформим все в виде фреймворка и сделаем его доступным через CocoaPods.

    Ну что ж, приступим, шаг за шагом.

    Скачать исходный код

    Тут все просто, не буду сильно вдаваться в детали, чтобы не получилось чересчур разжевано, качаем и распаковываем архив.
    Скачать и распаковать
    VERSION=2.3.4
    SHARK_ZIP=shark-$VERSION.zip
    # ---
    download() {
            if [ ! -s $SHARK_ZIP ]; then
            echo Downloading shark source code $SHARK_ZIP...
            curl -L --progress-bar -o $SHARK_ZIP "http://sourceforge.net/projects/shark-project/files/Shark%20Core/Shark%20${VERSION}/shark-${VERSION}.zip/download"
    
        else
                echo Source code $SHARK_ZIP already downloaded...
        fi
        doneSection
    }
    
    SRC_DIR=src
    # ---
    unpackSource() {
            echo Unpacking $SHARK_ZIP to $SRC_DIR...
            [ -d $SRC_DIR ] || unzip -q $SHARK_ZIP -d $SRC_DIR
            [ -d $SRC_DIR ] && echo " ...unpacked as $SRC_DIR"
            doneSection
    }
    



    Пропатчить исходный код

    Изначально код разрабатывался с помощью gcc 4.2, мы же пытаемся скомпилировать его используя clang версии 5.0. С момента выхода gcc 4.2 компиляторы и стандарт C++ ушли далеко вперед, поэтому нет ничего удивительного в том, что clang-у прийдутся не по вкусу некоторые куски кода. Не остается ничего другого кроме как исправить, т.е. пропатчить, проблемный код.

    Конструктор по умолчанию

    В первый раз компилятор споткнется на 78-й строке в файле ReClaM/EarlyStopping.cpp.
    EarlyStopping::EarlyStopping(unsigned sl = 5)
    

    Конструктор EarlyStopping имеет единственный аргумент (unsigned sl) и для этого аргумента прописано дефолтное значение (5). Таким образом, этот конструктор становится конструктором по умолчанию, «ну и пусть» — скажет gcc 4.2, но у clang-а по этому поводу совсем другое мнение. На StackOverflow можно найти обсуждение этой ошибки.

    Исправлять будем «в лоб», т.е. просто уберем дефолтное значение.
    EarlyStopping::EarlyStopping(unsigned sl)
    

    Секундочку! — скажите вы, — а что если этот конструктор используется где-то в коде библиотеки без аргумента?
    Абсолютно справедливый вопрос. К счастью, этот конструктор нигде больше в коде не вызывается (как явно, так и неявно), значит вреда от такого изменения не будет, но нужно быть внимательным при его вызове в своем коде и обязательно передавать значение для sl.

    finite не доступен для iOS

    Следующая ошибка — функция finite(x) не доступна (не включена / not included) для iOS, как для устройств, так и для симулятора. В сети можно найти упоминания об этой проблеме, например здесь.

    Решение — использовать функцию isfinite(x). Переопределим finite(x) как isfinite(x) с помощью препроцессорных макросов, для этого добавим следующий код в SharkDefs.h
    SharkDefs.h
    #if defined(__APPLE__) && defined(__MACH__)
    /* Apple OSX and iOS (Darwin). */
    #include <TargetConditionals.h>
    #if TARGET_IPHONE_SIMULATOR == 1
    /* iOS in Xcode simulator */
    #define finite(x) isfinite(x)
    #elif TARGET_OS_IPHONE == 1
    /* iOS on iPhone, iPad, etc. */
    #define finite(x) isfinite(x)
    // #define drem(x, y) remainder(x, y)
    #elif TARGET_OS_MAC == 1
    /* OSX */
    #endif
    #endif
    



    Логика здесь следующая, если компилируем под одну из операционок Apple, т.е. определены __APPLE__ и __MACH__, тогда включаем заголовочный файл TargetConditionals.h, после чего проверяем значения TARGET_IPHONE_SIMULATOR, TARGET_OS_IPHONE и TARGET_OS_MAC, и переопределяем finite(x) для iOS устройства и симулятора.

    Точно такая же проблема с функцией drem(x, y), которую нужно заменить на remainder(x, y). Однако Shark drem(x, y) не использует, так что это просто информация к сведению.

    Исправления в FileUtil

    Следующий ошибки связаны с модулем FileUtil, а именно с файлом FileUtil.h.

    Сначала clang не сможет найти определения типа iotype и констант SetDefault, ScanFrom и PrintTo. Дадим компилятору подсказку используя пространство имен (namespace), т.е. выполним простую замену там где это нужно
    iotype -> FileUtil::iotype
    SetDefault -> FileUtil::SetDefault
    ScanFrom -> FileUtil::ScanFrom
    PrintTo -> FileUtil::PrintTo
    


    Последняя проблема в этом файле — функция io_strict, которая вызывает функции scanFrom_strict и printTo_strict, объявленные позже.
    Решение? Просто переместим io_strict в конец файла и нет проблем.

    double erf(double) throw();

    Компиляция Mixture/MixtureOfGaussians.cpp обозначит следующий код как ошибочный
    extern "C" double erf(double) throw();
    


    extern "C" декларация не совпадает с оригинальным прототипом.
    В данном случае проблема решается путем удаления throw().
    extern "C" double erf(double);
    


    Я отдаю себе отчет, что некоторым исправлениям я не даю подробного объяснения, как например удаление throw(). Буду рад замечаниям в комментариях.


    RandomVector this->p

    Следующий на очереди файл Mixture/RandomVector.h, строка 72. Компилятор понятия не имеет как быть с ссылкой на p. Также настораживает комментарий к коду в этой строке (// !!!).
    for (unsigned k = x.dim(0); k--;) {
        l += log(Shark::max(p(x[ k ]), 1e-100));    // !!!
    }
    

    Проанализировав код, я нашел эту самую p. RandomVector наследует RandomVar, у которого объявлен «потерянный» метод p.
    В поисках p
    // RandomVar.h
    template < class T >
    class RandomVar
    {
    public:
        // ***
        virtual double p(const T&) const = 0;
        // ***
    }
    
    // RandomVector.h
    template < class T >
    class RandomVector : public RandomVar< Array< T > >
    {
    public:
        // ***
    }
    



    Напрашивается конструкция this->p.
    for (unsigned k = x.dim(0); k--;) {
        l += log(Shark::max(this->p(x[ k ]), 1e-100));    // !!!
    }
    


    CMakeLists.txt

    Последний патч не имеет ничего общего с ошибками компиляции.
    Поскольку мы хотим получить библиотеку для iOS, эта библиотека должна быть статической, однако проект по умолчанию соберет динамическую библиотеку. Чтобы получить нужный результат, заменим одну строку в файле CMakeLists.txt.
    ADD_LIBRARY( shark SHARED ${SRCS} )
    # заменить на
    ADD_LIBRARY( shark STATIC ${SRCS} )
    

    В принципе, можно не заменять строку с «SHARED», а просто добавить еще одну с «STATIC», тогда в результате будет собрана и динамическая и статическая библиотеки.

    Применяем патч

    Конечно же, если вы надумаете повторить этот процесс, нет смысла ковырять код вручную. Для экономии времени и нервов есть уже готовый патч.
    Если вам интересно, то получен этот патч с помощью утилиты diff
    diff -crB shark_orig shark_patched > shark.patch
    

    А чтобы применить это патч, нужно использовать утилиту, внимание, patch
    SRC_DIR=src
    # ---
    patchSource() {
            echo Patching source code...
            patch -d $SRC_DIR/Shark -p1 --forward -r - -i ../../shark.patch
            doneSection
    }
    


    Сконфигурировать и собрать

    Наконец-то с патчами покончено, можно приступить непосредственно к сборке.
    Собирать проект будем с помощью make, осталось только правильно все настроить для каждой платформы, в этом нам поможет утилита cmake (configure make), которая создаст Makefile и другие файлы, необходимые make. Нам придется конфигурировать и собирать библиотеку 3 раза, для следующих платформ и архитектур
    • iOS устройства — архитектуры armv7, armv7s, arm64
    • iOS симулятор — архитектуры i386 и x86_64
    • OSX — архитектура x86_64


    Да, немаловажный момент, мы будем использовать Xcode 5. Поддержка архитектуры armv6 официально удалена из Xcode 5, так что мы ее совсем не рассматриваем.


    Библиотеки для iOS устройства и симулятора мы после сольем в одну «толстую» библиотеку, которую можно будет использовать одновременно для разработки на устройствах и симуляторе без лишних телодвижений.

    Основы cmake

    Итак, cmake. Для каждой платформы, нам необходимо правильно настроить окружение, используя следующие настройки
    • C++ компилятор (CMAKE_CXX_COMPILER)
      По умолчанию используется /usr/bin/c++, нам же нужно использовать clang++ из инструментария (toolchain) Xcode.
    • Флаги С++ компилятора (CMAKE_CXX_FLAGS)
      Именно с помощью этих флагов мы установим необходимые архитектуры и другие важные параметры компиляции.
    • C компилятор (CMAKE_C_COMPILER)
      Несмотря на отсутствие чистого С кода, все равно необходимо правильно сконфигурировать этот параметр.
    • Корневая системная директория (CMAKE_OSX_SYSROOT)
      С помощью этой настройки, мы сообщим компилятору где искать все стандартные системные библиотеки для выбранной платформы.
    • Префикс установки (CMAKE_INSTALL_PREFIX)
      Этот параметр опциональный. Он используется командной make install, но мы этого делать не будем.
    • Генератор сборочных файлов (флаг -G)
      С помощью этого флага мы можем выбрать одну из многих систем сборки. В нашем случае это будет «Unix Makefiles», т.е. cmake сгенерирует привычный Makefile. Логично было бы использовать Xcode проект, но мне не удалось собрать все таким способом.


    Инструментарий Xcode

    Может быть и не самый удачный перевод термина Toolchain, но уж точно лучше, чем «цепочка инструментов». Xcode содержит все необходимые нам инструменты (компиляторы, библиотеки для iPhone OS SDK и iPhone Simulator SDK, и т.д.) Ясное дело, Xcode должен быть установлен, также нужно установить тот самый инструментарий (Xcode Command Line Tools), можно это сделать в настройках самого Xcode или с помощью команды xcode-select --install.

    Теперь используем xcode-select, чтобы найти все нужные инструменты и директории.
    Инструменты Xcode
    # здесь все инструменты разработчика
    XCODE_ROOT=$(xcode-select -print-path)
    # корневая папка iPhone OS SDK
    XCODE_ARM_ROOT=$XCODE_ROOT/Platforms/iPhoneOS.platform/Developer
    # корневая папка iPhone Simulator SDK
    XCODE_SIM_ROOT=$XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer
    # здесь все основные утилиты, такие как...
    XCODE_TOOLCHAIN_BIN=$XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin 
    # C++ компилятор
    CXX_COMPILER=${XCODE_TOOLCHAIN_BIN}/clang++
    # C компилятор
    C_COMPILER=${XCODE_TOOLCHAIN_BIN}/clang 
    



    В случае Mac OS X SDK, cmake достаточно умен чтобы найти корневую системную директорию самостоятельно, без дополнительных указаний.

    Далее для каждой платформы по-порядку.

    iOS устройства (iPhone OS SDK)
    Собирать будем в отдельной папке, скажем build/ios.

    Компиляторы мы уже нашли, теперь определимся с флагами для С++ компилятора. Поскольку мы будем собирать для armv7, armv7s и arm64 архитектур, мы воспользуемся специальным флагом -arch для clang++.

    Все необходимые системные библиотеки для ARM архитектур находятся в SDKs/iPhoneOS7.0.sdk поддиректории XCODE_ARM_ROOT
    CXX_FLAGS="-arch armv7 -arch armv7s -arch arm64"
    SYSTEM_ROOT=${XCODE_ARM_ROOT}/SDKs/iPhoneOS7.0.sdk
    


    Вызов cmake будет выглядеть следующим образом
    mkdir -p build/ios
    cd build/ios
    
    cmake \
      -DCMAKE_CXX_COMPILER=$CXX_COMPILER \
      -DCMAKE_OSX_SYSROOT="$SYSTEM_ROOT" \
      -DCMAKE_C_COMPILER=$C_COMPILER \
      -DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
      -G "Unix Makefiles" \
      ../../src/Shark
    


    Кстати, cmake выполнит за нас много дополнительной работы. Нам достаточно указать только C и C++ компиляторы, cmake же найдет все остальные утилиты из того же инструментария, например ranlib, lipo, ld и т.д.


    iOS симулятор (iPhone Simulator SDK)
    Те же самые компиляторы.

    Нужные нам архитектуры на этот раз i386 и x86_64, последняя нужна для тестирования на симуляторе устройств с 64-битной архитектурой, например «iPhone Retina (4-inch 64-bit)».

    Нужные библиотеки — в ${XCODE_SIM_ROOT}/SDKs/iPhoneSimulator7.0.sdk.

    И еще один очень важный момент, чтобы код собирался именно для симулятора, а не OS X с такой же архитектурой, компилятору нужно дать дополнительную подсказу с помощью флага -mios-simulator-version-min=7.0

    mkdir -p build/sim
    cd build/sim
    
    CXX_FLAGS="-arch i386 -arch x86_64 -mios-simulator-version-min=7.0"
    SYSTEM_ROOT=${XCODE_SIM_ROOT}/SDKs/iPhoneSimulator7.0.sdk
    
    cmake \
      -DCMAKE_CXX_COMPILER=$CXX_COMPILER \
      -DCMAKE_OSX_SYSROOT="$SYSTEM_ROOT" \
      -DCMAKE_C_COMPILER=$C_COMPILER \
      -DCMAKE_CXX_FLAGS="$CXX_FLAGS" \
      -G "Unix Makefiles" \
      ../../src/Shark
    


    Mac OS X
    Ну и наконец Mac OS X.
    На этот раз достаточно указать только С и С++ компиляторы, все остальное cmake найдет сам.
    mkdir -p build/osx
    cd build/osx
    
    cmake \
      -DCMAKE_CXX_COMPILER=$CXX_COMPILER \
      -DCMAKE_C_COMPILER=$C_COMPILER \
      -G "Unix Makefiles" \
      ../../src/Shark
    


    Хитрости cmake

    Ну теперь-то уж давай make!, — подумаете вы, но нет.
    У cmake есть пара особенностей, которые необходимо учесть.

    Тест компиляторов
    Во-первых, при самом первом запуске cmake выполняет тест для C и С++ компиляторов. Как ни странно, clang и clang++ этот тест успешно проваливают для iOS платформ. В интеренете есть несколько рекомендаций, как обойти этот тест, например в файле CMakeLists.txt добавить NONE в описание проекта, но мне это не помогло.
    PROJECT( shark NONE )
    


    Способ, который сработал для меня — сначала запустить cmake без указания clang и clang++ компиляторов и других опций. cmake сгенерирует файл CMakeCache.txt и директорию CMakeFiles. Если теперь запустить cmake уже с различными настройками, тест компиляторов на этот раз выполняться не будет.

    Изменение конфигурации
    Итого, cmake нужно запустить как минимум 2 раза, — опять подумаете вы.
    И опять — нет.

    Если при очередном запуске были изменены важные параметры, такие как C и С++ компиляторы, изменения не вступят в силу сразу же. cmake как бы выдаст предупреждение и посоветует запустить его еще раз. На самом деле cmake ничего такого не скажет, а вот утилита ccmake предоставит больше информации. ccmake это консольный графический интерфейс для cmake.

    В общем
    • Раз cmake, без параметров, чтобы пройти тест
    • Два cmake, с нужными параметрами
    • Три cmake, чтобы изменения вступили в силу


    make

    Да, теперь можно выполнить заветный make. Чтобы дело пошло быстрее, можно распараллелить сборку с помощью флага -j.
    make -j16
    

    Много времени этот процесс не займет, в результате у нас на руках окажется статическая библиотека libshark.a. На всякий пожарный, с помощью утилиты file убедимся что полученные библиотеки поддерживают все необходимые архитектуры.
    Проверка жирности
    $ file build/ios/libshark.a
    build/ios/libshark.a: Mach-O universal binary with 3 architectures
    build/ios/libshark.a (for architecture armv7):  current ar archive random library
    build/ios/libshark.a (for architecture armv7s): current ar archive random library
    build/ios/libshark.a (for architecture cputype (16777228) cpusubtype (0)):  current ar archive random library
    
    $ file build/sim/libshark.a
    build/sim/libshark.a: Mach-O universal binary with 2 architectures
    build/sim/libshark.a (for architecture i386): current ar archive random library
    build/sim/libshark.a (for architecture x86_64): current ar archive random library
    
    $ file build/osx/libshark.a
    build/osx/libshark.a: current ar archive random library
    



    Сделать «толстую» библиотеку

    Библиотеки для iOS устройств и симулятора нужно объединить в одну «толстую» библиотеку.

    Тут все достаточно просто, с этой задачей справится утилита lipo
    mkdir -p lib/ios
    $XCODE_TOOLCHAIN_BIN/lipo -create build/ios/libshark.a build/sim/libshark.a -o lib/ios/libshark.a
    

    На всякий случай проверьте полученную библиотеку (file lib/ios/libshark.a), чтобы убедиться что все 5 архитектур на месте.

    Упаковать в фреймворк

    Пришло время аккуратно упаковать библиотеку в виде фреймворка. Директорию фреймворка часто называют «бандл» (bundle).

    Создаем бандл

    На этом этапе создадим директорию Shark.framework и необходимую структуру папок внутри, со всеми нужными символическими ссылками, используя для этого mkdir и ln.
    Shark.framework/
    ├── Documentation -> Versions/Current/Documentation
    ├── Headers -> Versions/Current/Headers
    ├── Resources -> Versions/Current/Resources
    └── Versions
        ├── A
        │   ├── Documentation
        │   ├── Headers
        │   └── Resources
        └── Current -> A
    


    Копируем библиотеку

    Теперь скопируем статическую библиотеку внутрь бандла, при этом переименуем ее в Shark.
    cp build/ios/libshark.a Shark.framework/Versions/A/Shark
    


    Копируем заголовочные файлы

    Далее копируем все .h файлы из src/Shark/include в Headers папку внути бандла. Затем удалим ненужный statistics.h. Не нужен этот файл хотя бы потому, что для него нет соответствующей INSTALL команды в файлe CMakeLists.txt.
    cp -r src/Shark/include/* Shark.framework/Headers/
    rm Shark.framework/Headers/statistics.h
    


    Патчим заголовочные файлы
    Казалось бы, скопировали хедеры и все, но опять все не так очевидно. Если попытаться использовать фреймворк прямо сейчас, выскочит несколько непонятных ошибок связанных с путями к заголовочным файлам. И если в случае с динамической библиотекой под OS X мне удалось обойти проблему используя Header Search Path, то в случае с фреймворком все не так просто.

    Раз разработчики Shark не позаботились о правильных путях для #include директив, нам придется выполнить эту работу за них.

    В поисках правильного подхода, я обратил свое внимание на пример хорошо организованной библиотеки, а именно — boost. Все пути в #include директивах для компонентов из boost начинаются с boost/, например
    #include "boost/config.hpp"
    #include <boost/type_traits/remove_reference.hpp>
    


    Используем редактор sed и пропатчим файлы используя следующие правила для всех #include директив
    • Добавим недостающий пробел в "#include<something>" и "#include«something»" (да, были и такие)
    • Заменим SharkDefs.h, на Shark/SharkDefs.h
    • Добавим Shark/ в пути для всех компонентов библиотеки


    Под компонентами здесь имеются ввиду поддиректории папки Headers, т.e. Array, Rng, LinAlg, FileUtil, EALib, MOO-EALib, ReClaM, Mixture, TimeSeries, Fuzzy.

    Патчим .h файлы
    # поможет избежать ошибок типа "invalid character sequence"
    export LC_TYPE=C
    export LANG=C
    
    # добавить недостающие пробелы в #include директивах
    # исправить путь для SharkDefs.h
    # исправить пути для всех компонентов
    # использовать -E для современного синтаксиса регулярных выражений, также поможет избежать проблем из серии "gnu vs non-gnu sed"
    components="Array|Rng|LinAlg|FileUtil|EALib|MOO-EALib|ReClaM|Mixture|TimeSeries|Fuzzy"
    find Shark.framework/Headers -type f -exec \
        sed -E -i '' \
        -e "s,#include([<\"]),#include \1,g" \
        -e "s,#include([ \t])([<\"])(SharkDefs.h),#include\1\2Shark/\3,g" \
        -e "s,#include([ \t])([<\"])(${components}/),#include\1\2Shark/\3,g" \
        {} +
    



    Создаем Info.plist

    Последний этап в создании фреймворка — создать соответствующий Info.plist. Важными свойствами являются имя и версия фреймворка.
    Создать Info.plist
    FRAMEWORK_NAME=Shark
    FRAMEWORK_CURRENT_VERSION=2.3.4
    
    cat > Shark.framework/Resources/Info.plist <<EOF
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleExecutable</key>
        <string>${FRAMEWORK_NAME}</string>
        <key>CFBundleIdentifier</key>
        <string>dk.diku.image</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundlePackageType</key>
        <string>FMWK</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
        <string>${FRAMEWORK_CURRENT_VERSION}</string>
    </dict>
    </plist>
    EOF
    



    До выхода iOS 7, а именно устройств с архитектурой arm64, существовала возможность создания универсальных статических библиотек, а значит и фреймворков, одновременно для iOS и OSX, включая симулятор. Ничто не мешало упаковать armv6, armv7, armv7s, i386 и x86_64 в одну жирную библиотеку. Устройства с 64-битной архитектурой спутали все карты. В «толстой» библиотеке не может быть два слоя с одинаковой архитектурой, а значит не получится запихать x86_64 для iOS симулятора 64-битных устройств и x86_64 для OS X.


    Опубликовать на CocoaPods

    Уже прямо сейчас можно брать готовый фреймворк и смело добавлять его в свои проекты, но хочется автоматизировать и этот этап тоже. Как раз для этого и нужны CocoaPods. Если вы по какой-то причине отстали от поезда и еще не используете CocoaPods, самое время обратить на этот проект внимание.

    Создание спецификации для пода — отдельная история. Несмотря на то, что я практически расписал каждую мелочь до этого момента, здесь я сошлюсь на готовый результат.
    Shark-SDK.podspec
    Pod::Spec.new do |s|
      s.name = 'Shark-SDK'
      s.version = '2.3.4'
      s.license = { :type => 'GPLv2', :text => 'See https://sourceforge.net/directory/license:gpl/' }
      s.summary = 'iOS & OS X framework for Shark: C++ Machine Learning Library'
      s.description = <<-DESC
                          SHARK provides libraries for the design of adaptive systems, including methods for linear and nonlinear optimization (e.g., evolutionary and gradient-based algorithms), kernel-based algorithms and neural networks, and other machine learning techniques.
                         DESC
      s.homepage = 'https://sourceforge.net/projects/shark-project/'
      s.author = { 'shark-admin' => 'https://sourceforge.net/u/shark-admin/profile/' }
    
      s.source = { :http => 'https://github.com/mgrebenets/shark2-iosx/releases/download/v2.3.4/Shark-SDK.tgz', :type => :tgz }
    
      s.ios.vendored_frameworks = "Shark-iOS-SDK/Shark.framework"
      s.osx.vendored_frameworks = "Shark-OSX-SDK/Shark.framework"
    
      s.ios.deployment_target = '6.0'
      s.osx.deployment_target = '10.7'
    end
    



    И пример использования.
    Podfile
    # Podfile
    platform :ios, :deployment_target => '6.0'
    pod 'Shark-SDK'
    
    target :'shark2-osx-demo', :exclusive => true do
        platform :osx, :deployment_target => '10.7'
        pod 'Shark-SDK'
    end
    



    Итоги


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

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 6

      +4
      А есть ли какая-нибудь хорошая статья про саму библиотеку? Про возможности/применение и.т.д.?
        +1
        А под iOS 7 64 bit будет работать?
          0
          Да, конечно, это как раз и есть arm64 архитектура.
          0
          Спасибо за статью!

          Не очень понял по поводу:
          Таким образом, этот конструктор становится конструктором по умолчанию, «ну и пусть» — скажет gcc 4.2, но у clang-а по этому поводу совсем другое мнение.

          Что именно не нравится clang'у? Мне кажется, что последующая ссылка ведет не туда или нет?
            0
            Да, ссылка неправильная. Я подправил статью, вот то самое обсуждение.
            Сообщение об ошибке будет такое
            error: addition of default argument on redeclaration makes this constructor a default constructor
              0
              Тут clang совершенно прав. Я бы перенёс значение по-умолчанию из определения в объявление.

          Only users with full accounts can post comments. Log in, please.