# Создание пакетов conan для ОС Аврора

## Ознакомление с процессом создания пакетов conan

Проект Conan предоставляет достаточно обширную документацию для разработчиков
пакетов conan. Перед созданием первого публичного пакета для ОС Аврора
настоятельно рекомендуется ознакомиться с [руководством по созданию
пакетов](https://docs.conan.io/2/tutorial/creating_packages.html).

Процесс подготовки пакетов conan логически схож с процессом создания
RPM-пакетов. Знакомство с последним процессом упростит понимание процесса
создания conan пакетов.

Вкратце процесс по созданию conan пакетов заключается в следующем:

* Получение исходных кодов библиотек из внешних репозиториев.
* Формирование списка зависимостей, которые необходимы для работы библиотеки.
* Настройка параметров сборки библиотеки.
* Настройка системы сборки библиотеки.
* Сборка библиотеки и запуск теста для созданной библиотеки.
* Создание пакета, включающего бинарные файлы библиотеки и заголовочных файлов.

## Особенности сборки под ОС Аврора

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

* Собирать пакеты на одной платформе с разными версиями компиляторов.
* Добавлять или отключать возможности собираемых библиотек.

В настоящий момент сборка пакетов conan под ОС Аврора не может быть выполнена
вместе со сборкой приложения, что оставляет возможность для поставки только
бинарных версий библиотек.

Также для обеспечения поддержки LGPL-лицензий библиотеки собираются только в
формате разделяемых библиотек.

Таким образом при разработке conan пакета для ОС Аврора следует:

* Отключить обработку разных параметров сборки библиотеки.
* Реализовать сборку библиотеки в формате разделяемой.
* Включить все возможности библиотеки, чтобы поддержать все варианты
использования.

## Особенности наименования пакетов ОС Аврора

Для того, чтобы отделить пакеты, доступные в центральном репозитории conan,
от пакетов для ОС Аврора предлагается загружать все пакеты от имени пользователя
`aurora`. Данное поле рекомендуется сразу установить в рамках мета-информации
[внутри атрибута user](https://docs.conan.io/2/reference/conanfile/attributes.html#user).

Таким образом в рамках conanfile.txt проектов, использующих сборки conan пакетов
для ОС Аврора, будет явно указано назначение библиотеки:

```
[requires]
ffmpeg/7.0@aurora
```

## Использование рецептов conan center

При создании пакета помимо обращения к обширной документации можно также
воспользоваться [репозиторием с
рецептами](https://github.com/conan-io/conan-center-index) для conan center.
Данные рецепты ориентированы на реализацию гибкой сборки в рамках сборочного
окружения, поэтому большая часть их кода ориентирована на обработку большого
количества возможных аргументов и условий сборки. При создании conan пакетов под
ОС Аврора данные рецепты надо использовать в первую очередь для выяснения
деталей интеграции между сборочной системой библиотеки и средствами сборки
conan.

## Пример сборки пакета библиотеки ffmpeg

Рассмотрим ключевые параметры сборки библиотеки ffmpeg.

### Шаг № 1. Изучение системы сборки ffmpeg

Проект ffmpeg использует систему сборки GNU Autotools. Данная система сборки
[поддерживается conan](https://docs.conan.io/2/reference/tools/gnu.html).

Из вывода скрипта `configure --help` библиотеки ffmpeg можно выяснить, что при
аргументах по умолчанию собирается библиотека согласно лицензии LGPL 2.1+,
а добавление различных кодеков, например x264, переводит библиотеку в лицензию
GPL. При пакетировании для ОС Аврора не добавляем лишних зависимостей.

### Шаг № 2. Создание проекта библиотеки conan

В рамках описания проекта библиотеки conan создаётся как описание самой
библиотеки, так и тестовое приложение, проверяющее его работу. Для удобства
реализации последнего рекомендуется при генерации проекта в качестве системы
сборки указывать CMake. Таким образом можно будет сэкономить время на
формировании конфигурационного файла для тестового приложения.

### Шаг № 3. Заполнение мета-данных

В рамках конфигурационного файла указываем общую мета-информацию по библиотеке:

* Название библиотеки — ffmpeg.
* Наименование пользователя — aurora.
* Тип пакета — библиотека, library.
* Лицензия — LGPL 2.1+.
* Сайт разработчика — http://ffmpeg.org/
* Описание библиотеки.

```
name = "ffmpeg"
user = "aurora"
package_type = "library"
license = "LGPL-2.1-or-later"
url = "http://ffmpeg.org/"
description = "A toolbox to manipulate, convert and stream multimedia content"
```

### Шаг № 4. Настройка параметров сборки

Необходимо указать параметры, которые будут учитываться при формировании
бинарного пакета. Также необходимо указать список доступных динамических
параметров. Согласно предыдущим соображениям зафиксируем: операционную систему,
компилятор, тип сборки, архитектуру. Эти параметры определены в профиле. В
качестве опций определяем только одну опцию — разделяемая библиотека.

```
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True]}
```

### Шаг № 5. Указание исходных кодов

Один рецепт conan может использоваться для сборки разных версий библиотеки. Для
указания разных источников исходных кодов библиотеки можно воспользоваться
файлом `conandata.yml`, в котором для каждой версии библиотеки предоставить
набор из исходных кодов с указанием SHA256-контрольной суммы для контроля
целостности архива:

```
"7.0":
  url: "https://ffmpeg.org//releases/ffmpeg-7.0.tar.xz"
  sha256: "4426a94dd2c814945456600c8adfc402bee65ec14a70e8c531ec9a2cd651da7b"
```

Внутри рецепта conan для определения исходных кодов используем метод `get`:

```
def source(self):
    get(self, **self.conan_data["sources"][self.version], strip_root=True)
```

Аргумент `strip_root` указывает, что в архиве с исходными кодами все данные
лежат в подкаталоге.

В рамках вызова метода `layout` указываем, что все стандартные настройки для
поиска исходных кодов и бинарных файлов подходят для сборки.

```
def layout(self):
    basic_layout(self)
```

### Шаг № 6. Настройка аргументов сборки

Для настройки сборки с помощью GNU Autotools используется класс
[AutotoolsToolchain](https://docs.conan.io/2/reference/tools/gnu/autotoolstoolchain.html).
С его помощью формируются аргументы, которые будут переданы скрипту `configure`.

В рамках пакетирования для ОС Аврора были выставлены следующие параметры:

* `--disable-static` — отключить сборку статически линкуемых библиотек.
* `--disable-doc` — отключить сборку документации по библиотеке.
* `--disable-x86asm` — отключить ассемблерные вставки для x86.
* `--disable-programs` — отключить создание приложений, компилировать только
библиотеки.
* `--disable-avdevice` — отключить сборку библиотеки `avdevice`, т.к. она
зависит от запрещённых к использованию библиотек.
* `--enable-shared` — выполнять сборку разделяемых библиотек.
* `--arch` — указать целевую архитектуру сборки. Адаптация архитектуры conan к
названиям архитектуры внутри configure происходит в методе `_target_arch()`.

Также скрипт `configure`, поставляемый внутри ffmpeg, не поддерживает ряд
параметров, которые класс AutotoolsToolchain подставляет по умолчанию. Для
следующих аргументов устанавливается значение None, чтобы они не передавались
`configure`: `--sbindir`, `--includedir`, `--oldincludedir`, `--datarootdir`,
`--build`, `--host`, `--target`.

### Шаг № 7. Сборка библиотеки

С учётом выполненной настройки на предыдущем шаге достаточно воспользоваться
стандартными метода по сборке класса
[Autotools](https://docs.conan.io/2/reference/tools/gnu/autotools.html):
configure и make.

### Шаг № 8. Пакетирование

Внутрь conan пакета помещаем файл с описанием лицензии, и выполняем установку
файлов стандартным способом. Из целевого каталога удаляем файлы pkgconfig и
дополнительные файлы из каталога `share`.

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

### Шаг № 9. Доработка тестового приложения

В тестовом файле необходимо обеспечить использование функций, предоставляемых
библиотекой. В рамках примера добавляем подключение заголовочного файла
`libavcodec/version.h`.

### Шаг № 10. Выполнение сборки и публикация

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

Пример для одного таргета:
1. Зайти в Аврора build-engine.
2. Авторизоваться в conan remote по [инструкии отсюда](https://os-git.omprussia.ru/external/tools/conan-aurora-index/-/blob/add-recept-for-mfw-map/docs/build-package-tool.md?ref_type=heads#%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F).
3. Перейти в директорию с репозиторием https://os-git.omprussia.ru/external/tools/conan-aurora-index
внутри build-engine.
4. Выполнить команду
    ```
    ./tools/build-package --publish --target armv7hl  -- recepies/ffmpeg 7.0.1
    ```

## Особенности сборки пакета с зависимостями

Иногда пакетируемые пакеты зависят от уже запакованных в conan библиотек.
Для подключения таких библиотек в сборку требуется в файле рецепта `conanfile.py`
реализовать метод `requirements`, в котором следует перечислить необходимые библиотеки:
```
def requirements(self):
    self.requires("protobuf/3.18.3@aurora")
    self.requires("boost/1.85.0@aurora", transitive_headers=True)
```

Если необходимой библиотеки нет среди уже запакованных в conan, тогда
следует поискать её в [списке разрешённых](https://developer.auroraos.ru/doc/5.1.0/software_development/reference/public_api)
зависимостей ОМП.

Если она есть, то нужно создать текстовый файл `buildrequirements.txt` рядом с
файлом рецепта `conanfile.py` и на каждой строке указать название библиотеки.

Пример файла `buildrequirements.txt`:
```
libcurl-devel
zlib-devel
git
```
При сборке пакета через инструмент `build-package` перечисленные зависимости автоматически
установятся перед сборкой пакета.

Если зависимости нет ни в уже запакованных в conan библиотек, ни в 
[списке разрешённых](https://developer.auroraos.ru/doc/5.1.0/software_development/reference/public_api),
то для неё нужно выполнить пакетирование в отдельный conan-пакет и
подключить к собираемой изначально библиотеке.

## Особенности сборки пакета c QML-модулем

Сборка пакетов с QML-модулем практически не отличается от сборки пакета с обычной
библиотекой. Главным отличием является организация структуры каталогов в пакете
так, чтобы она удовлетворяла требованиям [QML-модуля](https://doc.qt.io/qt-5/qtqml-modules-qmldir.html)
и была удобна для поставки в целевое приложение.

В качестве примера сборки пакета c QML-модулем можно обратить внимание на рецепт плагина
[mfw-offline-routing](../recepies/mfw-offline-routing).

### Cборка пакета c QML-модулем

Чтобы создать пакет с QML-модулем требование организовать
структуру каталогов пакета следующим образом:

```
package
└───qmlplugins
│   └───<Root module directory>
│       ├──qmldir
│       ├──lib<Module name>.so
│       │   ...
```

На схеме:
* `qmlplugins` - каталог, который содержит QML-модули и который будет скопирован в
целевое приложение. Потенциально может содержать несколько QML-модулей.
* `<Root module directory>` - корневой каталог QML-модуля. Его название должно
соответствовать полю `module` внутри файла `qmldir`.
* `qmldir` - файл, определяющий модуль.
* `lib<Module name>.so` - бинарный файл модуля. Фрагмент названия `<Module name>`
должен соответствовать полю `plugin` внутри файла `qmldir`.

### Тестовое приложение с графическим интерфейсом

Еще одной особенностью сборки пакета c QML-модулем является более сложное
тестовое приложение.
В тестовое приложение потребуется:
1. Подключить QML-модуль
```cpp
QQmlEngine engine;
engine.addImportPath("application/lib/qmlplugins");
```

2. Загрузить QML, который будет создавать объекты из плагина
```cpp
QQmlContext *context = new QQmlContext(engine.rootContext());
QQmlComponent component(&engine, QString("application/qml/MyComponent.qml"));
```

3. Проверить, что при загрузке QML не возникло ошибок
```cpp
QObject *window = component.create(context);
return component.isReady() && window == nullptr;
```

4. Настроить запуск тестового приложения в `conanfile.py` таким образом, чтобы оно запускало
графический интерфейс в фоновом режиме.
```python
self.run(cmd + " -platform offscreen", env="conanrun")
```

5. Добавить зависимость `qt5-plugin-platform-offscreen` в `buildrequirements.txt`.
