From ab55cad2f17779ed31db4545943e20b75855cfbf Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 1 Mar 2025 18:47:10 +0100 Subject: [PATCH 1/9] fix: trim version suffix to avoid InvalidVersion fix: handle `continuous` without error --- appimagebuilder/commands/setup_runtime.py | 11 ++++++----- .../modules/setup/apprun_3/helpers/glibc_module.py | 9 ++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/appimagebuilder/commands/setup_runtime.py b/appimagebuilder/commands/setup_runtime.py index 5341aeca..8072db45 100644 --- a/appimagebuilder/commands/setup_runtime.py +++ b/appimagebuilder/commands/setup_runtime.py @@ -28,14 +28,15 @@ def id(self): def __call__(self, *args, **kwargs): apprun_version = self.context.recipe.AppDir.runtime.version() or "v2.0.0" - apprun_version = version.parse(apprun_version) + if apprun_version == "continuous": + apprun_version = version.parse("v2.0.0") + else: + apprun_version = version.parse(apprun_version.split("-")[0]) runtime_setup = None - if ( - version.parse("v2.0.0") <= apprun_version < version.parse("v3.0.0") - ) or apprun_version == version.parse("continuous"): + if version.parse("v2.0.0") <= apprun_version < version.parse("v3.0.0"): runtime_setup = AppRunV2Setup(self.context, self._finder) - if not runtime_setup and version.parse("v3.0.0-devel") <= apprun_version < version.parse("v4.0.0"): + if not runtime_setup and version.parse("v3.0.0") <= apprun_version < version.parse("v4.0.0"): runtime_setup = AppRunV3Setup(self.context) if not runtime_setup: diff --git a/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py b/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py index 167c3c40..5ca270cb 100644 --- a/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py +++ b/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py @@ -132,9 +132,12 @@ def _find_bundled_glibc_version(self): # compare with current major version and update if necessary if version_name and version_name.startswith("GLIBC_"): version_value = version_name.split("_")[1] - parsed_version = packaging.version.parse(version_value) - if parsed_version > major_version: - major_version = parsed_version + try: + parsed_version = packaging.version.parse(version_value) + if parsed_version > major_version: + major_version = parsed_version + except Exception: + pass if major_version == packaging.version.parse("0.0.0"): raise Exception("Could not find glibc library in module files") From a31c406554c07c3671f13e455045e7fc86bab836 Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 1 Mar 2025 19:08:49 +0100 Subject: [PATCH 2/9] chore: bump deps Also fix LIEF API usage --- appimagebuilder/modules/setup/apprun_3/app_dir_info.py | 2 +- .../modules/setup/apprun_3/helpers/glibc_module.py | 6 +++--- appimagebuilder/modules/setup/apprun_utils.py | 2 +- requirements.txt | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/appimagebuilder/modules/setup/apprun_3/app_dir_info.py b/appimagebuilder/modules/setup/apprun_3/app_dir_info.py index 5d712314..69a27030 100644 --- a/appimagebuilder/modules/setup/apprun_3/app_dir_info.py +++ b/appimagebuilder/modules/setup/apprun_3/app_dir_info.py @@ -88,7 +88,7 @@ def read_file_info(entry: pathlib.Path): if file_info.is_elf: file_info.interpreter = binary.interpreter file_info.machine_type = binary.header.machine_type - soname = binary.get(lief.ELF.DYNAMIC_TAGS.SONAME) + soname = binary.get(lief.ELF.DynamicEntry.TAG.SONAME) # store only the string representation of the soname if soname: file_info.soname = soname.name diff --git a/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py b/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py index 5ca270cb..016d8b2e 100644 --- a/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py +++ b/appimagebuilder/modules/setup/apprun_3/helpers/glibc_module.py @@ -60,16 +60,16 @@ def _patch_binaries_interpreter_path(self): for file in self.context.app_dir.files.values(): if file.interpreter and not self._is_file_in_a_module(file) and not file.path.is_symlink(): binary = lief.parse(file.path.__str__()) - self._patch_binary_interpreter_path(binary) + self._patch_binary_interpreter_path(binary, file.path.__str__()) - def _patch_binary_interpreter_path(self, binary): + def _patch_binary_interpreter_path(self, binary, output): """Patch the interpreter of a binary making it relative""" interpreter = binary.interpreter new_interpreter = interpreter.lstrip("/") binary.interpreter = new_interpreter - binary.write(binary.name) + binary.write(output) def _extract_library_paths_from_glibc_module_files(self): """Extracts library paths from glibc module files""" diff --git a/appimagebuilder/modules/setup/apprun_utils.py b/appimagebuilder/modules/setup/apprun_utils.py index 8d6853b8..f6ff635c 100644 --- a/appimagebuilder/modules/setup/apprun_utils.py +++ b/appimagebuilder/modules/setup/apprun_utils.py @@ -37,7 +37,7 @@ def is_binary_a_shared_library(binary): # read soname from ELF header if binary: - soname = binary.get(lief.ELF.DYNAMIC_TAGS.SONAME) + soname = binary.get(lief.ELF.DynamicEntry.TAG.SONAME) return soname is not None return False diff --git a/requirements.txt b/requirements.txt index d70d29a5..8a939e9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ arpy==2.3.0 certifi==2023.7.22 -cffi==1.16.0 +cffi==1.17.1 charset-normalizer==3.3.0 configparser==6.0.0 contextlib2==21.6.0 @@ -13,8 +13,8 @@ filelock==3.12.4 idna==3.4 jsonpath-rw==1.4.0 libconf==2.0.1 -lief==0.12.2 -packaging==23.2 +lief==0.16.4 +packaging==24.2 PGPy==0.6.0 pipenv==2023.10.3 platformdirs==3.11.0 From 8f9c279b6f43edfb229a82556ccc0f864a29c136 Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 1 Mar 2025 19:25:53 +0100 Subject: [PATCH 3/9] fix: apprun_3 error --- appimagebuilder/modules/setup/apprun_3/apprun3.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/appimagebuilder/modules/setup/apprun_3/apprun3.py b/appimagebuilder/modules/setup/apprun_3/apprun3.py index 00dd5da1..b5b7bc60 100644 --- a/appimagebuilder/modules/setup/apprun_3/apprun3.py +++ b/appimagebuilder/modules/setup/apprun_3/apprun3.py @@ -122,9 +122,12 @@ def _find_libapprun_hooks_so_target_dir(self, arch): ] # find dedicated folder for the architecture for base_dir in base_dirs: - for entry in base_dir.iterdir(): - if entry.is_dir() and arch in entry.name: - return entry + try: + for entry in base_dir.iterdir(): + if entry.is_dir() and arch in entry.name: + return entry + except FileNotFoundError: # /usr/lib64 is often omitted + pass return None From eb7e210f94d8b1df3f22f99e6ef5761491b71dba Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 1 Mar 2025 21:21:29 +0100 Subject: [PATCH 4/9] fix: find the most specific libstdc++ version If we match the first found on Debian, then we're likely to find only version 6, without minor, which is present on basically any system. However we need a precise match --- .../modules/setup/apprun_3/helpers/glibstcpp_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appimagebuilder/modules/setup/apprun_3/helpers/glibstcpp_module.py b/appimagebuilder/modules/setup/apprun_3/helpers/glibstcpp_module.py index 490f6002..caf69cb5 100644 --- a/appimagebuilder/modules/setup/apprun_3/helpers/glibstcpp_module.py +++ b/appimagebuilder/modules/setup/apprun_3/helpers/glibstcpp_module.py @@ -73,7 +73,7 @@ def _generate_glibstdcpp_module_config(self, libstdcpp_version, library_paths): def _extract_libstdcpp_version(self): version = None for entry in self._glibstdcpp_module_files: - if entry.soname == 'libstdc++.so.6': + if entry.soname == 'libstdc++.so.6' and not entry.path.is_symlink(): # extract libstdc++ version from file name version = entry.path.name.split('.so.')[-1] break From 7ef15d0ef4437e7c10f036310a3192b7b3b70aa3 Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 1 Mar 2025 21:51:19 +0100 Subject: [PATCH 5/9] feat: log which scripts use missing interpreter --- appimagebuilder/modules/setup/apprun_3/apprun3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appimagebuilder/modules/setup/apprun_3/apprun3.py b/appimagebuilder/modules/setup/apprun_3/apprun3.py index b5b7bc60..f85025db 100644 --- a/appimagebuilder/modules/setup/apprun_3/apprun3.py +++ b/appimagebuilder/modules/setup/apprun_3/apprun3.py @@ -306,7 +306,7 @@ def _patch_script_shebang(self, entry: AppDirFileInfo): f.write(chunk) logging.info("Patched script shebang: %s", entry.__str__()) else: - logging.warning("Script interpreter not found in AppDir: %s", rel_interpreter_path) + logging.warning("Script interpreter for %s not found in AppDir: %s", entry.path.__str__(), rel_interpreter_path) def _find_dirs_containing_executable_files(self): """Finds the dirs containing executable files""" From 1d75183efbb0dfda08ef99c01ad4c701b4b694eb Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 1 Mar 2025 21:52:58 +0100 Subject: [PATCH 6/9] feat(apprun_3): update MIME DB Useful for e.g. gdk-pixbuf --- .../modules/setup/apprun_3/apprun3.py | 2 ++ .../modules/setup/apprun_3/helpers/mime.py | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 appimagebuilder/modules/setup/apprun_3/helpers/mime.py diff --git a/appimagebuilder/modules/setup/apprun_3/apprun3.py b/appimagebuilder/modules/setup/apprun_3/apprun3.py index f85025db..883adba5 100644 --- a/appimagebuilder/modules/setup/apprun_3/apprun3.py +++ b/appimagebuilder/modules/setup/apprun_3/apprun3.py @@ -28,6 +28,7 @@ from appimagebuilder.modules.setup.apprun_3.helpers.glibstcpp_module import AppRun3GLibStdCppSetupHelper from appimagebuilder.modules.setup.apprun_3.helpers.gstreamer import AppRun3GStreamer from appimagebuilder.modules.setup.apprun_3.helpers.python import AppRun3Python +from appimagebuilder.modules.setup.apprun_3.helpers.mime import AppRun3MIME from appimagebuilder.modules.setup.apprun_3.helpers.qt import AppRun3QtSetup @@ -335,6 +336,7 @@ def _run_setup_helpers(self): AppRun3GdkPixbuf(self.context), AppRun3GStreamer(self.context), AppRun3Python(self.context), + AppRun3MIME(self.context), ] for helper in helpers: diff --git a/appimagebuilder/modules/setup/apprun_3/helpers/mime.py b/appimagebuilder/modules/setup/apprun_3/helpers/mime.py new file mode 100644 index 00000000..162203d0 --- /dev/null +++ b/appimagebuilder/modules/setup/apprun_3/helpers/mime.py @@ -0,0 +1,29 @@ +# Copyright 2020 Alexis Lopez Zubieta +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +import logging +import shutil +import subprocess + +from appimagebuilder.modules.setup.apprun_3.helpers.base_helper import AppRun3Helper + + +class AppRun3MIME(AppRun3Helper): + def run(self): + path = self.context.app_dir.path / "usr" / "share" / "mime" + if path.is_dir(): + bin_path = shutil.which("update-mime-database") + if not bin_path: + raise RuntimeError("Missing 'update-mime-database' executable") + + subprocess.run([bin_path, path]) + + print("Updated MIME database") From dfb2926551fde89565b73bcccba7ec275a34860f Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sun, 2 Mar 2025 11:00:11 +0100 Subject: [PATCH 7/9] fix: apply exclude patterns to dependencies Exclude not-AppImage-safe packages like libgl even if they are requested by include patterns --- appimagebuilder/modules/deploy/apt/deploy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appimagebuilder/modules/deploy/apt/deploy.py b/appimagebuilder/modules/deploy/apt/deploy.py index a9193c92..3c75e968 100644 --- a/appimagebuilder/modules/deploy/apt/deploy.py +++ b/appimagebuilder/modules/deploy/apt/deploy.py @@ -66,8 +66,10 @@ def _resolve_packages_to_deploy(self, include_patterns, exclude_patterns): required_packages = set(self.apt_venv.search_packages(include_patterns)) excluded_packages = excluded_packages.difference(required_packages) self.apt_venv.set_installed_packages(excluded_packages) - # lists packages to be installed including dependencies + # lists packages to be installed including dependencies, without explicitly excluded packages + # if a dependency is required to be deployed, please include it in the include list deploy_list = set(self.apt_venv.resolve_packages(include_patterns)) + deploy_list = deploy_list.difference(excluded_packages) return deploy_list From e6b4418c1f6f1fbf52b933f1083bebf0fa2496f7 Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sun, 2 Mar 2025 12:46:23 +0100 Subject: [PATCH 8/9] fix: fix LD_PRELOAD hooks library not found Absolute path resolves reliably --- appimagebuilder/modules/setup/apprun_3/apprun3.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/appimagebuilder/modules/setup/apprun_3/apprun3.py b/appimagebuilder/modules/setup/apprun_3/apprun3.py index 883adba5..5c4bcffc 100644 --- a/appimagebuilder/modules/setup/apprun_3/apprun3.py +++ b/appimagebuilder/modules/setup/apprun_3/apprun3.py @@ -101,6 +101,10 @@ def _deploy_libapprun_hooks_so(self, arch): libapprun_so_path = self.context.binaries_resolver.resolve_hooks_library(arch) + libapprun_so_target_path = self.get_hooks_library_target_path(arch) + shutil.copy(libapprun_so_path, libapprun_so_target_path) + + def get_hooks_library_target_path(self, arch): libapprun_so_target_dir = self._find_libapprun_hooks_so_target_dir(arch) # provide a target dir if none was found @@ -110,7 +114,7 @@ def _deploy_libapprun_hooks_so(self, arch): # copy the libapprun_hooks.so to the target dir libapprun_so_target_path = libapprun_so_target_dir / "libapprun_hooks.so" - shutil.copy(libapprun_so_path, libapprun_so_target_path) + return libapprun_so_target_path def _find_libapprun_hooks_so_target_dir(self, arch): """Finds a suitable directory for the libapprun_hooks.so""" @@ -146,7 +150,7 @@ def _deploy_apprun_config(self): path_env = self._find_dirs_containing_executable_files() self.context.runtime_env["PATH"] = ":".join(path_env) + ":$PATH" - self.context.runtime_env["LD_PRELOAD"] = "libapprun_hooks.so:$LD_PRELOAD" + self.context.runtime_env["LD_PRELOAD"] = str(self.get_hooks_library_target_path(self.context.main_arch))+":$LD_PRELOAD" self._set_user_defined_env_vars() From e495528de01809c848d523b9478d393ced034e8e Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sun, 2 Mar 2025 14:11:52 +0100 Subject: [PATCH 9/9] fix: allow filling debug option via env --- appimagebuilder/modules/setup/apprun_2/apprun2.py | 2 +- appimagebuilder/modules/setup/apprun_3/apprun3_context.py | 2 +- appimagebuilder/recipe/schema.py | 2 +- tests/recipe/test_validator.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/appimagebuilder/modules/setup/apprun_2/apprun2.py b/appimagebuilder/modules/setup/apprun_2/apprun2.py index ffcee5dc..bc7a81e6 100644 --- a/appimagebuilder/modules/setup/apprun_2/apprun2.py +++ b/appimagebuilder/modules/setup/apprun_2/apprun2.py @@ -65,7 +65,7 @@ def __init__(self, context: Context, finder: Finder): self.main_exec = recipe.AppDir.app_info.exec() self.main_exec_args = recipe.AppDir.app_info.exec_args() or "$@" self.apprun_version = recipe.AppDir.runtime.version() or "v2.0.0" - self.apprun_debug = recipe.AppDir.runtime.debug() + self.apprun_debug = (recipe.AppDir.runtime.debug().strip().lower() == "true") or False user_env_input = recipe.AppDir.runtime.env() or {} self.user_env = self.parse_env_input(user_env_input) self.apprun_arch = set(recipe.AppDir.runtime.arch() or []) diff --git a/appimagebuilder/modules/setup/apprun_3/apprun3_context.py b/appimagebuilder/modules/setup/apprun_3/apprun3_context.py index 55375b8e..e1844865 100644 --- a/appimagebuilder/modules/setup/apprun_3/apprun3_context.py +++ b/appimagebuilder/modules/setup/apprun_3/apprun3_context.py @@ -34,7 +34,7 @@ class AppRun3Context: def __init__(self, build_context: Context): self.build_context = build_context self.modules_dir = build_context.app_dir / "opt" - self.debug = build_context.recipe.AppDir.runtime.debug() or False + self.debug = (build_context.recipe.AppDir.runtime.debug().strip().lower() == "true") or False self.path_mappings = build_context.recipe.AppDir.runtime.path_mappings() or [] # init AppRun binaries resolver diff --git a/appimagebuilder/recipe/schema.py b/appimagebuilder/recipe/schema.py index f501d2d4..f7d5360e 100644 --- a/appimagebuilder/recipe/schema.py +++ b/appimagebuilder/recipe/schema.py @@ -36,7 +36,7 @@ def __init__(self): } self.v1_runtime = { - Optional("debug"): bool, + Optional("debug"): str, Optional("version"): str, Optional("path_mappings"): [str], Optional("arch"): [Or("gnueabihf", "x86_64", "i386", "aarch64")], diff --git a/tests/recipe/test_validator.py b/tests/recipe/test_validator.py index fe9bcee6..d040e472 100644 --- a/tests/recipe/test_validator.py +++ b/tests/recipe/test_validator.py @@ -69,7 +69,7 @@ def test_validate_appdir(self): }, "arch": ["i386", "x86_64", "aarch64", "gnueabihf"], "preserve": ["usr/bin/example"], - "debug": False, + "debug": "False", }, "files": {"include": ["/one", "/two"], "exclude": ["three", "four"]}, "test": {