Сборка Wine-приложений во Flatpak с помощью AFT#

Документ описывает, как упаковать Windows-приложение вместе с Wine в один Flatpak так, чтобы после flatpak install оно запускалось сразу, без ручной настройки WINEPREFIX. За основу взят инструмент aft-app из набора AFT.

Концепция#

У aft-app нет отдельного шага установки .exe. Windows-приложение всегда вносится в сборку одним из способов:

  • готовый WINEPREFIX - каталог с уже установленной программой кладётся в приложение как шаблон;

  • портативные файлы - каталог с .exe кладётся в приложение, префикс создаётся при первом запуске.

Запуск в обоих случаях делает скрипт-обёртка: при первом старте он разворачивает префикс в каталог данных приложения и вызывает wine app.exe.

Примечание

WINEPREFIX нельзя скачать готовым. Префиксы не распространяются отдельным файлом: они содержат абсолютные симлинки (dosdevices, ссылки на хостовые шрифты и устройства) и привязаны к конкретной версии Wine. Префикс всегда генерируется командой wineboot, а после переноса перепривязывается командой wineboot -u.

Ключевые особенности Wine#

При упаковке именно Wine есть три момента, которых нет у обычных приложений.

32-битная поддержка (ia32-libs)#

Для запуска 32-битных Windows-программ Wine нужен пакет ia32-libs (32-битные библиотеки). При выбранном runtime org.astra.Qt он уже входит в среду выполнения, поэтому докладывать его в приложение не нужно - он не попадает в сборку и не увеличивает её размер. 32-битные программы работают без дополнительной настройки.

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

$ aft-app analyze --package wine --runtime org.astra.Qt --runtime-version 1.8.5

Примечание

На голом базовом runtime (например org.astra.mainPlatform) ia32-libs в среде нет - тогда он попадает в приложение автоматически как обязательная зависимость wine (источник repository или dependencies.additional: [wine]). Это рабочий вариант, но сборка получается крупнее. Ещё один повод предпочесть org.astra.Qt.

Предупреждение

SIGSYS при первом запуске. Стандартный seccomp-фильтр Flatpak блокирует системные вызовы, нужные Wine (modify_ldt и др., а также ptrace для wine-preloader). Без послаблений процесс падает с кодом 159 (128 + SIGSYS). Обязательно добавьте разрешения:

permissions:
  allow: [multiarch, devel]

multiarch разрешает 32-битные syscalls, devel - ptrace.

Рекомендованные зависимости#

В Recommends пакета wine лежит то, без чего Wine работает наполовину: Vulkan/OpenGL (Direct3D), GnuTLS (HTTPS и .NET), SDL2, CUPS (печать). Для игр и сложных приложений включайте их:

dependencies:
  includeRecommends: true

Для простых приложений (как PuTTY) рекомендованные пакеты не нужны - достаточно обязательных.

Подготовка окружения#

  1. Подключите репозитории AstraFlatHub и установите инструменты AFT (см. раздел Начало работы в документации AFT).

  2. Установите базовую среду выполнения и SDK:

    $ sudo flatpak install -y astra-main org.astra.Qt//1.8.5
    $ sudo flatpak install -y astra-ext  org.astra.qtSdk//1.8.5
    

    Для Wine удобнее всего org.astra.Qt: этот runtime уже содержит ia32-libs и большинство библиотек, от которых зависит Wine. Меньше зависимостей - меньше размер и быстрее сборка. Проверить покрытие можно командой:

    $ aft-app analyze --package wine --runtime org.astra.Qt --runtime-version 1.8.5
    
  3. Установите Wine в систему - он нужен только для генерации эталонного префикса:

    $ sudo apt-get install -y --no-install-recommends wine
    

Шаг 1. Получение Windows-приложения#

Скачайте дистрибутив приложения. В примере - портативный putty.exe:

$ curl -fsSL -o putty.exe \
    "https://the.earth.li/~sgtatham/putty/latest/w64/putty.exe"

Шаг 2. Создание готового WINEPREFIX#

Создайте чистый префикс и поместите в него приложение.

$ export WINEPREFIX="$HOME/wine-template-prefix"
$ export WINEDLLOVERRIDES="mscoree,mshtml="   # не тянуть mono/gecko из сети
$ rm -rf "$WINEPREFIX"
$ wineboot -i            # инициализация префикса
$ wineserver -w          # дождаться завершения

$ mkdir -p "$WINEPREFIX/drive_c/Program Files/PuTTY"
$ cp putty.exe "$WINEPREFIX/drive_c/Program Files/PuTTY/putty.exe"

Если у вас установщик setup.exe - вместо копирования запустите его один раз в этом префиксе: wine setup.exe и пройдите установку.

Перенесите готовый префикс в каталог сборки приложения:

$ mkdir -p wineapp-src
$ cp -a "$WINEPREFIX" wineapp-src/wineprefix-template

Шаг 3. Скрипт-обёртка запуска#

Создайте wineapp-src/launch.sh. Он разворачивает префикс при первом старте (/app доступен только для чтения, префикс должен жить в данных приложения) и запускает .exe.

#!/bin/sh
set -e

# Каталог данных приложения (сохраняется между запусками)
DATA="${XDG_DATA_HOME:-$HOME/.local/share}/org.astra.PuTTY"
export WINEPREFIX="$DATA/prefix"

export WINEDLLOVERRIDES="${WINEDLLOVERRIDES:-mscoree,mshtml=}"
export WINEDEBUG="${WINEDEBUG:-fixme-all}"

# Готовим префикс ровно один раз
if [ ! -f "$WINEPREFIX/system.reg" ]; then
    mkdir -p "$DATA"
    if [ -d /app/share/wineprefix-template ]; then
        cp -a /app/share/wineprefix-template "$WINEPREFIX"
        wineboot -u            # перепривязать пути после переноса
    else
        wineboot -i            # чистый префикс, если шаблона нет
    fi
    wineserver -w
fi

exec wine "C:\\Program Files\\PuTTY\\putty.exe" "$@"

Шаг 4. Манифест сборки#

Создайте wine-putty.yaml. Источник - каталог sourcedir: build-команды раскладывают обёртку и шаблон префикса в /app. Сам Wine добавляется через dependencies.additional.

version: "1.0"

metadata:
  id: org.astra.PuTTY
  name: "PuTTY (Wine)"
  version: "0.83"
  arch: amd64
  branch: stable

runtime:
  id: org.astra.Qt
  version: "1.8.5"
  sdk: org.astra.qtSdk

source:
  type: sourcedir
  sourcedir:
    path: /home/user/wineapp-src
    buildCommands:
      - install -Dm755 launch.sh /app/bin/putty
      - mkdir -p /app/share && cp -a wineprefix-template /app/share/wineprefix-template

dependencies:
  analyze: true
  includeRecommends: false   # для игр/Direct3D поставьте true
  additional: [wine]         # докладывает Wine;

files:
  command: putty

permissions:
  sockets: [x11]
  shares: [network, ipc]
  devices: [dri, shm]
  filesystems: [home]
  allow: [multiarch, devel]  # обязательно для Wine (иначе SIGSYS)

build:
  sbom: true
  cache: true

export:
  repository: ./repo

Шаг 5. Сборка, установка, проверка#

$ aft-app validate wine-putty.yaml
$ sudo aft-app build wine-putty.yaml

$ flatpak remote-add --no-gpg-verify --if-not-exists myrepo ./repo
$ sudo flatpak install -y myrepo org.astra.PuTTY
$ flatpak run org.astra.PuTTY

Быстрая проверка, что Wine жив внутри песочницы (должно вернуть wine-10.0):

$ flatpak run --command=wine org.astra.PuTTY --version

При первом flatpak run префикс разворачивается в ~/.var/app/org.astra.PuTTY/data/org.astra.PuTTY/prefix и приложение запускается.

Справочник разрешений#

Поле

Значение для примера

Назначение

allow

[multiarch, devel]

Обязательно для Wine. Послабление seccomp, иначе SIGSYS.

sockets

[x11] (+ wayland, pulseaudio)

Графика и звук Windows-приложения.

devices

[dri, shm]

dri - GPU (GL/Vulkan), shm - общая память X11.

shares

[network, ipc]

Сеть (онлайн-приложения, игры) и IPC для X11.

filesystems

[home]

Доступ к файлам пользователя.

Варианты#

Установщик setup.exe

На шаге 2 вместо копирования запустите wine setup.exe в эталонном префиксе, затем поправьте путь к .exe в launch.sh.

Портативное приложение без префикса-шаблона

Не кладите wineprefix-template - ветка wineboot -i в launch.sh создаст чистый префикс при первом запуске. Файлы приложения положите в /app build-командой и запускайте их по пути /app/....

.NET и HTML внутри Wine

Чтобы работали офлайн, добавьте пакеты в сборку: dependencies.additional: [wine, wine-mono, wine-gecko].

Один файл вместо репозитория

Добавьте в export поле bundle: putty.flatpak - рядом с репозиторием соберётся одиночный файл .flatpak для установки командой flatpak install putty.flatpak.

Полный пример: игра SuperTuxKart#

Разберём упаковку портативной 3D-игры. Отличия от примера с PuTTY:

  • приложение портативное - префикс-шаблон не нужен, обёртка создаёт чистый префикс при первом запуске (ветка wineboot -i);

  • игре нужен GPU, поэтому includeRecommends: true (OpenGL/Vulkan) и devices: [dri, shm];

  • нестандартная структура архива - данные лежат отдельно от .exe.

Загрузка#

Портативную Windows-сборку берём с GitHub (надёжный CDN):

$ curl -fsSL --retry 5 -o stk.zip \
    "https://github.com/supertuxkart/stk-code/releases/download/1.5/SuperTuxKart-1.5-win.zip"

Распаковка#

Архив содержит сборки под несколько архитектур и отладочные символы (.pdb, ~591 МБ). Берём только 64-битную сборку и общие данные, символы выкидываем:

$ mkdir game
$ unzip -q stk.zip \
    "SuperTuxKart-1.5-win/stk-code/build-x86_64/*" \
    "SuperTuxKart-1.5-win/stk-code/data/*" -d game
$ mv game/SuperTuxKart-1.5-win/stk-code game/stk-code && rm -rf game/SuperTuxKart-1.5-win
$ find game -name '*.pdb' -delete

В итоге .exe лежит в game/stk-code/build-x86_64/bin/, а данные - в game/stk-code/data/ (823 МБ).

Примечание

Игра ищет каталог data относительно расположения .exe (как в штатном run_game.bat, который делает pushd в каталог сборки). Поэтому в обёртке перед запуском обязательно cd в каталог с .exe - структуру stk-code/{build-x86_64,data} нужно сохранить.

Обёртка launch.sh#

#!/bin/sh
set -e
DATA="${XDG_DATA_HOME:-$HOME/.local/share}/org.astra.SuperTuxKart"
export WINEPREFIX="$DATA/prefix"
export WINEDLLOVERRIDES="${WINEDLLOVERRIDES:-mscoree,mshtml=}"
export WINEDEBUG="${WINEDEBUG:-fixme-all}"

if [ ! -f "$WINEPREFIX/system.reg" ]; then
    mkdir -p "$DATA"
    wineboot -i            # портативная игра - чистый префикс с нуля
    wineserver -w
fi

cd /app/share/supertuxkart/stk-code/build-x86_64/bin
exec wine supertuxkart.exe "$@"

Манифест wine-supertuxkart.yaml#

version: "1.0"
metadata:
  id: org.astra.SuperTuxKart
  name: "SuperTuxKart (Wine)"
  version: "1.5"
  arch: amd64
  branch: stable
runtime:
  id: org.astra.Qt
  version: "1.8.5"
  sdk: org.astra.qtSdk
source:
  type: sourcedir
  sourcedir:
    path: /home/user/stk-build
    buildCommands:
      - install -Dm755 launch.sh /app/bin/supertuxkart
      - mkdir -p /app/share/supertuxkart && cp -a game/. /app/share/supertuxkart/
dependencies:
  analyze: true
  includeRecommends: true    # OpenGL/Vulkan для 3D
  additional: [wine]
files:
  command: supertuxkart
permissions:
  sockets: [x11, wayland, pulseaudio]
  devices: [dri, shm]          # dri - GPU обязательно
  shares: [network, ipc]
  filesystems: [home]
  allow: [multiarch, devel]    # обязательно для Wine
build:
  sbom: true
  cache: true
export:
  repository: ./repo

Сборка и проверка#

$ aft-app validate wine-supertuxkart.yaml
$ sudo aft-app build wine-supertuxkart.yaml
$ sudo flatpak install -y myrepo org.astra.SuperTuxKart
$ flatpak run org.astra.SuperTuxKart

При первом запуске префикс создаётся за несколько секунд, игра находит данные и пишет свой конфиг в drive_c/users/<user>/AppData/Roaming/supertuxkart/.

Примечание

В логе возможны некритичные предупреждения окружения (Fontconfig error: Cannot load default config file, ALSA lib ... Cannot access file /usr/share/alsa/alsa.conf). На запуск они не влияют; при желании устраняются добавлением fontconfig / alsa в dependencies.additional.