Files
sufw-taetigkeitsnachweise/1_build-pipeline/build.spec
T
2026-06-30 21:28:28 +02:00

160 lines
4.7 KiB
RPMSpec

# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller Spec-Datei für den Tätigkeitsnachweis-Generator.
Baut eine standalone .exe aus run.py, die Streamlit, openpyxl und
PyMuPDF (fitz) enthält.
Verwendung:
pyinstaller build.spec
"""
import os
import sys
from pathlib import Path
# Pfade relativ zum Speicherort dieser Spec-Datei
# PyInstaller stellt SPECPATH bereit (__file__ existiert in exec()-Kontext nicht)
_SPEC_DIR = Path(SPECPATH).resolve()
_PROJECT_ROOT = _SPEC_DIR.parent
_APP_DIR = _PROJECT_ROOT / "0_application"
# ── Streamlit & PyMuPDF Hidden Imports ──────────────────────────
# Streamlit hat viele dynamische Imports; collect_all holt alles.
# PyMuPDF (fitz) braucht die native DLL.
hiddenimports = [
# Streamlit-Komponenten
"streamlit",
"streamlit.web.bootstrap",
"streamlit.runtime",
"streamlit.runtime.scriptrunner",
"streamlit.runtime.state",
"streamlit.runtime.caching",
"streamlit.watcher",
"streamlit.watcher.local_sources_watcher",
"streamlit.watcher.poll_path_watcher",
"streamlit.watcher.event_based_path_watcher",
"streamlit.elements",
"streamlit.elements.arrow",
"streamlit.elements.pyplot",
"streamlit.elements.altair",
"streamlit.elements.plotly",
"streamlit.proto",
# openpyxl
"openpyxl",
"openpyxl.cell",
"openpyxl.styles",
# PyMuPDF
"fitz",
# Standard-Libs die manchmal übersehen werden
"tornado",
"watchdog",
"pandas",
"numpy",
"PIL",
"altair",
"pyarrow",
"git",
"blinker",
"rich",
"pydeck",
]
# ── Binaries (PyMuPDF DLL) ──────────────────────────────────────
binaries = []
# Finde die PyMuPDF-DLL
try:
import fitz
fitz_dir = Path(fitz.__file__).parent
for f in fitz_dir.iterdir():
if f.suffix.lower() in (".dll", ".pyd", ".so", ".dylib"):
binaries.append((str(f), "fitz"))
except ImportError:
pass
# ── Data Files ──────────────────────────────────────────────────
datas = [
(str(_APP_DIR / "app.py"), "."),
(str(_APP_DIR / "src"), "src"),
(str(_APP_DIR / "config"), "config"),
]
# ── Alle Streamlit-Abhängigkeiten & Metadaten VOR Analysis() sammeln ─
from PyInstaller.utils.hooks import collect_all, copy_metadata
streamlit_datas, streamlit_binaries, streamlit_hidden = collect_all("streamlit")
datas.extend(streamlit_datas)
binaries.extend(streamlit_binaries)
hiddenimports.extend(streamlit_hidden)
# Package-Metadaten: streamlit & Co. nutzen importlib.metadata.version(),
# das die METADATA-Dateien aus dem dist-info-Ordner benötigt.
for pkg in ["streamlit", "pyarrow", "numpy", "pandas", "altair", "openpyxl"]:
try:
datas += copy_metadata(pkg)
except Exception:
pass
# Deduplizieren (Datas und Hiddenimports)
hiddenimports = list(set(hiddenimports))
seen = set()
_unique_datas = []
for src, dst in datas:
key = (src, dst)
if key not in seen:
seen.add(key)
_unique_datas.append((src, dst))
datas = _unique_datas
# ── Analyse ─────────────────────────────────────────────────────
a = Analysis(
[str(_SPEC_DIR / "run.py")],
pathex=[str(_APP_DIR)],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
"tkinter",
"matplotlib",
"scipy",
"IPython",
"jupyter",
"notebook",
"sqlalchemy",
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False,
)
# ── PyZ ─────────────────────────────────────────────────────────
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
# ── EXE ─────────────────────────────────────────────────────────
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name="Tätigkeitsnachweis-Generator",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True, # True = Konsolenfenster sichtbar (zeigt Streamlit-Logs)
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)