Собираем приложение через сборочную систему

Общее описание процесса сборки

Чтобы получить пакет, готовый к запуску на устройстве или эмуляторе, нужно выполнить следующие шаги:

  1. Синхронизировать директорию проекта со сборочной системой, чтобы все последние изменния попали в билд.
  2. Скомпилировать наше приложение и получить RPM-пакет.
  3. Подписать пакет ключом разработчика.
  4. Провалидировать пакет и убедиться, что он может быть установлен и запущен Авророй.
  5. Установить подписанный пакет на устройство или в эмулятор.

Но первым делом необходимо подготовить сборочную директорию, в которой будут находиться промежуточные файлы и артефакты сборки.

Подготовка сборочной директории

Для того, чтобы промежуточные файлы компиляции и артефакты сборки не перемешивались с исходниками проекта, разумно будет собирать это всё в отдельной директории. Aurora SDK по умолчанию создаёт сборочные директории в своей рабочей папке, указанной при установке. Мне такой подход не нравится, я предпочитаю хранить всё внутри директории проекта и просто добавлять сборочные артефакты в .git/info/exclude или даже глобальный .gitignore.

Кроме того, необходимо разделять сборки по архитектурам и профилям debug/release. В качестве примера я буду собирать debug-сборку приложения под архитектуру ARM7 для запуска на устройстве F+ R570e. Так что в корне проекта создам следующий путь:

mkdir -p .build/armv7hl-debug

Такое название выбрано не случайно — токен armv7hl обозначает архитектуру ARM7 в сборочном таргете PSDK и его использование упростит скрипт автоматизации, который я приведу в параграфе Автоматизация сборки.

Собираем пакет с приложением

Теперь всё готово для сборки приложения.

Синхронизация директории проекта

Т.к. в этом цикле статей я описываю процедуру удалённой сборки с помощью билд-системы (ведь QEMU тоже можно считать удалённой машиной), важно обеспечить синхронизацию директорий проекта между сборочной системой и локальным компьютером, на котором будет вестись разработка.

Есть разные способы обеспечения синхронизации:

  1. Можно постоянно коммитить код в репозиторий и забирать его перед каждой сборкой. Но, думаю, не стоит объяснять, почему это не самый лучший вариант.
  2. Можно использовать общую сетевую директорию (например, по протоколу Samba) или даже облачный диск. Но мне не нравится такой вариант из-за его ненадёжности — часто случаются проблемы синхронизации, которые отвлекают от разработки.
  3. Использовать системные утилиты синхронизации, например rsync. Этот вариант мне нравится больше всего, т.к. он надёжный, не требует никаких дополнительных настроек и хорошо автоматизируется.

Если вы настраивали сборочную систему в QEMU по шагам из первого поста этого цикла, то утилита rsync в ней уже установлена. Если вы используете реальную систему на x86 в качестве сборочной и пропустили первый пост, убедитесь, что утилита присутствует в системе. То же самое касается вашей локальной машины, т.к. для работы rsync она должна быть установлена на всех участвующих в синхронизации системах.

Итак, если всё готово, можно попробовать синхронизацию рабочей директории между локальной и сборочной системами:

PROJ=$(basename ${PWD}); rsync -avz --delete ../${PROJ} aurora_builder:Projetcs/

Этой командой мы синхронизируем всю директорию проекта со сборочной системой, она будет скопирована в директорию Projetcs (нужно предварителаьно убедиться, что директория существует). С ключами запуска утилиты rsync можно ознакомиться в документации или почитать любой из сторонних гайдов, в этой статье я останавливаться на них не буду.

Сборка пакета

Теперь, наконец, переходим к сборке пакета. Для удобства повествования я предлагаю подключиться к сборочной системе по ssh и вводить команды напрямую в билд-систему. От целевого использования этот вариант практически ничем не отличается, просто так удобнее разбираться в самих командах.

Процесс компиляции, подписания и установки пакета задействует множество утилит, но к счастью в Platform SDK есть удобный скрипт mb2, который позволяет значительно упростить процедуру. У скрипта есть несколько различных команд, нас сейчас интересует только build. Выполняться она должна не из корня проекта, а из директории, в которой будут создаваться сборочные артефакты.

cd Projects/MyProject/.build/armv7hl-debug

${PSDK_DIR}/sdk-chroot mb2 -t AuroraOS-${PSDK_VER}-base-armv7hl build -d ../../

После успешного завершения выполнения команды в текущей директории должны появиться артефакты сборки. В основном это объектные o-файлы и моки, которые нам пока не интересны. Важно только убедиться, что появилась директория RPMS и в ней находится rpm-пакет нашего приложения.

Но прежде, чем переходить к подписанию пакета, давайте разберёмся с командой mb2.

Во-первых, она находится внутри окружения PSDK, поэтому запускать её нужно из-под sdk-chroot.

Во-вторых, т.к. даже в самом базовом варианте у нас может быть установлено несколько сборочных таргетов, нужно явно указывать какой из них использовать с помощью аргумента -t. Посмотреть список установленных таргетов можно следующей командой:

${PSDK_DIR}/sdk-chroot sdk-assistant list

Аргумент build говорит скрипту, что мы хотим собрать приложение, а опциональный аргумент -d или --enable-debug включает генерацию отладочных символов.

Последним аргументом нужно указать путь до корня проекта. В нашем случае, т.к. сборочная директория находится внутри проекта, просто указываем уровень вложенности.

Подписание пакета ключом разработчика

Теперь, когда у нас есть в руках пакет с приложением, нам нужно подписать его ключом разработчика, чтобы подтвердить свои добрые намерения. Для упрощения будем рассматривать подпись общедоступными ключами, которые мы добавляли в билд-систему при настройке PSDK.

Подписывать будем специальной утилитой rpmsign-external:

${PSDK_DIR}/sdk-chroot rpmsign-external sign \
    --key ${CERTS_DIR}/regular_key.pem \
    --cert ${CERTS_DIR}/regular_cert.pem \
    RPMS/ru.my_org.MyProject-0.1.1-armv7hl.rpm > /dev/null

Тут нужно подставить свой путь до rpm-файла, а в случае с debug-сборкой ещё и не перепутать какой именно пакет подписывать. Если подписываем персональным ключом, то в процессе утилита попросит ввести пасс-фразу. Результатом выполнения команды должна стать строчка вида:

Signed ( 1 / 1 ) "RPMS/ru.my_org.MyProject-0.1.1-armv7hl.rpm"

Если попытаться ещё раз подписать этот же пакет, утилита выведет ошибку без особых уточнений что же пошло не так:

Could not sign "RPMS/ru.my_org.MyProject-0.1.1-armv7hl.rpm"

Поэтому может быть целесообразно добавить в команду подписания флаг --force.

После успешного подписания пакета можно проверить его командами verify или dump:

${PSDK_DIR}/sdk-chroot rpmsign-external verify RPMS/ru.my_org.MyProject-0.1.1-armv7hl.rpm 
${PSDK_DIR}/sdk-chroot rpmsign-external dump RPMS/ru.my_org.MyProject-0.1.1-armv7hl.rpm 

Разница между ними в том, что verify просто проверяет наличие подписи у пакета, а dump выводит подробную информацию о ней.

Валидация пакета

Прежде чем пытаться установить и запустить приложение на устройстве, желательно проверить его валидатором. Это делается простой командой:

${PSDK_DIR}/sdk-chroot rpm-validator -p regular --color auto RPMS/ru.my_org.MyProject-0.1.1-armv7hl.rpm > /dev/null

Результатом в идеальном случае должен быть пустой вывод, но могут быть какие-то предупреждения, т.к. SDK постоянно развивается и то, что было допустимо в прошлых версиях, в новых может быть объявлено как deprecated. В таком случае вывод может выглядеть примерно так:

Processing RPMS/ru.my_org.MyProject-0.1-1.armv7hl.rpm...
224 blocks
(WARNING)	[Aurora.Controls 1.0] Import is unstable or deprecated

В этом нет ничего страшного, пакет можно считать валидным. Но, конечно, с причинами предупреждения лучше разобраться.

Автоматизация сборки

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

Начнём со скрипта сборки и валидации на сборочной системе:

#!/bin/bash

# подключаем переменные окружения
source ${HOME}/.bashrc

PROJ=$1
ARCH=$2
BUILD_DIR="${HOME}/Projects/${PROJ}/.build/${ARCH}-debug"
CERTS_DIR="${HOME}/Tools/Certs"

if [ ! -d ${BUILD_DIR} ]; then
    mkdir -p ${BUILD_DIR}
fi
cd ${BUILD_DIR}

echo "Using PSDK from ${PSDK_DIR}"

# собираем пакет
${PSDK_DIR}/sdk-chroot mb2 -t AuroraOS-${PSDK_VER}-base-${ARCH} build -d "../../" > /dev/null

RPM=$(find . -iname \*.rpm | grep -v "\-debug")

if [ -n ${RPM} ]; then
   # подписываем пакет
   ${PSDK_DIR}/sdk-chroot rpmsign-external sign \
       --key ${CERTS_DIR}/regular_key.pem \
       --cert ${CERTS_DIR}/regular_cert.pem \
       "${RPM}" > /dev/null

   # выполняем валидацию пакета
   ${PSDK_DIR}/sdk-chroot rpm-validator -p regular --color auto "${RPM}" > /dev/null
else
   echo "Build failed."
   exit 1
fi

Этот скрипт принимает два аргумента (название директории проекта и целевую архитектуру) и выполняет три действия:

  1. Компиляция приложения и сборка RPM
  2. Подписывание RPM общедоступным ключом
  3. Валидация RPM-пакета

Сохраним этот скрипт в файл ~/Tools/psdk-build и дадим права на выполнение:

chmod 755 ~/Tools/psdk-build

Теперь можно отключиться от сборочной системы и сделать небольшой скрипт уже на локальной машине:

#!/bin/bash

ARCH=$1
BUILDER=aurora_builder
PROJ_DIR=`basename ${PWD}`
TOOLS_DIR="Developer/Tools"

if [ -z ${ARCH} ]; then
    ARCH="armv7hl"
fi

rsync -arzhe ssh --delete ../${PROJ_DIR} ${BUILDER}:Developer/
ssh ${BUILDER} ${TOOLS_DIR}/psdk-build ${PROJ_DIR} ${ARCH}
if [ $? -eq 0 ]; then
    rsync -arzhe ssh --delete ${BUILDER}:Developer/${PROJ_DIR}/.build ./
fi

Скрипт предполагает вызов из корневой директории проекта и принимает на вход только один аргумент — название архитектуры в том виде, в котором они указаны в таргетах PSDK. Суть скрипта довольно простая:

  1. Синхронизировать текущий проект со сборочной машиной.
  2. Вызвать ранее созданный скрипт psdk-build на удалённой машине.
  3. Синхронизировать директорию .build локальной машины со сборочной системой (но уже в обратную сторону).

Скрипт можно назвать, например, aurora-build, дать права на исполнение и сохранить в такое место, где ОС сможет его найти. Тогда можно будет в корневой директории проекта вызвать его простой командой:

aurora-build armv7hl

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

Заключение

Итак, мы проделали большую работу и ещё на шаг приблизились к своей первой цели: компиляции и запуску приложения на устройстве без использования Aurora SDK. Теперь у нас есть собранный, подписанный и провалидированный rpm-пакет с нашим приложением, можем переходить к его установке на устройство под управлением ОС Аврора.