Да, такой заголовок не очень-то наполнен смыслом, поэтому дам несколько пояснений, для тех кто еще думает «идти под кат или нет?».
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
#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
.
// 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_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
.
# поможет избежать ошибок типа "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),#include12Shark/3,g"
-e "s,#include([ t])([<"])(${components}/),#include12Shark/3,g"
{} +
Создаем 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, самое время обратить на этот проект внимание.
Создание спецификации для пода — отдельная история. Несмотря на то, что я практически расписал каждую мелочь до этого момента, здесь я сошлюсь на готовый результат.
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
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
Итоги
Итого, если вы вдруг придумали сами себе какую-ту задачу, результат которой, мягко говоря, не гарантирован, все равно попытайтесь что-то сделать. Никогда не знаешь какие новые скилы удастся в результате прокачать.
Автор: i4niac