Skip to content

Commit

Permalink
linux: add support for AppImage packages
Browse files Browse the repository at this point in the history
AppImage is a more flexible packaging solution for Linux than what
PyInstaller currently offers.

Some of the benefits are:

- Ability to ship the app with predefined environment variables
(solving issues like #321)
- Improved integration with the desktop environment (if you're using
go-appimage[1] or libappimage[2])
- Much lighter in size (~27 MB difference)

To actually make the AppImage, I'm using appimage-builder[3], which
makes the process a bit easier.

For more details about the AppImage technology, check out their
website[4].

[1] https://github.com/probonopd/go-appimage
[2] https://github.com/AppImageCommunity/libappimage
[3] https://appimage-builder.readthedocs.io/en/latest/index.html
[4] https://appimage.org/
  • Loading branch information
guihkx authored and DevilXD committed Nov 16, 2023
1 parent 1e31957 commit a31fa37
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ log.txt
/lock.file
settings.json
/lang/English.json
# AppImage
/AppDir
/appimage-builder
/appimage-build
/*.AppImage
/*.AppImage.zsync
144 changes: 144 additions & 0 deletions appimage/AppImageBuilder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Useful links:
# https://appimage-builder.readthedocs.io/en/latest/reference/recipe.html

version: 1

script:
# Clean up any previous builds.
- rm -rf "$BUILD_DIR" "$TARGET_APPDIR" || true
- mkdir -p "$BUILD_DIR" "$TARGET_APPDIR"
- cd "$BUILD_DIR"

# Build a recent version of libXft first. This fixes an issue where the app won't start with libXft 2.3.3 if an emoji font is installed on the system.
- curl -L https://xorg.freedesktop.org/releases/individual/lib/libXft-2.3.8.tar.xz -o libXft.tar.xz
- tar xvf libXft.tar.xz
- cd libXft-2.3.8
- ./configure --prefix="$TARGET_APPDIR/usr" --disable-static
- make -j$(nproc)
- make install-strip

# Package the app.
- mkdir -p "$TARGET_APPDIR"/usr/{src,share/icons/hicolor/128x128/apps}
- cp -r "$SOURCE_DIR/../lang" "$SOURCE_DIR/../pickaxe.ico" "$SOURCE_DIR"/../*.py "$TARGET_APPDIR/usr/src"
- cp "$SOURCE_DIR/pickaxe.png" "$TARGET_APPDIR/usr/share/icons/hicolor/128x128/apps/io.github.devilxd.twitchdropsminer.png"

# Remove certifi from the line below if using Python 3.10+.
- python3.8 -m pip install --ignore-installed --prefix=/usr --root="$TARGET_APPDIR" -r "$SOURCE_DIR/../requirements.txt" certifi

AppDir:
app_info:
id: io.github.devilxd.twitchdropsminer
name: Twitch Drops Miner
icon: io.github.devilxd.twitchdropsminer
version: '{{APP_VERSION}}'
exec: usr/bin/python3.8
exec_args: '${APPDIR}/usr/src/main.py $@'

apt:
arch: amd64
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main universe
key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32

include:
- gir1.2-appindicator3-0.1
- python3-tk

exclude:
- adwaita-icon-theme
- dconf-service
- glib-networking-services
- gsettings-desktop-schemas
- hicolor-icon-theme
- humanity-icon-theme
- libavahi-client3
- libavahi-common3
- libbrotli1
- libcolord2
- libcups2
- libdb5.3
- libdconf1
- libgmp10
- libgnutls30
- libgssapi-krb5-2
- libhogweed5
- libicu66
- libjson-glib-1.0-0
- libk5crypto3
- libkrb5-3
- libkrb5support0
- liblcms2-2
- libncursesw6
- libnettle7
- libp11-kit0
- libpangoxft-1.0-0
- libpsl5
- librest-0.7-0
- libsoup2.4-1
- libsoup-gnome2.4-1
- libsqlite3-0
- libtasn1-6
- libtiff5
- libtinfo6
- libunistring2
- libwebp6
- libxft2 # We'll ship our own, updated version of this library.
- libxml2
- mime-support
- readline-common
- tzdata
- xkb-data

files:
exclude:
- etc
- usr/bin/normalizer
- usr/bin/pdb3*
- usr/bin/py3*
- usr/bin/pydoc*
- usr/bin/pygettext3*
- usr/include
# The next 2 files come from our own build of libXft, and they can be removed.
- usr/lib/libXft.la
- usr/lib/pkgconfig
- usr/lib/python3.9
- usr/lib/valgrind
- usr/lib/*-linux-gnu/engines-1.1
- usr/lib/*-linux-gnu/glib-2.0
- usr/lib/*-linux-gnu/gtk-3.0
- usr/lib/*-linux-gnu/libgtk-3.0
- usr/share/applications
- usr/share/binfmts
- usr/share/bug
- usr/share/doc
- usr/share/doc-base
- usr/share/glib-2.0
- usr/share/lintian
- usr/share/man
- usr/share/pixmaps
- usr/share/python3
- usr/share/themes

runtime:
env:
PATH: '${APPDIR}/usr/bin:${PATH}'
PYTHONHOME: '${APPDIR}/usr'
PYTHONPATH: '${APPDIR}/usr/lib/python3.8/tkinter:${APPDIR}/usr/lib/python3.8/site-packages'
APPDIR_LIBRARY_PATH: '${APPDIR}/usr/lib:${APPDIR}/usr/lib/x86_64-linux-gnu:${APPDIR}/lib/x86_64-linux-gnu'
TCL_LIBRARY: '${APPDIR}/usr/share/tcltk/tcl8.6'
TK_LIBRARY: '${APPDIR}/usr/lib/tcltk/x86_64-linux-gnu/tk8.6'
TKPATH: '${APPDIR}/usr/lib/tcltk/x86_64-linux-gnu/tk8.6'
# Remove the line below if using Python 3.10+.
SSL_CERT_FILE: '${APPDIR}/usr/lib/python3.8/site-packages/certifi/cacert.pem'
# The app seems to have problems running on Wayland at the moment.
# See: https://github.com/DevilXD/TwitchDropsMiner/issues/321
GDK_BACKEND: x11

AppImage:
arch: x86_64
file_name: Twitch.Drops.Miner-x86_64.AppImage
sign-key: None
update-information: guess
Binary file added appimage/pickaxe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 16 additions & 9 deletions constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import os
import sys
import random
import logging
Expand All @@ -18,8 +19,9 @@
from typing_extensions import TypeAlias


# True if we're running from a built EXE, False inside a dev build
IS_PACKAGED = hasattr(sys, "_MEIPASS")
# True if we're running from a built EXE (or a Linux AppImage), False inside a dev build
IS_APPIMAGE = "APPIMAGE" in os.environ and os.path.exists(os.environ["APPIMAGE"])
IS_PACKAGED = hasattr(sys, "_MEIPASS") or IS_APPIMAGE
# logging special levels
CALL = logging.INFO - 1
logging.addLevelName(CALL, "CALL")
Expand All @@ -39,7 +41,9 @@ def _resource_path(relative_path: Path | str) -> Path:
Works for dev and for PyInstaller.
"""
if IS_PACKAGED:
if IS_APPIMAGE:
base_path = Path(sys.argv[0]).absolute().parent
elif IS_PACKAGED:
# PyInstaller's folder where the one-file app is unpacked
meipass: str = getattr(sys, "_MEIPASS")
base_path = Path(meipass)
Expand Down Expand Up @@ -73,12 +77,15 @@ def _merge_vars(base_vars: JsonType, vars: JsonType) -> None:


# Base Paths
# NOTE: pyinstaller will set this to its own executable when building,
# detect this to use __file__ and main.py redirection instead
SELF_PATH = Path(sys.argv[0]).absolute()
if SELF_PATH.stem == "pyinstaller":
SELF_PATH = Path(__file__).with_name("main.py").absolute()
WORKING_DIR = SELF_PATH.absolute().parent
if IS_APPIMAGE:
SELF_PATH = Path(os.environ["APPIMAGE"]).absolute()
else:
# NOTE: pyinstaller will set sys.argv[0] to its own executable when building,
# detect this to use __file__ and main.py redirection instead
SELF_PATH = Path(sys.argv[0]).absolute()
if SELF_PATH.stem == "pyinstaller":
SELF_PATH = Path(__file__).with_name("main.py").absolute()
WORKING_DIR = SELF_PATH.parent
# Development paths
VENV_PATH = Path(WORKING_DIR, "env")
SITE_PACKAGES_PATH = Path(VENV_PATH, SYS_SITE_PACKAGES)
Expand Down

0 comments on commit a31fa37

Please sign in to comment.