Приложение на Qt6#
Пример показывает два пути для одной и той же программы на Qt6: сборку из исходников и упаковку готовой бинарной поставки из архива. Программа пишется здесь же, поэтому оба пути повторяются целиком, без загрузок со стороны.
Qt6-приложения работают на среде org.astra.Qt6. Она несёт Qt6 целиком:
libqt6core6t64, libqt6gui6, libqt6widgets6, libqt6network6,
QML и мультимедиа. Qt6 существует только для линии 1.8.6, поэтому версия среды
и SDK здесь одна.
Подготовка#
sudo flatpak install astra-main org.astra.Qt6//1.8.6
sudo flatpak install astra-extended org.astra.Qt6Sdk//1.8.6
Дерево примера#
qt6-hello/
|-- qt6-hello.yaml
`-- src/
|-- CMakeLists.txt
|-- main.cpp
|-- ru.example.Qt6Hello.desktop
|-- ru.example.Qt6Hello.metainfo.xml
`-- icons/
`-- 128x128/
`-- app.png
В каталог icons/128x128 кладётся любой PNG размером 128 на 128. Имя файла
роли не играет: при сборке иконки переименовываются в идентификатор приложения.
Исходный код#
src/main.cpp:
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QLabel label("Qt6 внутри Flatpak");
label.setAlignment(Qt::AlignCenter);
label.resize(360, 120);
label.setWindowTitle("Qt6 Hello");
label.show();
return app.exec();
}
src/CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(qt6-hello LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
add_executable(qt6-hello main.cpp)
target_link_libraries(qt6-hello PRIVATE Qt6::Widgets)
install(TARGETS qt6-hello RUNTIME DESTINATION bin)
src/ru.example.Qt6Hello.desktop:
[Desktop Entry]
Type=Application
Name=Qt6 Hello
Name[ru]=Пример на Qt6
Comment=Минимальное приложение на Qt6
Exec=qt6-hello
Icon=qt6-hello
Terminal=false
Categories=Utility;
src/ru.example.Qt6Hello.metainfo.xml:
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>ru.example.Qt6Hello</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<name>Qt6 Hello</name>
<summary>Минимальное приложение на Qt6</summary>
<description>
<p>Пример приложения на Qt6 для проверки сборки во Flatpak.</p>
</description>
<launchable type="desktop-id">ru.example.Qt6Hello.desktop</launchable>
</component>
Путь первый: сборка из исходников#
qt6-hello.yaml:
version: "1.0"
metadata:
id: ru.example.Qt6Hello
name: "Qt6 Hello"
version: "1.0"
arch: amd64
branch: stable
runtime:
id: org.astra.Qt6
version: "1.8.6"
sdk: org.astra.Qt6Sdk
source:
type: sourcedir
sourcedir:
path: ./src
buildDeps:
packages:
- qt6-base-dev
- qt6-tools-dev
- cmake
buildCommands:
- cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/app -DCMAKE_BUILD_TYPE=Release
- cmake --build build -j
- cmake --install build
files:
command: qt6-hello
desktop: ./src/ru.example.Qt6Hello.desktop
metainfo: ./src/ru.example.Qt6Hello.metainfo.xml
icons-dir: ./src/icons
permissions:
sockets: [wayland, fallback-x11]
devices: [dri]
shares: [ipc]
filesystems: [xdg-documents]
persist: [.config]
export:
repository: ./repo
Сборка и установка:
cd qt6-hello
aft-app validate qt6-hello.yaml
sudo aft-app build qt6-hello.yaml
sudo flatpak remote-add --if-not-exists --no-gpg-verify local-repo ./repo
sudo flatpak install -y local-repo ru.example.Qt6Hello
flatpak run ru.example.Qt6Hello
Путь второй: упаковка из архива#
Источник tar рассчитан на случай, когда программа приходит собранной. Чтобы
повторить этот путь на том же примере, архив сначала нужно сделать. Сборка идёт
обычным образом, вне Flatpak, на машине с установленными qt6-base-dev и
cmake:
cmake -S src -B build-native -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
cmake --build build-native -j
DESTDIR="$PWD/stage" cmake --install build-native
В stage получится дерево usr/bin/qt6-hello. Дальше содержимое
перекладывается в плоский каталог и упаковывается:
mkdir -p pack/data
cp stage/usr/bin/qt6-hello pack/
cp src/ru.example.Qt6Hello.desktop pack/data/
tar -C pack -cJf qt6-hello-1.0-linux-x64.tar.xz qt6-hello data
Плоский каталог здесь не случайность. Правила раскладки сопоставляются с
именами записей верхнего уровня архива, а не с полными путями внутри него.
Правило с шаблоном lib/* не сработает никогда: на верхнем уровне нет записи
с таким именем. Каталог, попавший под правило, копируется целиком в
<dest>/<имя каталога>, поэтому архив собирается без обёрточного каталога
вида qt6-hello-1.0.
Манифест для архива отличается только секцией источника:
version: "1.0"
metadata:
id: ru.example.Qt6Hello
name: "Qt6 Hello"
version: "1.0"
arch: amd64
branch: stable
runtime:
id: org.astra.Qt6
version: "1.8.6"
sdk: org.astra.Qt6Sdk
source:
type: tar
tar:
path: ./qt6-hello-1.0-linux-x64.tar.xz
layout:
- { pattern: "qt6-hello", dest: bin }
- { pattern: "data", dest: share }
files:
command: qt6-hello
desktop: ./src/ru.example.Qt6Hello.desktop
metainfo: ./src/ru.example.Qt6Hello.metainfo.xml
icons-dir: ./src/icons
permissions:
sockets: [wayland, fallback-x11]
devices: [dri]
shares: [ipc]
filesystems: [xdg-documents]
persist: [.config]
export:
repository: ./repo
Если в архиве лежат ещё и библиотеки, они разбираются отдельным правилом, а недостающие системные библиотеки докладываются пакетами из репозитория:
source:
type: tar
tar:
path: ./app-1.0-linux-x64.tar.xz
packages: [libwebp7]
layout:
- { pattern: "app", dest: bin }
- { pattern: "lib*", dest: lib }
- { pattern: "plugins", dest: lib }
Шаблон lib* заберёт все записи верхнего уровня, чьё имя начинается с
lib, то есть и libapp.so.1, и libcodec.so. Поддерживаются только
* и ?, и сопоставление идёт с именем записи целиком.
Для источников tar и system deb-зависимости не разбираются: состав задаёт сам источник. Что программа требует на самом деле, покажет analyze по разделяемым библиотекам:
aft-app analyze --tar ./qt6-hello-1.0-linux-x64.tar.xz \
--runtime org.astra.Qt6 --runtime-version 1.8.6
Тонкости Qt6#
Если поставщик собрал приложение со своим Qt внутри, эти библиотеки в сборке лишние: они дублируют среду и мешают ей. Их убирают шаблонами очистки:
files:
cleanPatterns:
- "lib/libQt6*"
Приложениям на QML нужны модули QML. Они есть в среде, но Qt ищет их относительно расположения своих библиотек, поэтому чаще всего проблема оказывается ровно в предыдущем пункте: приложение принесло свою копию Qt.
Qt6 не тянет за собой Qt5. Программа, собранная против Qt6, но подгружающая плагин на Qt5, потребует обеих сред сразу, а так не бывает: плагин придётся исключить или заменить.
Диалоги открытия и печати Qt6 использует через xdg-desktop-portal. Разрешение на весь домашний каталог для этого не нужно: диалог возвращает приложению уже открытый файл.