Кроссплатформенное приложение на Qt: Распространение

  • Tutorial
Сборка дистрибутива — завершающий этап подготовки программы к выходу в свет. К сожалению, ни о какой стандартизации в этой области речи не идет, более того, на некоторых ОС (не будем показывать пальцем), так вообще целый зоопарк форматов.

Итак, в идеале нам нужны дистрибутивы:
  • Windows
  • OS X (.dmg)
  • OS X (.pkg — App Store)
  • Linux (.deb)
  • Linux (.rpm)
  • Linux (.tar.gz — universal)

Qt Installer Framework


Справедливости ради стоит отметить, что Qt имеет свой, разумеется кроссплатформенный, фреймворк для создания установщиков. Он позволяет создавать как обычные, так и online инсталлеры, способные докачивать дополнительные компоненты из репозитория. Также есть возможность вставлять свои виджеты, добавлять новые действия (страницы). Вы наверняка устанавливали Qt Creator, а значит видели эту систему в работе. Однако при создании установщика приходится писать xml конфиг, работать с ключами в командной строке, в общем настройка имеет довольно высокий порог вхождения, особенно по сравнению с другими решениями. При этом я так и не нашел в документации упоминания о том, как пропустить страницу выбора компонентов. В общем получился весьма специфичный продукт, минус которого еще и в том, что такой способ установки считается родным только на Windows платформе.

Windows


Соответственно и вариантов создания установщиков для этой ОС масса, но я остановился на довольно удобном open source продукте — Inno setup. Создание инсталлера в нем не сложнее, чем собственно установка «далее-далее-готово».


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

Linux


Исторически сложилось, что софт под Linux распространяется через репозитории, причем дистрибутив каждой программы представляет собой пакет специального вида, содержащий собственно файлы программы, информацию о зависимостях, и некоторые другие метаданные. Несмотря на все попытки стандартизации, кроме мейнстримовых .deb и .rpm существует множество других форматов, специфичных для отдельных дистрибутивов Linux. Canonical экспериментирует с форматом .click, напоминающим бандлы приложений в OS X, но на сегодня рассматривать этот вариант в числе основных пока преждевременно. Раз уж Ubuntu де-факто стандарт Линукса на десктопе, я буду описывать создание пакетов именно на этой платформе. Также следует отметить, что чаще всего дистрибутив стороннего ПО несет с собой все необходимое для функционирования, потому продвинутые возможности пакетной системы остаются незадействованными.

.tar.gz

Что общего между дистрибутивами? Конечно командная оболочка! Значит будем делать скриптовый установщик, который запустится на любой системе, вне зависимости от предполагаемого формата пакетов. Для того, чтобы система «увидела», что установлена новая программа, каждый файл должен занять положенное место. Минимальный набор состоит из: иконки, исполняемого файла, и специального конфига с расширением .desktop

Иконка: Согласно спецификации, Linux не требует преобразовывать исходники иконки в какие-либо сборки, нужно просто скопировать (при установке) файлы в поддиректории вида:
/usr/share/icons/hicolor/RxR/apps, где R — разрешение иконки
или
/usr/share/icons/hicolor/scalable/apps — если вы хотите установить векторное изображение в формате .svg
Однако это еще не все, каждая тема иконок имеет свой кэш, и если его не обновить — увидеть новую иконку не получится. Кэш автоматически обновляется при штатной установке пакетов, либо можно сделать это вручную, выполнив команду:
gtk-update-icon-cache /usr/share/icons/hicolor
Исполняемый файл
/usr/bin
Конфигурационный файл .desktop
/usr/share/applications
содержит всю информацию о программе — название и описание приложения, его категория, название иконки и т.д.

Для примера:
[Desktop Entry]
Name=iStodo
Comment=iStodo is an organizer for students with scheduling and planning features.
GenericName=Organizer for students
Exec=istodo
Icon=istodo
Categories=Office;Qt;
Terminal=false
Type=Application
Version=1.2
Name[ru]=Органайзер iStodo
Comment[ru]=Составление расписания и планирование учебы
GenericName[ru]=Органайзер для студентов

Узнать больше можно прочитав стандарт.

Правилом хорошего тона при установке сторонних программ с нестандартным способом установки будет хранение всех своих файлов в подкаталоге /opt/myapp, куда в том числе следует положить и деинсталлер.

Итак, в итоге у меня получился следующий скрипт, который полагает, что рядом с ним лежит бинарник, файл .desktop, скрипт на удаление, папка с иконками(в ней подкаталоги по размерам):
install.sh
#!/bin/bash

iconsPath="/usr/share/icons/hicolor"
desktopPath="/usr/share/applications" 
installPath="/opt/myapp"
linkPath="/usr/bin/myapp"

#Check user
if [[ $EUID -ne 0 ]]; then
    echo "This script must be run as root"
    exit 1
fi

#Remove old version
if [ -d $installPath ]; then
    $installPath'/uninstall.sh' > /dev/null   
fi
    
echo "myapp install..."
mkdir -v $installPath
cp -v myapp $installPath
cp -v uninstall.sh $installPath
cp -v myapp.desktop $desktopPath
ln -sv $installPath'/myapp' $linkPath
chmod a+x $linkPath
echo "---"
cp -v icons/32x32/myapp.png $iconsPath'/32x32/apps'
cp -v icons/48x48/myapp.png $iconsPath'/48x48/apps'
cp -v icons/64x64/myapp.png $iconsPath'/64x64/apps'
cp -v icons/128x128/myapp.png $iconsPath'/128x128/apps'
cp -v icons/256x256/myapp.png $iconsPath'/256x256/apps'
gtk-update-icon-cache /usr/share/icons/hicolor
update-desktop-database

echo "...finished!"

Логично, что потребуется и скрипт для удаления программы:
uninstall.sh
#!/bin/bash

iconsPath="/usr/share/icons/hicolor"
desktopPath="/usr/share/applications" 
installPath="/opt/myapp"
linkPath="/usr/bin/myapp"
echo "myapp uninstall"

#Check user
if [[ $EUID -ne 0 ]]; then
    echo "This script must be run as root"
    exit 1
fi

rm -v $iconsPath'/32x32/apps/myapp.png'
rm -v $iconsPath'/48x48/apps/myapp.png'
rm -v $iconsPath'/64x64/apps/myapp.png'
rm -v $iconsPath'/128x128/apps/myapp.png'
rm -v $iconsPath'/256x256/apps/myapp.png'
rm -v $desktopPath'/myapp.desktop'

#Remove app folder
echo "---"
rm -rfv $installPath
rm -v  $linkPath

echo "Uninstall finished!"

.deb

Несмотря на пугающе-монструозные спецификации формата, для создания простейшего пакета требуется всего один конфигурационный файл — control. Но для начала нам нужно провести некоторые приготовления:
a) Создать папку проекта, с указанием версии программы и пакета, например myapp-1.1-1
b) В нее добавить папку DEBIAN (регистр важен!), в которой будут находиться все служебные файлы, в частности control
Пример для control
Package: istodo
Version: 1.2-1
Section: misc
Architecture: amd64
Installed-Size: 16500
Maintainer: Yakov Eremin <support@istodo.ru>
Description: Organizer for students
 iStodo is an organizer for students with scheduling and planning features.
Тут все очевидно, нужно только заметить, что полное описание начинается с пробела
c) Создать структуру каталогов, повторяющую положение файлов установленной программы в файловой системе, нечто вроде

myapp-1.1-1
├── DEBIAN
│   └── control
└── usr
    ├── bin
    │   └── myapp
    └── share
        ├── applications
        │   └── myapp.desktop
        └── icons
            └── hicolor
                ├── 128x128
                │   └── apps
                │       └── myapp.png
                ├── 256x256
                │   └── apps
                │       └── myapp.png
                ├── 32x32
                │   └── apps
                │       └── myapp.png
                ├── 48x48
                │   └── apps
                │       └── myapp.png
                └── 64x64
                    └── apps
                        └── myapp.png

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

Ну и собираем пакет командой:
fakeroot dpkg-deb --build myapp-1.1-1/

Если вам требуются дополнительные возможности, тут можно почитать подробный обзор.

.rpm

Как говорится, есть два способа — простой и правильный.
Простой: если .deb пакет не содержит зависимостей и скриптов, то можно просто сконвертировать его, в результате наверняка получится корректно работающий пакет формата .rpm. Для этого предназначена утилита alien, которую можно установить из репозитория.
fakeroot alien -kr myapp-1.0-1.deb

Минусом такого подхода является неработоспособность пакетов на Fedora 18 и более поздних. Насколько я понимаю, проблема вызвана разницей в правах доступа на системные каталоги, из-за чего происходит конфликт. Остается два варианта: либо исправить пакеты руками так (требуется установленная Fedora), либо собирать .rpm пакеты как положено.
Правильный: К сожалению, тут уже конфигом на три с половиной строки не отделаться, потому «Сборка rpm для чайников» — тема отдельной статьи. При этом полное руководство доступно на русском языке.

Подробнее о сборке пакетов можно почитать тут.

OS X


Как было указано выше, есть два основных метода распространения — образ диска с программой, или загрузка из каталога. Оба метода уже неплохо разобраны, стоит отметить лишь несколько принципиальных моментов.


Для того, чтобы программа могла запускаться не только на системах с установленным Qt SDK, нужно использовать специализированную утилиту — macdeployqt, которая скопирует все необходимые плагины и фреймворки в бандл приложения. При добавлении ключа "-d" будет сгенерирован .dmg образ, который можно использовать как базовый для кастомизации.

  • Открываем его через дисковую утилиту, конвертируем в read/write
  • Открываем получившийся образ в Finder
  • В меню «Вид» скрываем лишние элементы (строку статуса и т.д.)
  • Добавляем ссылку на /Applicatons
  • Создаем скрытую папку, копируем в нее фоновое изображение
  • В «параметрах вида» (ПКМ) подстраиваем размер иконок, сетку, фон
  • Конвертируем образ обратно

Оглавление цикла статей
iStodo
39,00
Компания
Поделиться публикацией

Комментарии 39

    +3
    Собственно раскидать файлы по целевой системе не проблема — куча мануалов есть. Но при чём тут Qt?
    Есть другая, более интересная проблема: приложение на Qt требует библиотек, часто той версии, на которой собрана.
    Пусть будут целевые системы Ubuntu 10.04, WIndows 7, Windows XP, пусть на Ubuntu Qt 4.6, на Windows вообще ничего, а приложение собирается под Qt 4.8 — как быть?
      0
      Qt при том, что это единственный кроссплатформенный фреймворк, который нативно выглядит и быстро развивается. Вариантов не много, либо тащить библиотеки с собой, либо линковать статически.
        +1
        О достоинствах Qt я не спорю.

        Ладно, если тащить библиотеки с собой, как заставить приложение использовать «принесённую» библиотеку, а не установленную в системе?
          0
          Я одно время написал скрипт, который идёт по зашитым путям до shared либ и меняет их с абсолютных на относительные, например делает ссылку на lib/QtCore, а затем ещё и копирует в эту папку сам бинарь QtCore.

          Автор говорит про macdeployqt, я думаю, эта утилита делает то же самое при создании бандла.
            0
            Линковать статически, если есть такая возможность. Тогда библиотека войдет в состав бинарника.
              0
              Думал об этом. Попробовать надо.
              Просто библиотек много — сеть, БД, генераторы отчётов всякие, по мелочи там… короче вручную переносить набирается библиотек что-то около 100мб.
                0
                Ну, это ваш путь – либо тащить с собой, либо брать те, что есть в системе. При распространении программы как deb и rpm пакеты, есть возможность указать зависимость от библиотек определенных версий. Есть команды, которые умеют устанавливать пакет и автоматически резолвить зависимости, устанавливая нужные библиотеки нужных версий (для deb-пакетов, например, gdebi).
                  0
                  Есть команды, которые умеют устанавливать пакет и автоматически резолвить зависимости

                  Угу, и это неплохо так ломает пол-системы, точнее весь Qt-софт, работающий на старых версиях :) Сталкивался.
                  Сейчас выхожу из ситуации путём сборки на разных версиях Qt, и распространяю бинари по пользователям.
                  На Windows-машины — ручками, кучу библиотек под XP, + другая куча под 7, + бинари так же ручками какой-кому…
                0
                насколько я помню, если приложение использует вебкит, то не получиться статически собрать
                +1
                Если мы сами задаём пути для установки приложения и библиотек, задачу можно тривиально решить аж двумя путями:
                • Java-way. Вместо запуска исполняемого файла прописываем ярлык на написанный и установленный нами скрипт, прописывающий LD_LIBRARY_PATH перед запуском.
                • Unix-way. Специально для решения этой проблемы есть такая волшебная штука, как RPath. Хорошая, годная поддержка сборки с RPath из коробки есть, например, в CMake (искать по слову «rpath»).
                  0
                  Хм… Первый путь неплох, спасибо.
                    0
                    К слову, тот же CMake (через инструментарий CPack) точно умеет делать при сборке продукта DEB, RPM и инсталляторы под Windows на основе NSIS. Про Mac OS X не скажу, надеюсь, прокомментирует кто-нибудь знакомый со сборкой CMake под OS X на практике.

                    DEB и RPM мы долгое время делали именно штатными средствами CMake, так что (при отсутствии высоких требований к дополнительным функциям пакетного менеджера) вполне могу советовать ознакомиться.
                      0
                      Просто я с CMake не знаком, надо читать мануалы. А хочется, как всегда, тут и сразу :)
                    0
                    К слову, Java умеет и второй вариант — CLASSPATH можно прописать в манифесте jar-файла.
                    0
                    как заставить приложение использовать «принесённую» библиотеку, а не установленную в системе
                    Тупо расположить в папке с исполняемым файлом? На счет Линукса не уверен, но в винде в первую очередь там библиотеки будут искаться.
                      +1
                      Как раз в Линуксе такой вариант не работает вообще — там по-умолчанию библиотеки в папке с исполняемым файлом не ищутся (типа, не секурно), только в системных путях.
                        +1
                        В линуксе бесполезно. На винде так и делаю.
                  0
                  Год назад понадобилось написать что–то кросс–платформенное на Qt5, и чтобы были и DEB и RPM сборки, но это оказалось (мне, новичку разработки под Linux) не совсем тривиальной задачей, в отличие от проектов на Qt4. Когда разобрался, персонал экспериенс запостил на qt-project'овскую вики. На английском, но может кому пригодится…
                    0
                    Автор как-то незаслуженно пропустил готовые решения, позволяющие делать кроссплатформенные дистрибутивы. В прошлом проекте использовали InstallBuilder, например. Хотя справедливости ради надо отметить, что с ростом сложности проекта появляется все больше отдельного кода установки под разные платформы.
                      0
                      А он бесплатный? На каких условиях?
                        0
                        Платный. Есть триал на месяц кажется.
                        0
                        В разделе меню сайта Features о Qt-разработчиках говорится отдельно:
                        BitRock has partnered with Qt Software® (formerly Trolltech®) to bring you InstallBuilder for Qt®, the first crossplatform packaging tool specifically for Qt developers. InstallBuilder for Qt includes robust functionality that makes it easy to build complex installers without writing a single line of Java code. With InstallBuilder for Qt you can:
                        • Generate installers for all supported platforms from one project file, on one machine.
                        • Build installers using either the GUI development environment or by directly editing a human-friendly XML file that can easily be integrated into your automated build process and version control system.
                        • Integrate with the underlying desktop environment and package management systems.
                        • Create start menu shortcuts on Windows systems, KDE/Gnome desktop shortcuts and register with the RPM system on Linux.

                        А цена Enterprise версии составляет $9 995 (странно, почему не 9 999?).
                          0
                          Мы брали за 2000 под три ОС и одного разработчика. 2000 — это 2-3 недели разработчика. Однозначно InstallBuilder нам сэкномил денег намного больше.
                        0
                        Не хватает FreeBSD port/package
                          0
                          Спасибо, интересная статья!
                          Мы в mega.co.nz используем Open Build Service для создания пакетов под основные Linux дистрибутивы. Отличная вещь, на Хабре было пару статей о том как установить и настроить.
                            0
                            Спасибо Вам за статью. Скажите, а как Вы планируете решать вопрос обновления ПО? Рассматривая эту проблему для Windows лично мне больше всего понравилось, как это реализовано у платного продукта Advanced Installer. В Mac OS X видимо на себя эту задачу берет AppStore, а в Linux?
                            Было бы здорово, если осветить этот вопрос в кроссплатформенном контексте. Конечно интересует вариант, когда приложение переодически самостоятельно проверяет наличие обновлений и выдает сообщение о новой версии с возможностью установить установить его «в один клик».
                            P.S. а если еще попробовать все это реализовать в системе интеграции (наподобии jenkins etc).
                              0
                              В Линуксе обновление штатно производится средствами управления пакетами в репозиториях дистрибутива (например, в убунте — apt-get). Собственно, больше всего проблем в Windows, потому что там нет стандартизированных средств управления приложениями (магазинов, репозиториев софта), и каждый глумится, как может.
                                0
                                Но ведь apt-get требует root прав? И сначала нужно будет выполнить apt-get update, потом apt-get upgrade (и все это из консоли)?
                                Получается, что в Linux для обновления только unix-way (если можно так сказать)?
                                  +1
                                  Менеджер обновлений сам периодически проверяет обновления для всех репозиториев, и все программы обновляются скопом. Для стороннего ПО минус в том, что кроме пакета придется поднимать еще и репозитории.
                                    0
                                    Спасибо *ушел читать «Ubuntu for dummies»*
                                      0
                                      Поднять репозиторий — это не минус, а скорее плюс.
                                        0
                                        Вопрос в том, что кросс-дистрибутивность пакетов, как я понимаю, гораздо выше, чем у репозиториев.
                                        0
                                        Хочу дополнить — как правило, при этом система запрашивает пароль пользователя, а не рута. Так что и рутовый пароль постоянно вводить тоже не надо.
                                        А по поводу репозиториев — если «стороннее ПО» opensource, можно договориться с разработчиками дистрибутива, и они добавят ПО в свой репозиторий, а так же проследят, чтобы не было конфликтов с другими пакетами, и были все нужные библиотеки.
                                          0
                                          apt-get требует root прав. То, что для их получения sudo по‐умолчанию спрашивает пароль пользователя — это фишка sudo. Про пароль root в этой ветке до вас не говорил никто, только про права.

                                          Кстати, с разработчиками дистрибутива можно договариваться и для freeware, и даже для платных продуктов. Во всяком случае, я в основном дереве portage (Gentoo) видел все три варианта: тут есть Opera (старая), chrome, nero, пачка игрушек. Но, боюсь, что договориться будет сложнее (разве что вы подпишетесь на самостоятельную поддержку пакета) или невозможно по идеологическим причинам (для дистрибутивов вроде gNewSense).
                                    0
                                    Сейчас наш органайзер запрашивает информацию об обновлениях с сервера, и, при необходимости, выдает ссылку на страницу со списком изменений и кнопкой скачать. Автообновление для Windows делается тривиально, но придется повелосипедить. Если на OS X ставить стороннюю программу не через App Store, то в общем тоже не сложно, делай внутри своего бандла что хочешь. Под линукс штатное обновление будет работать только если подключен репозиторий, а это дополнительные заморочки, по крайней мере на первом этапе. Чтобы обойти нехватку прав можно либо написать демона, который будет в автозапуске от рута, и передавать ему команды, скажем через D-Bus. Еще один вариант — хранить бинарник в домашнем каталоге, а в /usr/bin просто линк на него.
                                    0
                                    — комментарий перенесен --
                                      0
                                      Без обид конечно, но я думаю, что под линукс надо распространять сурцы, если это возможно. А под конкретный дистр уже сами пользователи соберут, как им надо.

                                      PS мейнтейню istodo в archlinux
                                      PPS К слову, для меня, как для одного из мейнтейнеров арча, наличие исходников приложения является довольно значимым фактором для переноса пакета в офф.репы.
                                        0
                                        Распространять можно не только в интернете, а например внутри организации. ПО может быть только для внутреннего использования, а пользователь там — никто, это задачи администраторов.
                                        0
                                        Советую еще обратить внимание на Ruby gem fpm: github.com/jordansissel/fpm
                                        Умеет собирать, в том числе, deb, rpm, tar и pkg и конвертировать одно в другое. Сборщик deb там реализован свой, а для rpm такой реализации нет, поэтому всё равно нужно будет устанавливать rpmbuild.

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

                                        Самое читаемое