Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
37 changes: 37 additions & 0 deletions chimpstackr.spec
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,43 @@ 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). 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', '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'),
Expand Down
7 changes: 7 additions & 0 deletions src/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/run.py
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
Loading