From 647feb7b6b0c8cc140fcc9bbe64eb6a497597de3 Mon Sep 17 00:00:00 2001 From: Kaufmann Date: Thu, 9 Oct 2025 18:53:47 +0200 Subject: - made the filtering spec file Windows compatible - added a spec file with a more extensive filtering method (not necessary, output of AI when mistakenly stating that the old filtering does not work for windows) --- azrechner-trimmed-v2.spec | 208 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 azrechner-trimmed-v2.spec (limited to 'azrechner-trimmed-v2.spec') diff --git a/azrechner-trimmed-v2.spec b/azrechner-trimmed-v2.spec new file mode 100644 index 0000000..e77e619 --- /dev/null +++ b/azrechner-trimmed-v2.spec @@ -0,0 +1,208 @@ +# calendar_gui.spec +# -*- mode: python ; coding: utf-8 -*- +import os +import sys +import collections +from pathlib import Path +from PyInstaller.utils.hooks import collect_all + +DEBUG_FILTER = False + +# Collect reportlab barcode data (same as before) +datas = [] +binaries = [] +hiddenimports = [] +tmp_ret = collect_all('reportlab.graphics.barcode') +datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] + +# ---- Analysis ---- +a = Analysis( + ['calendar_gui.py'], + pathex=[os.getcwd()], + binaries=binaries, + datas=datas, + hiddenimports=hiddenimports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=['scipy','numpy'], # keep your existing excludes + noarchive=False, + optimize=0, +) + +# Platform-aware essential names & drop patterns +IS_WINDOWS = sys.platform.startswith('win') +IS_MAC = sys.platform == 'darwin' +IS_UNIX = not IS_WINDOWS and not IS_MAC + +# Lowercase names will be used for case-insensitive matching +if IS_WINDOWS: + ESSENTIAL_QT_NAMES = [ + 'qt5core.dll', 'qt5gui.dll', 'qt5widgets.dll', + # the platform plugin folder & plugin name on Windows + os.path.join('platforms', 'qwindows.dll'), + # Python runtime dlls that are critical + f'python{sys.version_info.major}{sys.version_info.minor}.dll', # e.g. python312.dll + ] + DROP_PATTERNS = [ + # Qt optional DLLs you listed (Windows names) + 'Qt5Quick.dll', 'qt5qml.dll', 'qt5qmlmodels.dll', + 'qt5svg.dll', 'qt5websockets.dll', 'qt5eglfsdeviceintegration.dll', + # Pillow / image codec dll patterns (windows) + 'libavif', 'libwebp', 'lcms2', 'sharpyuv', 'openjp2', 'webpdemux', 'webpmux', + # qml/translations directories + ] + STRIP_ON_BUILD = False +else: + # Linux / other UNIX + ESSENTIAL_QT_NAMES = [ + 'libQt5Core.so', 'libQt5Gui.so', 'libQt5Widgets.so', 'libQt5XcbQpa.so', + 'libpython{major}.{minor}.so'.format(major=sys.version_info.major, minor=sys.version_info.minor), + 'libstdc++.so.6', 'libc.so.6' + ] + DROP_PATTERNS = [ + # Pillow image codecs & windows variants too + 'libavif-', 'libwebp-', 'liblcms2-', 'libsharpyuv', 'libopenjp2', + # Qt optional subsystems you listed + 'libQt5Quick.so', 'libQt5Qml.so', 'libQt5QmlModels.so', + 'libQt5Svg.so', 'libQt5WebSockets.so', 'libQt5EglFSDeviceIntegration.so', + ] + STRIP_ON_BUILD = True + +# Normalize patterns to lowercase for case-insensitive comparisons +ESSENTIAL_QT_NAMES = [p.lower() for p in ESSENTIAL_QT_NAMES] +DROP_PATTERNS = [p.lower() for p in DROP_PATTERNS] + +# filter block +def _iter_strings_in_entry(entry): + """ + Yield all string-like parts found inside a PyInstaller TOC entry. + Handles: + - simple strings / Paths + - tuples/lists like (src, dest) or (src, dest, type) + - objects that have .path or similar (best-effort) + """ + if entry is None: + return + # primitive string / Path + if isinstance(entry, (str, Path)): + yield str(entry) + return + # sequence-like: iterate + if isinstance(entry, collections.abc.Sequence) and not isinstance(entry, (str, bytes, bytearray)): + for el in entry: + # recurse + yield from _iter_strings_in_entry(el) + return + # fallback: try to cast to string + try: + s = str(entry) + if s: + yield s + except Exception: + return + +def _entry_has_any(entry, substring): + """Return True if any string part of the entry contains substring (case-insensitive).""" + sub = substring.lower() + for s in _iter_strings_in_entry(entry): + if sub in s.lower(): + return True + return False + +def entry_basename_set(entry): + """ + Return set of basenames (lowercased) found in the entry. + Useful to match bare filenames like 'Qt5Quick.dll' or 'libQt5Quick.so'. + """ + names = set() + for s in _iter_strings_in_entry(entry): + try: + p = Path(s) + names.add(p.name.lower()) + except Exception: + names.add(str(s).lower()) + return names + +def should_drop_entry(entry): + """ + Decide whether to drop this TOC entry. + Uses: + - ESSENTIAL_QT_NAMES (substrings) to never drop essentials + - DROP_PATTERNS (substrings) to drop matches + - qml/translations directory substrings + Matches case-insensitively against all string parts of the entry. + """ + # never drop if any essential name substring appears in entry + for ess in ESSENTIAL_QT_NAMES: + if _entry_has_any(entry, ess): + return False + + # drop if any drop-pattern matches + for p in DROP_PATTERNS: + if _entry_has_any(entry, p): + return True + + # drop QML or translations paths + for s in _iter_strings_in_entry(entry): + sl = s.lower().replace('\\', '/') + if '/qml/' in sl or '/translations/' in sl: + return True + + # Bonus: drop Pillow codec names if entry mentions 'pillow.libs' AND codec names + low_all = " ".join(s.lower() for s in _iter_strings_in_entry(entry)) + if 'pillow.libs' in low_all and any(p in low_all for p in DROP_PATTERNS): + return True + + # no reason to drop + return False + + +# Now filter a.binaries and a.datas using should_drop_entry +filtered_binaries = [] +for entry in a.binaries: + drop = should_drop_entry(entry) + if DEBUG_FILTER: + print(("DROP" if drop else "KEEP"), "binary:", list(_iter_strings_in_entry(entry))) + if not drop: + filtered_binaries.append(entry) +a.binaries = filtered_binaries + +filtered_datas = [] +for entry in a.datas: + drop = should_drop_entry(entry) + if DEBUG_FILTER: + print(("DROP" if drop else "KEEP"), "data:", list(_iter_strings_in_entry(entry))) + if not drop: + filtered_datas.append(entry) +a.datas = filtered_datas +# ---------------------------------------------------------------- + + +# ---- Build steps: PYZ, EXE, COLLECT ---- +pyz = PYZ(a.pure, a.zipped_data, cipher=None) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='azrechner', + debug=False, + bootloader_ignore_signals=False, + strip=STRIP_ON_BUILD, # only strip on unix-like systems + upx=True, + console=False, +) + +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=STRIP_ON_BUILD, # only strip on unix-like systems + upx=True, + # if you know specific DLLs that break with UPX you can list them here + upx_exclude=[], + name='azrechner-trimmed-v2', +) -- cgit v1.1