Как стать автором
Обновить

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

Разработка под iOS
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



Итоги


Итого, если вы вдруг придумали сами себе какую-ту задачу, результат которой, мягко говоря, не гарантирован, все равно попытайтесь что-то сделать. Никогда не знаешь какие новые скилы удастся в результате прокачать.
Теги:iososxapplemachine learningcocoapodscmakexcodeclang c++open source
Хабы: Разработка под iOS
Всего голосов 18: ↑16 и ↓2+14
Просмотры18K

Похожие публикации

Лучшие публикации за сутки