Авторские статьи

Ваши Qt программы в Ubuntu Store.


Согласитесь что заманчиво звучит представить ваши программы, использующие тулкит Qt, в Ubuntu Store для миллионов людей? Мне часто задают вопрос про упаковку Qt программ в snap пакет и решил показать вам на примере своего Hello World все этапы.

Упаковка готового

Вначале давайте рассмотрим простой вариант, когда программа уже скомпилирована и вам нужно "всего лишь" упаковать её в snap пакет. Поискал среди своих попыток поучиться азам программирования с помощью QtCreator и нашёл графический аналог Hello World в папке qtprogram1/. Дерево внутри было такое

.
├── qt1
│   ├── main.cpp
│   ├── mainwindow.cpp
│   ├── mainwindow.h
│   ├── mainwindow.ui
│   ├── qt1.pro
│   └── qt1.pro.user
└── qt1-build-desktop
    ├── Makefile
    ├── moc_mainwindow.cpp
    ├── qt1
    └── ui_mainwindow.h
2 directories, 10 files

Готовый бинарник доступен по qtprogram1/qt1-build-desktop/qt1. Что нам нужно для создания snap пакета данной программы? По большому счёту лишь установленный snapcraft (sudo apt install snapcraft) и пустая папка под ваш проект. Но, как не раз сообщал вам в предыдущих статьях, настоятельно рекомендую не загаживать свою основную систему и настроить "сборочный цех" в виртуальных средах или в контейнерах. Сборочный цех настоятельно рекомендую натравить на Apt-cacher-ng, который сэкономит вам трафик, время и нервы.

Итак, у нас есть snapcraft и пустая папка, в которую копирую своё поделие в лице qtprogram1/, и создаю файл snapcraft.yaml.

name: qt1
version: "1"
summary: My first Qt app
description: Qt Hello World
confinement: strict
architectures: [amd64]

apps:
 qt1:
  command: desktop-launch $SNAP/opt/myapp/qt1
  plugs: [home, unity7, x11, wayland, desktop, desktop-legacy, gsettings]

parts:
 project-files:
  plugin: dump
  source: qtprogram1/
  organize:
   qt1-build-desktop/qt1 : opt/myapp/qt1
  after: [integration]

 integration:
  plugin: nil
  stage-packages:
   - libstdc++6
   - libc-bin
  after: [desktop-qt5]

Подробнее о полях. Кратенько, имя = qt1, версия = 1, режим ограничения = жёсткий (strict). Бинарник скомпилирован под 64 битные системы и работать будет только в них, поэтому architectures: [amd64]. Теперь буду объяснять, читая файл snapcraft.yaml снизу вверх.

Официальное руководство о синтаксисе The snapcraft syntax.

В разделе кусочков (parts:) добавляю 2 кусочка под придуманными мной именами - project-files и integration. Кусочек integration будет помогать мне с добавлением в будущий пакет snap программы всего того что нужно ей для работы. Для этих целей для кусочка integration указываем плагин nil и через stage-packages добавляю пару имён пакетов из официального репозитория, чьё содержимое (пакетов deb) будет запаковано вместе с программой. Так как формировать stage-packages утомительно, то в помощь нам, юным снапкрафтерам, разработчики представили эталонные кусочки для популярных фреймворков/тулкитов, которые называются облачными (cloud parts). Более подробно о cloud parts и доступных помощниках.

Традиционно, в stage-packages добавляют всё то, что в мире deb называется зависимостями программы и указывается в Depends. Магической фразой after: [desktop-qt5], прошу snapcraft помочь мне с формированием нужного программе и динамически пополнить stage-packages такими именами пакетов, чьё содержимое сделают qt5-программу работоспособной внутри её snap пакета. В дополнение мне будет доступен эталонный shell-скрипт desktop-launch от разработчиков, в котором определены все нужные переменные окружения. Изучить содержимое desktop-launch мы сможем чуть позже, когда запустим первый цикл итерации, нам скачают свежею версию скрипта и разместят по адресу prime/bin/desktop-launch.

Кусочек project-files использует плагин dump для размещения программы там где мне нужно в будущем snap пакете. Плагину dump устанавливаю исходной папкой для его работы каталог source: qtprogram1/. Прошу разместить qt1-build-desktop/qt1 по адресу opt/myapp/qt1. Обратите внимание, что лидирующего слеша у opt нет, так как opt/ будет внутри снап пакета и в системах пользователей фактически будет находиться по абсолютному адресу /snap/qt1/1/opt/. Где размещать программу решать вам! Данный hello world, состоящий из одного бинарника, лучше бы разместить в usr/bin/, так как данный путь desktop-launch пропишет в различных переменных окружениях типа PATH. Но в вашем случае с бинарником могут идти сопровождающие файлы и не очень красиво будет какать в usr/bin/ всем чем попало, хоть и находящийся внутри snap пакета. Стыдно признаться, но я так поступил с LanguageTool, нарушив стандарты иерархии файловой системы (Filesystem Hierarchy Standard - FHS) =(. Для красоты прошу на всех стадиях работы snapcraft (Pull -> Build -> Stage -> Prime -> Snap) действия над кусочком project-files выполнять после кусочка integration (after: [integration]).

Официальное руководство о плагинах Snapcraft plugins reference.
Официальное руководство о переменных окружения, доступных дополнительно внутри snap пакета, Environment variables.

В разделе apps: указываю имя моего бинарника как qt1 и что фактически прошу вызывать мою программу через вызов shell-скрипта-обёртки desktop-launch $SNAP/opt/myapp/qt1. Для простой программы прошу подключить при установке в систему к следующим её интерфейсам - plugs: [home, unity7, x11]. Программа в snap темнице будет иметь только те окошки для связи со внешним миром, которые вы укажите в plugs. Если вам нужен доступ к звуку, сети и так далее, то читайте официальное руководство об интерфейсах Interfaces reference и указывайте требуемое через запятую.

Всё готово для создания пакета snap для вашей программы. Командуйте в вашем каталоге, где лежит snapcraft.yaml, snapcraft и он, проходя этапы Pull -> Build -> Stage -> Prime -> Snap, создаст вам пакет. В моём случае родился qt1_1_amd64.snap

Как посмотреть что вам в итоге замуровали с программой? Обратите внимание на подкаталог prime/, который остался от работы предпоследнего этапа Prime. Просмотрите и проанализируйте скрипт prime/bin/desktop-launch, который будет формировать нужное окружение и запускать вашу программу. Проконтролируйте что ваша программа расположена там где вы просили. В моём случае это prime/opt/myapp/qt1.

Так же можно посмотреть содержимое пакета через команду unsquashfs -l *.snap > report.txt.

Пробуем установить пакет!? sudo snap install --dangerous qt1_1_amd64.snap
Здесь --dangerous, так как пакет никем не подписан, авторство ваше не известно. Если пакет залить в Ubuntu Store и оно пройдёт автоматическое одобрение, то оно будет подписано вашей учётной записью Ubuntu SSO.

Ваша программа стартовала? Моя да!

первая программа Qt в snap пакете

Что если программа не стартует? Универсального решения проблемы нет. Узнайте через ldd (ldd qt1) обо всех используемых библиотеках вашим бинарником и ищите (not found). Все имена библиотек, которые мелькнули в выводе ldd с not found, скормите команде dpkg -S имя и выясните в каких пакетах они прячутся. Данные имена пакетов нужно занести в stage-packages и повторить итерацию упаковки - snapcraft и снова установить пакет snap заново. Этим автоматически занимается snapcraft, но в исключительных случаях он может не справиться.

Если в программе вы используете сторонние файлы (текст, медиа), то "подключать-подгружать" их нужно, НЕ используя абсолютные пути. Другими словами, ДО упаковки программы в snap, пробуйте копировать каталог с программой и всем её скарбом в различные папки своей системы и добейтесь работоспособности.

Ваша программа не должна что-либо записывать в тот каталог, из которого она стартует. Snap пакет вашей программы, не распаковывая содержимое, будет примонтирован в каталог /snap/имя-программы/ с доступом только на чтение и это место не будет доступно для записи. Для проверки этого факта ДО каких-либо итераций упаковки легче и проще разместить программу в /opt/ вашей системы и сделать владельцем папок и файлов root'а.
sudo chown -R root:root /opt/myapp/
sudo find /opt/myapp/ -type d -exec chmod 0775 {}\;
sudo find /opt/myapp/ -type f -exec chmod 0664 {}\;

Мы сымитировали через /opt/myapp/ с владельцем root будущий смонтированный snap пакет и теперь программа может запускаться от вашей учётной записи, но записывать в каталоги и файлы, принадлежащие root она не сможет. Работает программа? Если нет, то прервитесь с упаковкой и сконцентрируйтесь на работоспособности программы в таких условиях.

Для записи чего-либо для программы существуют пути, доступные через переменные SNAP_USER_DATA и SNAP_USER_COMMON.

Сначала компиляция проекта и затем упаковка

Давайте попробуем изменить snapcraft.yaml и заставим snapcraft собрать проект и упаковать его. Беглый осмотр вывел на файл qtprogram1/qt1-build-desktop/Makefile, в котором мелькает qmake. Плагин с таким же именем есть и у snapcraft'а.

Пробуем!?

name: qt1
version: "1"
summary: My first Qt app
description: Qt Hello World
confinement: strict
architectures: [amd64]

apps:
 qt1:
  command: desktop-launch $SNAP/opt/myapp/qt1
  plugs: [home, unity7, x11]

parts:
 project-files:
  plugin: qmake
  source: .
  qt-version: qt5
  project-files: [qtprogram1/qt1/qt1.pro]
  after: [integration]

 integration:
  plugin: nil
  stage-packages:
   - libstdc++6
   - libc-bin
  after: [desktop-qt5]

Кусочек project-files теперь использует плагин qmake и специфичные опции, которые объясняют где взять файла проекта и что нужно выбрать qt5. Но из тёплых стран ко мне прилетела птица Обломинго. Подтвердилось, что во мне умер программист, так и не родившись . Свой HelloWorld писал во времена Qt4, а сейчас прошу собрать в Qt5. Посыпались ошибки, которые привели с помощью гуглежа на сайты, где профи посоветовали в файле qtprogram1/qt1/qt1.pro добавить widgets и привести строку к виду

QT       += core gui widgets

. QtGui теперь в QtWidgets и поэтому в qtprogram1/qt1/main.cpp подключение должно быть таким #include <QtWidgets/QApplication>.

Проект стал собираться с помощью snapcraft и плагина к qmake, но итогового бинарного qt1 в нужном мне месте не наблюдалось. Команда find . -name "qt1" -type f вывела лишь ./parts/project-files/build/qt1

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

Building project-files 
....
make install INSTALL_ROOT=/sp/qt1/parts/project-files/install
make: Nothing to be done for 'install'.

Обратился за помощью в почтовой рассылке к разработчикам snapcraft и те объяснили, что в идеале мне нужно указать сделать требуемое в qt1.pro, но если бы я знал как это сделать . И тут же дали совет, что snapcraft умеет изменять поведение плагинов через Scriptlets и дали готовое решение для меня. Кусочек project-files нужно модифицировать так:

parts:
  project-files:
    plugin: qmake
    source: .
    qt-version: qt5
    project-files: [qtprogram1/qt1/qt1.pro]
    install: |
      install -d $SNAPCRAFT_PART_INSTALL/opt/myapp
      install qt1 $SNAPCRAFT_PART_INSTALL/opt/myapp/qt1
    after: [integration]

Чудеса просто! Snapcraft если умеет такое, то слов просто нет. В вашем случае может использоваться другая схема сборки программы, но основную суть вы должны уловить. Snapcraft через различные свои плагины умеет работать со всеми известными системами сборки и поможет с компиляцией вашего проекта из любых источников - от локальной папки до удалённого git.

На этом всё! Буду вам безмерно благодарен, если поделитесь статьёй со своими друзьями программистами, которые теперь легко и просто могут представить свои программы для миллионов пользователей.

Дата последней правки: 2019-11-16 08:42:57

RSS vasilisc.com   


Разделы

Главная
Новости
Ворох бумаг
Видео Linux
Игры в Linux
Безопасность
Статьи об Astra Linux
Статьи о FreeBSD
Статьи об Ubuntu
Статьи о Snappy
Статьи об Ubuntu Phone
Статьи о Kubuntu
Статьи о Xubuntu
Статьи о Lubuntu
Статьи об Open Source
Карта сайта