From bdc6b5a45087582cf255598e7e423937cb95253f Mon Sep 17 00:00:00 2001 From: noah-peeters Date: Fri, 23 Oct 2026 14:42:06 +0200 Subject: [PATCH 1/4] fix(linux): use the system GLib/GTK stack instead of bundling the build host's The AppImage bundled GLib, GTK3, X11 and libstdc++ from the ubuntu-24.04 build runner. On newer distros the user's own GVfs/GTK/Mesa plugins are loaded into the app and need newer versions, so on Ubuntu 26.04 the first Ctrl press loaded GVfs against the old GLib and segfaulted (undefined symbol g_variant_builder_init_static). Drop those host libs from the PyInstaller bundle on Linux and fail the CI build if they come back. Wheel-vendored libs (hashed names) stay bundled. Fixes #179 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 5 +++++ chimpstackr.spec | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad91e01..5c0f03c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -180,6 +180,11 @@ jobs: ls -la dist/chimpstackr/ echo "=== Testing CLI ===" dist/chimpstackr/chimpstackr-cli --help || true + # Host GLib/GTK/libstdc++ must come from the user's system (issue #179) + if ls dist/chimpstackr/_internal | grep -E '^lib(glib-2\.0|gio-2\.0|gtk-3|stdc\+\+)\.so'; then + echo "::error::Build host system libs were bundled, see chimpstackr.spec" + exit 1 + fi - name: Create AppImage run: | diff --git a/chimpstackr.spec b/chimpstackr.spec index 3f99d94..e365d25 100644 --- a/chimpstackr.spec +++ b/chimpstackr.spec @@ -113,6 +113,41 @@ a_cli = Analysis( noarchive=False, ) +# ── Linux: use the user's desktop stack, don't bundle the build host's ── +# PyInstaller picks these up from the build machine (ubuntu-24.04). At runtime the +# user's own GIO/GVfs modules, GTK input methods, pixbuf loaders and Mesa drivers +# get loaded into the app and need the user's matching versions of these libs. +# Bundling older copies shadows them and crashes newer distros (issue #179: +# Ubuntu 26.04 GVfs needs g_variant_builder_init_static from GLib 2.84+). +# Wheel-vendored libs have hashed names (e.g. libxcb-ad31f5a3.so) and are kept. +if sys.platform.startswith('linux'): + import re + _system_libs = re.compile(r'^lib(' + '|'.join([ + # GLib / GTK3 stack (loaded by Qt's gtk3 platform theme) + 'glib-2.0', 'gio-2.0', 'gobject-2.0', 'gmodule-2.0', 'gthread-2.0', + 'gtk-3', 'gdk-3', 'gdk_pixbuf-2.0', 'atk-1.0', 'atk-bridge-2.0', 'atspi', + 'pango-1.0', 'pangocairo-1.0', 'pangoft2-1.0', 'cairo', 'cairo-gobject', + 'harfbuzz', 'fribidi', 'thai', 'datrie', 'graphite2', 'pixman-1', 'epoxy', + 'fontconfig', 'freetype', 'png16', 'brotlicommon', 'brotlidec', 'expat', + 'ffi', 'pcre2-8', 'mount', 'blkid', 'selinux', 'z', 'bz2', 'zstd', + # X11 / xcb (shared with the system's Mesa libGL) + 'X11', 'X11-xcb', 'Xau', 'Xdmcp', 'Xext', 'Xfixes', 'Xi', 'Xinerama', + 'Xrandr', 'Xrender', 'Xcursor', 'Xcomposite', 'Xdamage', 'xkbcommon', + 'xcb-glx', 'xcb-randr', 'xcb-render', 'xcb-shm', 'xcb-sync', 'xcb-xfixes', + # D-Bus, systemd, crypto and Kerberos (deps of the system's GVfs modules) + 'dbus-1', 'systemd', 'gcrypt', 'gpg-error', 'bsd', 'md', 'ssl', 'crypto', + 'gssapi_krb5', 'krb5', 'krb5support', 'k5crypto', 'com_err', 'keyutils', + 'cap', 'lz4', 'lzma', 'uuid', + # Toolchain runtime (Mesa's LLVM needs the system's newer libstdc++) + 'stdc\\+\\+', 'gcc_s', + ]) + r')\.so(\.|$)') + + def _drop_system_libs(toc): + return [e for e in toc if not _system_libs.match(os.path.basename(e[0]))] + + a.binaries = _drop_system_libs(a.binaries) + a_cli.binaries = _drop_system_libs(a_cli.binaries) + # Merge analyses so shared libs are only collected once MERGE( (a, 'chimpstackr', 'chimpstackr'), From 7ba1410e761a4e5a22a8c2e2ed03d2d9aa595b35 Mon Sep 17 00:00:00 2001 From: noah-peeters Date: Fri, 23 Oct 2026 15:04:11 +0200 Subject: [PATCH 2/4] fix(linux): keep libgthread bundled, openSUSE doesn't install it by default Co-Authored-By: Claude Opus 5.5 --- chimpstackr.spec | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chimpstackr.spec b/chimpstackr.spec index e365d25..8c0d146 100644 --- a/chimpstackr.spec +++ b/chimpstackr.spec @@ -123,8 +123,9 @@ a_cli = Analysis( if sys.platform.startswith('linux'): import re _system_libs = re.compile(r'^lib(' + '|'.join([ - # GLib / GTK3 stack (loaded by Qt's gtk3 platform theme) - 'glib-2.0', 'gio-2.0', 'gobject-2.0', 'gmodule-2.0', 'gthread-2.0', + # GLib / GTK3 stack (loaded by Qt's gtk3 platform theme). libgthread + # stays bundled: Qt needs it, it's an empty stub, and openSUSE omits it. + 'glib-2.0', 'gio-2.0', 'gobject-2.0', 'gmodule-2.0', 'gtk-3', 'gdk-3', 'gdk_pixbuf-2.0', 'atk-1.0', 'atk-bridge-2.0', 'atspi', 'pango-1.0', 'pangocairo-1.0', 'pangoft2-1.0', 'cairo', 'cairo-gobject', 'harfbuzz', 'fribidi', 'thai', 'datrie', 'graphite2', 'pixman-1', 'epoxy', From 85ff29f28e1ee6d9d0f41b0816a999a7f842ad61 Mon Sep 17 00:00:00 2001 From: noah-peeters Date: Fri, 23 Oct 2026 15:36:10 +0200 Subject: [PATCH 3/4] fix(linux): keep libbz2 bundled, Fedora only ships libbz2.so.1 Co-Authored-By: Claude Opus 5.5 --- chimpstackr.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chimpstackr.spec b/chimpstackr.spec index 8c0d146..5f63b8e 100644 --- a/chimpstackr.spec +++ b/chimpstackr.spec @@ -125,12 +125,13 @@ if sys.platform.startswith('linux'): _system_libs = re.compile(r'^lib(' + '|'.join([ # GLib / GTK3 stack (loaded by Qt's gtk3 platform theme). libgthread # stays bundled: Qt needs it, it's an empty stub, and openSUSE omits it. + # libbz2 too: Fedora names it libbz2.so.1, Python's _bz2 wants .so.1.0. 'glib-2.0', 'gio-2.0', 'gobject-2.0', 'gmodule-2.0', 'gtk-3', 'gdk-3', 'gdk_pixbuf-2.0', 'atk-1.0', 'atk-bridge-2.0', 'atspi', 'pango-1.0', 'pangocairo-1.0', 'pangoft2-1.0', 'cairo', 'cairo-gobject', 'harfbuzz', 'fribidi', 'thai', 'datrie', 'graphite2', 'pixman-1', 'epoxy', 'fontconfig', 'freetype', 'png16', 'brotlicommon', 'brotlidec', 'expat', - 'ffi', 'pcre2-8', 'mount', 'blkid', 'selinux', 'z', 'bz2', 'zstd', + 'ffi', 'pcre2-8', 'mount', 'blkid', 'selinux', 'z', 'zstd', # X11 / xcb (shared with the system's Mesa libGL) 'X11', 'X11-xcb', 'Xau', 'Xdmcp', 'Xext', 'Xfixes', 'Xi', 'Xinerama', 'Xrandr', 'Xrender', 'Xcursor', 'Xcomposite', 'Xdamage', 'xkbcommon', From e370422f6f16936063f8206a4fa14e1359b2735d Mon Sep 17 00:00:00 2001 From: noah-peeters Date: Fri, 23 Oct 2026 15:36:10 +0200 Subject: [PATCH 4/4] fix: call multiprocessing.freeze_support() in frozen entry points On Python 3.14 the default start method on Linux is forkserver, so numba's parallel backend lock makes multiprocessing start its resource tracker by re-running sys.executable. In a PyInstaller build that is the app itself, which opened another ChimpStackr, which did the same, endlessly (reported in #179 for a build made with scripts/build_linux.sh on Ubuntu 26.04). freeze_support() lets PyInstaller run the helper and exit instead. Co-Authored-By: Claude Opus 5.5 --- src/cli.py | 7 +++++++ src/run.py | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cli.py b/src/cli.py index d973e5c..2022694 100644 --- a/src/cli.py +++ b/src/cli.py @@ -7,6 +7,13 @@ python -m src.cli --input img1.jpg img2.jpg img3.jpg --output stacked.png --align """ import os +import multiprocessing + +# Frozen builds: let multiprocessing helpers that re-run this executable exit +# instead of running the CLI (see src/run.py) +if __name__ == "__main__": + multiprocessing.freeze_support() + os.environ.setdefault('OPENCV_IO_ENABLE_OPENEXR', '1') import argparse diff --git a/src/run.py b/src/run.py index f6e8c02..07aa00a 100644 --- a/src/run.py +++ b/src/run.py @@ -1,4 +1,10 @@ -import os, sys, tempfile, logging +import os, sys, tempfile, logging, multiprocessing + +# Frozen builds: multiprocessing helpers (e.g. the resource tracker Python 3.14 +# starts for numba's parallel lock) re-run this executable. Let them do their +# job and exit instead of opening another copy of the app (issue #179). +if __name__ == "__main__": + multiprocessing.freeze_support() # Enable OpenEXR support in OpenCV (must be set before cv2 import) os.environ.setdefault('OPENCV_IO_ENABLE_OPENEXR', '1')