# -*- mode: python ; coding: utf-8 -*- """PyInstaller 打包配置。 由 ``packaging/build_pyinstaller.py`` 调用 —— 选项通过**环境变量**传进来 (spec 文件本身不接受命令行参数,这是 PyInstaller 的既定用法): =========================== ========================================== 环境变量 作用 =========================== ========================================== ``CLG_NAME`` 产物名(默认 chongming-license-gui) ``CLG_ONEFILE`` ``1`` = 单文件(默认),``0`` = 目录模式 ``CLG_CONSOLE`` ``1`` = 带控制台窗口(排查启动问题用) ``CLG_ICON`` .ico 路径(由 ``_common.build_app_icon()`` 生成) =========================== ========================================== """ import os from pathlib import Path ROOT = Path(SPECPATH).resolve().parent # noqa: F821 - SPECPATH 由 PyInstaller 注入 NAME = os.environ.get("CLG_NAME", "chongming-license-gui") ONEFILE = os.environ.get("CLG_ONEFILE", "1") == "1" CONSOLE = os.environ.get("CLG_CONSOLE", "0") == "1" ICON = os.environ.get("CLG_ICON") or None # PySide6 装了整套 Addons(Qt3D / WebEngine / Charts…),本工具一个都不用。 # PyInstaller 的 PySide6 hook 只打包"被 import 到的" Qt 模块,但显式排除能兜住 # 间接依赖,避免几百 MB 的模块被拖进来。 # 注:排除不存在的模块是安全的(PyInstaller 只按名字匹配)。 EXCLUDES = [ # 3D / 图形 / 可视化 "PySide6.Qt3DAnimation", "PySide6.Qt3DCore", "PySide6.Qt3DExtras", "PySide6.Qt3DInput", "PySide6.Qt3DLogic", "PySide6.Qt3DRender", "PySide6.QtCharts", "PySide6.QtDataVisualization", "PySide6.QtGraphs", "PySide6.QtGraphsWidgets", "PySide6.QtCanvasPainter", # Web / QML / Quick(体积最大的一批) "PySide6.QtWebEngineCore", "PySide6.QtWebEngineWidgets", "PySide6.QtWebEngineQuick", "PySide6.QtWebView", "PySide6.QtWebChannel", "PySide6.QtWebSockets", "PySide6.QtHttpServer", "PySide6.QtQml", "PySide6.QtQuick", "PySide6.QtQuick3D", "PySide6.QtQuickControls2", "PySide6.QtQuickWidgets", "PySide6.QtQuickTest", # 多媒体 / 设备 / 传感器 "PySide6.QtMultimedia", "PySide6.QtMultimediaWidgets", "PySide6.QtSpatialAudio", "PySide6.QtTextToSpeech", "PySide6.QtBluetooth", "PySide6.QtNfc", "PySide6.QtPositioning", "PySide6.QtLocation", "PySide6.QtSensors", "PySide6.QtSerialPort", "PySide6.QtSerialBus", "PySide6.QtRemoteObjects", "PySide6.QtScxml", "PySide6.QtStateMachine", "PySide6.QtAxContainer", # 文档 / 设计器 / 测试 / 数据库 / 容器 "PySide6.QtPdf", "PySide6.QtPdfWidgets", "PySide6.QtDesigner", "PySide6.QtUiTools", "PySide6.QtHelp", "PySide6.QtTest", "PySide6.QtSql", "PySide6.QtDBus", "PySide6.QtConcurrent", "PySide6.QtNetworkAuth", "PySide6.QtXml", "PySide6.QtSvgWidgets", "PySide6.QtOpenGL", "PySide6.QtOpenGLWidgets", # 与本工具无关的标准库大件 "tkinter", "pydoc_data", ] a = Analysis( # noqa: F821 [str(ROOT / "packaging" / "launcher.py")], # 用源码树解析本包,而不是依赖 editable 安装(冻结工具对 .pth 式 # editable 安装的解析并不可靠) pathex=[str(ROOT / "src")], binaries=[], datas=[], # 无数据文件:图标与样式表都是运行时生成的 # PySide6.QtSvg 是显式 import,正常能被扫到;这里再钉一遍 —— 少了它 # 图标会全部变空白,而且不报错 hiddenimports=["PySide6.QtSvg"], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=EXCLUDES, noarchive=False, optimize=0, ) pyz = PYZ(a.pure) # noqa: F821 if ONEFILE: # 单文件:自解压到临时目录再运行(分发最省事,首次启动稍慢) exe = EXE( # noqa: F821 pyz, a.scripts, a.binaries, a.datas, [], name=NAME, debug=False, bootloader_ignore_signals=False, strip=False, # UPX 压缩容易触发杀软误报,也会损坏部分 Qt DLL,明确关掉 upx=False, upx_exclude=[], runtime_tmpdir=None, console=CONSOLE, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon=ICON, ) else: # 目录模式:启动更快,适合放在固定办公机上长期使用 exe = EXE( # noqa: F821 pyz, a.scripts, [], exclude_binaries=True, name=NAME, debug=False, bootloader_ignore_signals=False, strip=False, upx=False, console=CONSOLE, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon=ICON, ) coll = COLLECT( # noqa: F821 exe, a.binaries, a.datas, strip=False, upx=False, upx_exclude=[], name=NAME, )