# -*- mode: python ; coding: utf-8 -*- import os import subprocess import sys from pathlib import Path from datetime import datetime spec_dir = Path(SPECPATH) def git_build_ref(): try: dirty = subprocess.check_output( ['git', 'status', '--porcelain', '--untracked-files=normal'], cwd=spec_dir, stderr=subprocess.DEVNULL, text=True, ) if dirty.strip(): return datetime.now().strftime('%H%M%S-%d%m%Y') except (OSError, subprocess.CalledProcessError): return datetime.now().strftime('%H%M%S-%d%m%Y') try: tag = subprocess.check_output( ['git', 'describe', '--tags', '--exact-match', 'HEAD'], cwd=spec_dir, stderr=subprocess.DEVNULL, text=True, ).strip() if tag: return tag except (OSError, subprocess.CalledProcessError): pass try: commit = subprocess.check_output( ['git', 'rev-parse', '--short', 'HEAD'], cwd=spec_dir, stderr=subprocess.DEVNULL, text=True, ).strip() if commit: return commit except (OSError, subprocess.CalledProcessError): pass return 'untagged' def _build_library_roots(): windows_root = Path(os.environ.get('SystemRoot', r'C:\Windows')).resolve() return tuple(dict.fromkeys( path.resolve() for path in ( Path(sys.executable).parent, Path(sys.prefix), Path(sys.base_prefix), Path(sys.base_prefix) / 'DLLs', windows_root / 'System32', windows_root, ) if path.is_dir() )) def _set_isolated_build_path(roots): # Analysis searches PATH for DLLs. Unrelated tools may provide incompatible # copies of the Windows runtime or ICU libraries there. os.environ['PATH'] = os.pathsep.join(str(root) for root in roots) def _check_binary_sources(binaries, roots): foreign = [ (name, source) for name, source, kind in binaries if kind == 'BINARY' and not any(Path(source).resolve().is_relative_to(root) for root in roots) ] if foreign: details = '\n'.join(f'{name}: {source}' for name, source in foreign[:10]) raise RuntimeError(f'Build contains DLLs outside Python and Windows:\n{details}') app_name = f'LEDMapper-{git_build_ref()}' library_roots = _build_library_roots() _set_isolated_build_path(library_roots) build_version_path = spec_dir.parent / 'build' / 'ledmapper-build-version.txt' build_version_path.parent.mkdir(parents=True, exist_ok=True) build_version_path.write_text(app_name.removeprefix('LEDMapper-'), encoding='utf-8') a = Analysis( [str(spec_dir / 'main.py')], pathex=[str(spec_dir), str(spec_dir.parent)], binaries=[], datas=[(str(spec_dir / 'assets'), 'assets'), (str(build_version_path), '.')], hiddenimports=[ 'canvas', 'background_image_loader', 'background_dialog', 'icons', 'localization', 'main_window', 'mapping_renderer', 'models', 'panel_mapping_generator', 'settings', 'settings_dialog', 'theme', 'unfolding', 'update_service', 'version', ], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], noarchive=False, optimize=0, ) _check_binary_sources(a.binaries, library_roots) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name=app_name, debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=False, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon=str(spec_dir / 'assets' / 'ledmapper.ico'), )