diff --git a/.gitignore b/.gitignore index 18a163e..c6ef017 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ dist/ # PyInstaller output /packaging/build/ /packaging/dist/ +# Generated at build time from cim_suite.__version__ (see packaging/suite.spec) +/packaging/_version_info.generated.txt # Packaged portable-build zips — transient test artifacts, regenerated per build /packaging/CIM-Service-Suite_*.zip diff --git a/packaging/suite.spec b/packaging/suite.spec index 3174492..fc48c85 100644 --- a/packaging/suite.spec +++ b/packaging/suite.spec @@ -9,6 +9,7 @@ # Setup (ISCC.exe). import os +import sys block_cipher = None @@ -16,10 +17,21 @@ block_cipher = None # its parent and must be on pathex so `import cim_suite` resolves at build time. _repo_root = os.path.dirname(SPECPATH) +# Make `import cim_suite` and the local `verinfo` helper importable while the spec runs. +for _p in (_repo_root, SPECPATH): + if _p not in sys.path: + sys.path.insert(0, _p) + +import cim_suite # noqa: E402 (single-sourced version) +import verinfo # noqa: E402 (packaging/verinfo.py) + # Bundled brand fonts (Lato) live in the theme package and must be copied into the # frozen app at the same package-relative path so theme.fonts can find them. _font_src = os.path.join(_repo_root, "cim_suite", "core", "ui", "theme", "fonts") +# The runtime window icon (cim_suite/shell/branding.py resolves it package-relative). +_app_icon_src = os.path.join(_repo_root, "cim_suite", "shell", "resources") + # IOModbus ships its device catalog (register maps) as a package resource; it must be # copied into the frozen app at the same package-relative path so config.catalog_text # (importlib.resources) can read it. @@ -30,15 +42,26 @@ _iomodbus_res = os.path.join(_repo_root, "cim_suite", "modules", "iomodbus", "re _pyproject = os.path.join(_repo_root, "pyproject.toml") _changelog = os.path.join(_repo_root, "CHANGELOG.md") +# End-user run guide shipped at the bundle root (and inside the portable zip). +_readme = os.path.join(SPECPATH, "READ-ME-FIRST.txt") + +# Exe icon + Windows version resource, both generated from single sources. +_icon = os.path.join(SPECPATH, "icon.ico") +_version_file = os.path.join(SPECPATH, "_version_info.generated.txt") +with open(_version_file, "w", encoding="utf-8") as _vf: + _vf.write(verinfo.render_version_resource(cim_suite.__version__)) + a = Analysis( [os.path.join(SPECPATH, "suite_launcher.py")], pathex=[_repo_root], binaries=[], datas=[ (_font_src, os.path.join("cim_suite", "core", "ui", "theme", "fonts")), + (_app_icon_src, os.path.join("cim_suite", "shell", "resources")), (_iomodbus_res, os.path.join("cim_suite", "modules", "iomodbus", "resources")), (_pyproject, "."), (_changelog, "."), + (_readme, "."), ], hiddenimports=["serial.tools.list_ports", "openpyxl", "PySide6.QtCharts"], hookspath=[], @@ -71,7 +94,8 @@ exe = EXE( upx=False, console=False, disable_windowed_traceback=False, - icon=None, + icon=_icon, + version=_version_file, ) coll = COLLECT( exe,