summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azrechner-trimmed-v2.spec208
-rw-r--r--azrechner-trimmed.spec109
2 files changed, 279 insertions, 38 deletions
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',
+)
diff --git a/azrechner-trimmed.spec b/azrechner-trimmed.spec
index 51261b5..d42a1b1 100644
--- a/azrechner-trimmed.spec
+++ b/azrechner-trimmed.spec
@@ -1,9 +1,11 @@
# calendar_gui.spec
# -*- mode: python ; coding: utf-8 -*-
import os
+import sys
+from pathlib import Path
from PyInstaller.utils.hooks import collect_all
-# Collect reportlab barcode data as you already did
+# Collect reportlab barcode data (same as before)
datas = []
binaries = []
hiddenimports = []
@@ -20,46 +22,79 @@ a = Analysis(
hookspath=[],
hooksconfig={},
runtime_hooks=[],
- excludes=['scipy'], # keep your existing excludes
+ excludes=['scipy','numpy'], # keep your existing excludes
noarchive=False,
optimize=0,
)
-# ---- Helper: drop specific native libs / plugin files we listed ----
-# IMPORTANT: do NOT drop essential Qt runtime libs needed for your platform.
-ESSENTIAL_QT_NAMES = [
- 'libQt5Core.so', 'libQt5Gui.so', 'libQt5Widgets.so', 'libQt5XcbQpa.so',
- 'libpython3.12.so', 'libstdc++.so.6', 'libc.so.6'
-]
-
-DROP_PATTERNS = [
- # Pillow image codec libs you identified
- 'libavif-', 'libwebp-', 'liblcms2-',
- 'libsharpyuv', 'libwebpdemux', 'libwebpmux',
-
- # Qt optional subsystems you listed (Quick, Qml, QmlModels, Svg, WebSockets)
- 'libQt5Quick.so', 'libQt5Qml.so', 'libQt5QmlModels.so',
- 'libQt5Svg.so', 'libQt5WebSockets.so', 'libQt5EglFSDeviceIntegration.so',
-
-]
-
-def should_drop_path(path_str):
- s = str(path_str)
- # never drop critical essentials
+# 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]
+
+
+def should_drop_path(path_obj):
+ """
+ Return True when the path (string or Path) should be dropped from binaries/datas.
+ Uses case-insensitive substring matching and avoids dropping essential runtime libs.
+ """
+ s = str(path_obj)
+ s_lower = s.lower().replace('\\', '/')
+ # never drop critical essentials (match if any essential substring is contained)
for ess in ESSENTIAL_QT_NAMES:
- if ess in s:
+ if ess in s_lower:
return False
# drop if pattern matches
for p in DROP_PATTERNS:
- if p in s:
+ if p in s_lower:
return True
- # also drop QML/translations directories
- if '/qml/' in s or '\\qml\\' in s:
+ # drop QML/translations directories if present
+ if '/qml/' in s_lower or '/translations/' in s_lower:
return True
- if '/translations/' in s or '\\translations\\' in s:
+ # Also drop "pillow.libs" link targets if they match the patterns above
+ if 'pillow.libs' in s_lower and any(p in s_lower for p in DROP_PATTERNS):
return True
return False
+
# ---------- robust filtering for a.binaries / a.datas ----------
def _entry_src(obj):
"""
@@ -67,18 +102,17 @@ def _entry_src(obj):
PyInstaller entries may be (src, dest) or (src, dest, type) etc.
"""
try:
- # sequence-like entries (tuple/list)
return obj[0]
except Exception:
- # fallback: maybe it's already a path-like object
return obj
+
filtered_binaries = []
for entry in a.binaries:
src = _entry_src(entry)
if should_drop_path(src):
- # drop it
- # print("Dropping binary:", src) # uncomment for debugging
+ # drop this binary entry
+ # print("Dropping binary:", src)
continue
filtered_binaries.append(entry)
a.binaries = filtered_binaries
@@ -87,7 +121,7 @@ filtered_datas = []
for entry in a.datas:
src = _entry_src(entry)
if should_drop_path(src):
- # print("Dropping data:", src) # uncomment for debugging
+ # print("Dropping data:", src)
continue
filtered_datas.append(entry)
a.datas = filtered_datas
@@ -105,7 +139,7 @@ exe = EXE(
name='azrechner',
debug=False,
bootloader_ignore_signals=False,
- strip=True, # on Linux: strip Python bootstrap (saves some MB)
+ strip=STRIP_ON_BUILD, # only strip on unix-like systems
upx=True,
console=False,
)
@@ -115,10 +149,9 @@ coll = COLLECT(
a.binaries,
a.zipfiles,
a.datas,
- strip=True, # strip shared objects where safe
+ strip=STRIP_ON_BUILD, # only strip on unix-like systems
upx=True,
- upx_exclude=[], # leave empty; PyInstaller will avoid known-bad files
+ # if you know specific DLLs that break with UPX you can list them here
+ upx_exclude=[],
name='azrechner-trimmed',
)
-
-