linux: add support for AppImage packages

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/
This commit is contained in:
guihkx authored and DevilXD committed 2023-11-16 20:36:25 +01:00
1 parent 1e31957388
commit a31fa375c9
4 files changed
+166 -9

No files matched your search

+16 -9
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
import os
import sys
import random
import logging
@@ -18,8 +19,9 @@ if TYPE_CHECKING:
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")
@@ -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)
@@ -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)