Android NDK компиляция OpenCPN

Для чего Android на яхте?

Когда вы несете вахту ночью и вокруг только звезды и где-то в паре метров от вас слегка подсвеченный компас. В этот момент для полного комфорта управления желательно иметь под рукой надежное навигационное устройство.

70302495018e006b7fe2b03b4acb87c2.jpg

Водостойкое (достигается помещением планшета в водонепроницаемый прозрачный пакет) и с возможностью установки дополнительных программ, которые позволяют скоротать время на вахте. Таких, как Stellarium-android, андроид интерфейс для SunSDR2 радио и т.д.

В предыдущих статьях я написал как сделать стационарное устройство с вложением 300 евро. А сегодня разберём как сделать такое устройство с нулевыми вложениями, при условии, что у вас уже есть любое Android устройство и опыт в кросс-компиляции.

С чего начать?

Начинать лучше всего с краткой инструкции. К сожалению, местами она устарела, а местами очень краткая. Поэтому первым делом предлагаю поставить Android SDK вот таким способом через IDE Eclipse.

Я сознательно не использую Android Студио, так как код который мы будем разбирать был сделан 5 лет назад. Во избежания недоразумений я использую GNU/Linux Debian Wheezy. Так как все библиотеки в этом олдолдстабле дистрибутиве соответствуют Android NDK от 10 релиза (r10e). Полный список всех доступных 64 битных релизов NDK находиться тут.

Будте внимательны, если у вас 32 битный компьютер, то вам не надо заниматься компиляцией тулчейна с нуля (по крайней мере для известного железа, всё уже собрано). 32 битные NDK для вашего целевого компьютера Linux можно скачать тут.

Процесс подготовки окружения

Создаём необходимые директории

mkdir ~/Projects/
mkdir ~/Projects/android-ndk/
sudo mkdir /opt/android_toolchain
sudo chown -R / /opt/android_toolchain

Разархивируем в созданную директорию android-ndk-r10e-linux-x86_64.zip или android-ndk-r10e-linux-x86.bin. И запускаем команду, которая установит кросскомпилятор нужного типа для выполнения:

~/Projects/android-ndk/android-ndk-r10e/build/tools/make-standalone-toolchain.sh \
--toolchain=arm-linux-androideabi-4.8 --platform=android-19 \
--install-dir=/opt/android_toolchain

Установка и использование независимого от IDE тулчейна новых версий невозможна. Поэтому мы используем старые версии.

Кроме того, наше головное устройство навигации имеет тачпанель, которая вместе с SVGA экраном поддерживается только олдолдстабле версией GNU/Linux Debian Wheezy на платформе Olimex OLinuXino A20.

По этим причинам OpenCPN выбрана версии 4.0, так как компилятор как раз соответствует старой версии Debian Linux. И именно в это время была написана поддержка Android для OpenCPN.

Установка Qt5

Qt5 — это известный коммерческий ферймворк, который был «всегда». То есть существовал до 2004 года. До года секретного начала проекта Android в Америке. И сейчас мы возвращаемся к истокам. В нашем приложении будет микс Qt5 и wxWidget. Чисто гипотетически NDK Android позволяет написать свою операционную систему. Например, Tizen. Но мы этого делать не будем. Тем более уже на этом шаге мы попадаем в зависимость от Qt5, что уже исключает радужные перспективы такой новой ОС. Так как появляется зависимость от сторонней группы разработчиков, которая обычно является фатальной. IMHO

Старые архивы находятся тут. Нужно выбрать тот архив, который соответствует вашей системе.

wget https://download.qt.io/new_archive/qt/5.2/5.2.1/qt-opensource-linux-android-x86-5.2.1.run
sudo apt-get install build-essential
sudo apt-get install libfontconfig1
sudo apt-get install mesa-common-dev
sudo apt-get install libglu1-mesa-dev -y
sudo chmod +x ~/Downloads/qt-opensource-linux-x86-5.2.1.run
sudo bash ~/Downloads/qt-opensource-linux-x86-5.2.1.run

Cледуем установкам по умолчанию в инсталляторе Qt. В результате получаем в меню Приложений новое приложение Qt Creator (Opensource). По умолчанию он ставится в папку /opt/Qt5.2.1. Эту папку я использовал для версии без андроида. А нужную версию поставил в папку /opt/Qt.

По ссылке из начала абзаца можно найти информацию как вручную создать запускающий файл Qt-Creator.desktop в папке  .local/share/applications. Часто в Linux библиотеках интегрирование между приложениями осуществляется через вызов этих файлов. Так же наиболее легкие и мобильные оконные менеджеры под Linux используют эти файлы для связывания приложения с расширением и автоматического вызова нужного приложения из wxWidget.

wxWidgets

Это фреймворк, который отрисовывает кнопки и окна, интегрирует принтеры и другие периферийные устройства. Достаточно быстрый, чтоб работать на очень слабых устройствах, подобных Olimex OLinuXino A20.

cd ~/Projects
mkdir wxqt
cd wxqt
sudo apt-get install git -y
git clone https://github.com/bdbcat/wxWidgets.git
cd ~/Projects/wxqt/wxWidgets
git submodule update --init src/zlib
git submodule update --init src/png
git submodule update --init src/jpeg
git submodule update --init src/expat
git submodule update --init 3rdparty/catch

Компиляция с использованием сценария из Qt:

mkdir build_android
cd build_android
export PKG_CONFIG_PATH=/opt/Qt5.2.1/5.2.1/android_armv7/lib/pkgconfig
export CPPFLAGS=-D__ANDROID__
export PATH=/opt/android_toolchain/bin:$PATH
export CC=arm-linux-androideabi-gcc
export CXX=arm-linux-androideabi-g++
export QT5_CUSTOM_DIR=/opt/Qt5.2.1/5.2.1/android_armv7/
../configure --with-qt --build=x86_64-unknown-linux-gnu \
--host=arm-linux-androideabi --enable-compat28 --disable-shared \
--disable-arttango --enable-image --disable-dragimage \
--disable-sockets --with-libtiff=no --without-opengl \
--disable-baseevtloop --disable-xrc --disable-cmdline \
--disable-miniframe --disable-mdi --enable-debug --disable-stc \
--disable-ribbon --disable-propgrid --disable-timepick \
--disable-datepick --disable-xlocale --disable-intl

Перед компиляцией нужно исправить несколько проблем.

Так в файлах

  • include/wx/qt/app.h

  • include/wx/qt/window.h

нужно добавить #include .

В файле include/wx/evtloop.h в 19 строке добавить условие defined (_ _ANDROID_ _) чтоб определялась переменная wxUSE_EVENTLOOP_SOURCE в 1 для нашей директивы при конфигурации связанной с операционной системой _ _ANDROID_ _ .

В файле src/qt/evtloop.cpp в 254 строке добавить в условие || ! wxUSE_EVENTLOOP_SOURCE. Этот блок кода был написан в 2003 году нашим соотечественником, который работал для поддержки wx в Виндоус и вызывает метод из проприетарной dll для Windows. То есть это просто устаревшее наследие и надо ещё будет посмотреть как базовый класс этого приложения будет работать с консолью и общим циклом приложений в Android.

Или даже проще закомментировать кусок кода связанный с Виндоус, потому что строкой выше находится точно такой же кусок кода, но уже через класс реализованный в wx без опоры на сторонние dll.

make

Кросс-компиляция OpenCPN

Достаём исходный код из репозитория и переключаемся на ветку исходников wxqt. Попутно убираем на запас новый плагин, в котором один файл по какой-то причине не закоммичен. Этот плагин нас пока не интересует, поэтому отложим его в tmp каталог.

sudo apt-get install cmake
sudo apt-get install gettext
cd ~/Projects
git clone https://github.com/OpenCPN/OpenCPN
mkdir tmp
cd OpenCPN
mv ./plugins/chartdldr_pi ../tmp
git checkout wxqt

Пприводим в соответствие с нашими каталогами файл ~/Projects/OpenCPN/buildandroid/build_android.cmake

#Toolchain and options definition file for OPenCPN Android build


#  Locations of the cross-compiler tools
# this one is important
SET(CMAKE_SYSTEM_NAME Generic)
#this one not so much
SET(CMAKE_SYSTEM_VERSION 1)

# specify the cross compiler
SET(CMAKE_C_COMPILER   /opt/android_toolchain/bin/arm-linux-androideabi-gcc)
SET(CMAKE_CXX_COMPILER   /opt/android_toolchain/bin/arm-linux-androideabi-g++)

# Location of the generic wxWidgets base
SET(wxQt_Base /home/<ваш_юзернейм>/Projects/wxqt/wxWidgets)

#Location of the specific wxWidgets build (for Qt_Androidd)
SET(wxQt_Build build_android)

#Location of the root of the Qt installation
SET(Qt_Base /opt/Qt5.2.1/5.2.1/android_armv7/)

В файле ~/Projects/OpenCPN/src/s57chart.cpp, который отвечает за отрисовку карт в формате CMAP (CM93) в строке 7056 используется вызов сортировки через функцию CMPFUNC, определенную через шаблоны. Компилятор для Андроида спотыкается на этой строчке. Я её просто закомментировал до момента, когда мне потребуется посмотреть на Андроиде отсортированные Маяки.

Аналогично комментируем строчку 1481 в файле routemanagerdialog.cpp.

Так как параметр CMAKE_TOOLCHAIN_FILE на моей машине не всегда корректно интерпретируется кросс компилятором, то мы прямо разместим пару мягких ссылок (ярлыков) на необходимые заголовочные файлы Qt в директории include в wxqt.

ln -s /opt/Qt5.2.1/5.2.1/android_armv7/include/QtGui /home/<ваш_пользователь>/Projecs/wxqt/wxWidgets/include/QtGui
ln -s /opt/Qt5.2.1/5.2.1/android_armv7/include/QtCore /home/<ваш_пользователь>/Projecs/wxqt/wxWidgets/include/QtCore

И компилируем статические библиотеки:

cd ~/Projects/OpenCPN
mkdir build_android
cd build_android
cmake -D_wx_selected_config=androideabi-qt -DCMAKE_TOOLCHAIN_FILE=../buildandroid/build_android.cmake ..
make

Assetbridge

Это почти детективная история в стиле Google. В Реадми файле Андроид проекта OpenCPN указано, что нужна эта библиотека. Написана она одним человеком с именем Стив, но все репозитории удалены. И прямых ссылок Гугл не выдаёт. Но по gist.github.com удалось найти пару человек которые касались этой библиотеки и скрипта, который делает то же самое что написано в этой статье. Хорошо что сохранился форк репозитория создателя этого скрипта, где есть исходники этой библиотеки.

Она состоит из двух частей java и С. Си код просто даёт доступ к временной директории, а Java код позволяет оперативно переносить в заданную директорию необходимые данные. Такие, как значки для карты, иконки и стили для размещения иконок. Все эти перечисленные ресурсы изначально запаковываются в APK файл нашего приложения.

Так же сохранился другой gist-отчёт для одного пользователя из университета в городе Манила. И репозиторий этого проекта. Google возможно намеренно не показывает ссылки связанные с возможной уязвимостью андроид устройств, либо со старыми версиями библиотек.

Так же есть исходники этой библиотеки в ветке android основного репозитария. Эта ветка менялась 2 года назад и является более свежей, поэтому попробуем эту ветку позже.

Нам нужно скопировать этот каталог в аналогичное место нашей ветки. Выполнить команду ndk-build и расположить построенную библиотеку в каталоге ~/Projects/OpenCPN/buildandroid/. Выбор каталога связан с дефолтными настройками в файле ~/Projects/OpenCPN/buildandroid/opencpn.pro

mv ~/Projects/mitchd/OpenCPN/buildandroid/assetbridge ~/Projects/OpenCPN/buildandroid
cd ~/Projects/OpenCPN/buildandroid/assetbridge/
export PATH=/opt/android_toolchain/bin:$PATH
export CC=arm-linux-androideabi-gcc
export CXX=arm-linux-androideabi-g++
export NDK_PROJECT_PATH=/home/<ваш_пользователь>/Projects/OpenCPN/buildandroid/assetbridge/
/home/<ваш_пользователь>/Projects/android-ndk/android-ndk-r10e/ndk-build
cp ./libs/armeabi/libassetbridge.so ..

Эта библиотека даёт возможность C++ коду забирать файлы упакованные в APK (иконки, символы, стили для иконок) из директории на Android устройстве /data/data/org.opencpn.opencpn/cache/.

Сборка финального APK файла

Прежде всего надо отредактировать файл /home/<ваш_юзернейм>/Projects/OpenCPN/buildandroid/opencpn.pro. Следующие строчки требуют изменения:

        wxQt_Base=/home/<ваш_юзернейм>/Projects/wxqt/wxWidgets
        wxQt_Build=build_android

        OCPN_Base=/home/<ваш_пользователь>/Projects/OpenCPN/
        OCPN_Build=build_android

Вот эту дополнительную строчку оставим на последующие эксперименты. Дело в том что например на старом имидже Debian от Olimex A20 с чипом Allwiner OpenGL не работает. Таковы издержки того что эта прекрасная компания (точнее директор компании Цветан Узанов из Болгарии город Пловдив) непосредственно не нанимала программистов, а только поддерживала свободных разработчиков скидками и эвентами, а также иногда приглашала в ресторан, как своих друзей.
Что так же объясняется тем что нанять хорошего разработчика в Пловдиве, выдерживая конкуренцию по зарплате с Американской компанией по безопасности практически не возможно.

LIBS += $${OCPN_Base}/$${OCPN_Build}/lib/libGLU.a

Готовимся собрать динамическую библиотеку и выполняем следующие команды:

cd ~/Projects/OpenCPN/build_android
export PATH=/opt/android_toolchain/bin:$PATH
export ANDROID_NDK_ROOT=/home/<ваш_пользователь>/Projects/android-ndk/android-ndk-r10/
export ANDROID_SDK_ROOT=/home/<ваш_пользователь>/android-sdk-linux
/opt/Qt5.2.1/5.2.1/android_armv7/bin/qmake -makefile ../buildandroid/opencpn.pro -o Makefile.android -r -spec android-g++ CONFIG+=debug

В результате работы qmake будут сгенерированы два файла в каталоге ~/Projects/OpenCPN/build_android

Для того чтоб следующий шаг был успешен. Нужно до компиляции wxWidget откатиться на предыдущую ветку библиотеки libpng и кое-что подсократить в старой версии OpenCPN. Сейчас точно не указываю какие изменения были сделаны, так как они просто связаны с комментированием того что не компилировалось без разбирательства как подружить части между собой.

cd ~/Projects/wxqt/wxWidget/src/png
git checkout libpng16
Далее скопировать 3 файла необходимых из старой весии. 
В основном они связаны с переименованием функций 
и исправление одного бага свзяанного 
с Neon https://forums.wxwidgets.org/viewtopic.php?t=42087

cd ~/Projects/OpenCPN/build_android
make -f Makefile.android

Мы получим libopencpn.so в текущем каталоге размером около 74 мегабайт.

Переходим к финальным командам, ради которхых всё и затевалось.

make -f Makefile.android install INSTALL_ROOT=./apk_build
/opt/Qt5.2.1/5.2.1/android_armv7/bin/androiddeployqt --input ./android-libopencpn.so-deployment-settings.json  --output ./apk_build --android-platform android-19 --deployment bundled

Получаем файл ~/Projects/OpenCPN/build_android/apk_build/bin/QtApp-debug.apk

Установка apk на устройство

Будем использовать командную строку и adb.

cd ~/Projects/OpenCPN/build_android/apk_build/bin
~/android-sdk-linux/platform-tools/adb kill-server
~/android-sdk-linux/platform-tools/adb start-server
~/android-sdk-linux/platform-tools/adb device
~/android-sdk-linux/platform-tools/adb -s  install ./QtApp-debug.apk  

В дальнейшем надо попытаться подобрать лучшие (максимально новые) версии wxWidget и OpenCPN. Но максимально совместимые с нашим кодом на основе OpenCPN 4.0

Несколько наиболее важных частей — Командная строка, libpng, wxSound были в стадии обновления и несколько лет эту часть кода в этих версиях никто не менял. Поэтому возможно придётся сделать промежуточный патчь для OpenCPN нашей версии, но который совместим с максимально новым wxWidget и Qt именно под Android.

Так как проект OpenCPN это по сути 4 различные версии кода под основныне операционные системы. Они отделены друг от друга директивами #define и #if #else #endif. Одна из которых Android.

© Habrahabr.ru