#!/usr/bin/env bash # Build a QtPotato AppImage from the host's Qt/KF6/mpv libraries. # # The result only runs on distros whose glibc is at least as new as the build host's, # so build on the oldest distro you want to support. # # Usage: packaging/appimage/build-appimage.sh [build-dir] set -euo pipefail SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" BUILD_DIR="$(realpath -m "${1:-$SRC_DIR/build-appimage}")" TOOLS_DIR="${TOOLS_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/qtpotato-appimage-tools}" APPDIR="$BUILD_DIR/AppDir" QT_PLUGINS="$(qmake6 -query QT_INSTALL_PLUGINS)" export APPIMAGE_EXTRACT_AND_RUN=1 # works without FUSE # The strip bundled with linuxdeploy is too old for binaries from current distros # (e.g. DT_RELR sections) and aborts the deployment. export NO_STRIP=1 fetch_tool() { local repo=$1 file=$2 if [[ ! -x "$TOOLS_DIR/$file" ]]; then curl -fL --create-dirs -o "$TOOLS_DIR/$file" \ "https://github.com/linuxdeploy/$repo/releases/download/continuous/$file" chmod +x "$TOOLS_DIR/$file" fi } fetch_tool linuxdeploy linuxdeploy-x86_64.AppImage fetch_tool linuxdeploy-plugin-qt linuxdeploy-plugin-qt-x86_64.AppImage fetch_tool linuxdeploy-plugin-appimage linuxdeploy-plugin-appimage-x86_64.AppImage export PATH="$TOOLS_DIR:$PATH" cmake -S "$SRC_DIR" -B "$BUILD_DIR" -G Ninja \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTING=OFF cmake --build "$BUILD_DIR" rm -rf "$APPDIR" DESTDIR="$APPDIR" cmake --install "$BUILD_DIR" # Plugins that are loaded at runtime by name, so linuxdeploy-plugin-qt cannot find them: # Kirigami's style plugins, the Plasma platform theme (colors, fonts, icon theme and the # QtQuick style from the user's Plasma settings) and the Breeze/Union widget styles. copy_plugins() { local subdir=$1; shift mkdir -p "$APPDIR/usr/plugins/$subdir" for pattern in "$@"; do for f in "$QT_PLUGINS/$subdir"/$pattern; do if [[ -e "$f" ]]; then cp "$f" "$APPDIR/usr/plugins/$subdir/"; fi done done } copy_plugins kf6/kirigami/platform '*.so' copy_plugins platformthemes KDEPlasmaPlatformTheme6.so copy_plugins styles breeze6.so UnionWidgetsStyle.so copy_plugins union '*.so' copy_plugins kf6 FrameworkIntegrationPlugin.so # EGL integration for Wayland; without it Qt Quick cannot create an OpenGL context. copy_plugins wayland-graphics-integration-client '*.so' # QtQuick styles and their private modules are also picked at runtime; list them in a # dummy QML file so qmlimportscanner deploys them. EXTRA_QML="$BUILD_DIR/appimage-extra-qml" mkdir -p "$EXTRA_QML" cat > "$EXTRA_QML/Styles.qml" <<'EOF' import QtQuick import QtQuick.Controls import QtQuick.Controls.Fusion import QtQuick.Templates import org.kde.kirigami import org.kde.kirigami.platform import org.kde.desktop import org.kde.qqc2desktopstyle.private import org.kde.breeze import org.kde.union Item {} EOF deps_only=() while IFS= read -r -d '' f; do deps_only+=(--deploy-deps-only="$f") done < <(find "$APPDIR/usr/plugins" -name '*.so' -print0) # linuxdeploy-plugin-qt deploys every image format plugin and fails on any with a missing # dependency (e.g. kimg_jxr without libjpegxr). Hand it a filtered view of the plugin dir # through a qmake wrapper. FILTERED_PLUGINS="$BUILD_DIR/appimage-qt-plugins" rm -rf "$FILTERED_PLUGINS" mkdir -p "$FILTERED_PLUGINS/imageformats" for d in "$QT_PLUGINS"/*; do [[ "$(basename "$d")" == imageformats ]] || ln -s "$d" "$FILTERED_PLUGINS/" done for f in "$QT_PLUGINS"/imageformats/*.so; do if ldd "$f" | grep -q 'not found'; then echo "Skipping $(basename "$f"): missing dependencies" else ln -s "$f" "$FILTERED_PLUGINS/imageformats/" fi done cat > "$BUILD_DIR/qmake-wrapper" </dev/null || echo dev)-x86_64.AppImage}" cd "$BUILD_DIR" linuxdeploy-x86_64.AppImage \ --appdir "$APPDIR" \ --executable "$APPDIR/usr/bin/qtpotato" \ --desktop-file "$APPDIR/usr/share/applications/dev.trivk.qtpotato.desktop" \ --icon-file "$APPDIR/usr/share/icons/hicolor/scalable/apps/dev.trivk.qtpotato.svg" \ --custom-apprun "$SRC_DIR/packaging/appimage/AppRun" \ "${deps_only[@]}" \ --plugin qt \ --output appimage echo "AppImage: $LDAI_OUTPUT"