122 lines
3.7 KiB
RPMSpec
122 lines
3.7 KiB
RPMSpec
# -*- mode: python ; coding: utf-8 -*-
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|
from PyInstaller.utils.win32.versioninfo import (
|
|
FixedFileInfo,
|
|
StringFileInfo,
|
|
StringStruct,
|
|
StringTable,
|
|
VarFileInfo,
|
|
VarStruct,
|
|
VSVersionInfo,
|
|
)
|
|
|
|
|
|
project_root = Path(SPECPATH)
|
|
app_version = os.environ.get("WECOM_BUILD_VERSION", "1.0.0").strip() or "1.0.0"
|
|
app_name = f"ZhenAI-WeCom-Assistant-v{app_version}"
|
|
|
|
# 完整的版本信息资源。没有它,EXE 在杀软启发式评分和 SmartScreen 提示里
|
|
# 就是一个"来历不明"的匿名程序;有了它至少提示里能显示发布者与产品名。
|
|
_version_parts = [int(p) for p in (app_version.split(".") + ["0", "0", "0"])[:4]]
|
|
version_resource = VSVersionInfo(
|
|
ffi=FixedFileInfo(
|
|
filevers=tuple(_version_parts),
|
|
prodvers=tuple(_version_parts),
|
|
),
|
|
kids=[
|
|
StringFileInfo(
|
|
[
|
|
StringTable(
|
|
"080404b0",
|
|
[
|
|
StringStruct("CompanyName", "甄养堂"),
|
|
StringStruct("FileDescription", "甄AI客服 · 企业微信客服助手"),
|
|
StringStruct("FileVersion", app_version),
|
|
StringStruct("InternalName", app_name),
|
|
StringStruct("LegalCopyright", "Copyright (C) 2026 甄养堂"),
|
|
StringStruct("OriginalFilename", f"{app_name}.exe"),
|
|
StringStruct("ProductName", "甄AI客服"),
|
|
StringStruct("ProductVersion", app_version),
|
|
],
|
|
)
|
|
]
|
|
),
|
|
VarFileInfo([VarStruct("Translation", [2052, 1200])]),
|
|
],
|
|
)
|
|
|
|
hidden_imports = [
|
|
"app_version",
|
|
"backend_client",
|
|
"conversation_store",
|
|
"dsh_agent",
|
|
"gui_runtime",
|
|
"mcp_bridge",
|
|
"registration_store",
|
|
"runtime_paths",
|
|
"wechat_gui",
|
|
"wechat_gui_qt",
|
|
"PySide6.QtWebEngineCore",
|
|
"PySide6.QtWebEngineWidgets",
|
|
]
|
|
hidden_imports += collect_submodules("mcp")
|
|
|
|
a = Analysis(
|
|
[str(project_root / "app_main.py")],
|
|
pathex=[str(project_root)],
|
|
binaries=[],
|
|
datas=[
|
|
(str(project_root / "edge_light_theme"), "edge_light_theme"),
|
|
(str(project_root / "assets" / "brand"), "assets/brand"),
|
|
(str(project_root / "dsh_desk.cordis.yml"), "."),
|
|
]
|
|
# 昵称 OCR 的模型与配置:只收代码不收这些文件的话,打包后 RapidOCR
|
|
# 初始化直接失败,会话身份静默退化。
|
|
+ collect_data_files("rapidocr_onnxruntime"),
|
|
hiddenimports=hidden_imports,
|
|
hookspath=[],
|
|
hooksconfig={},
|
|
runtime_hooks=[],
|
|
excludes=["PyQt5", "PyQt6", "PySide2"],
|
|
noarchive=False,
|
|
# 去掉 assert 与 __debug__ 分支;不动 docstring(有库在运行期读它)。
|
|
optimize=1,
|
|
)
|
|
pyz = PYZ(a.pure)
|
|
|
|
# 目录形态(onedir):单文件 EXE 每次冷启动都要把几百 MB 的 Qt WebEngine
|
|
# 解压进临时目录再被杀毒软件逐个扫一遍,低配硬盘上一等就是几十秒。
|
|
# 目录形态零解压,冷启动只剩正常的模块加载。
|
|
exe = EXE(
|
|
pyz,
|
|
a.scripts,
|
|
[],
|
|
exclude_binaries=True,
|
|
name=app_name,
|
|
debug=False,
|
|
bootloader_ignore_signals=False,
|
|
strip=False,
|
|
upx=False,
|
|
console=False,
|
|
disable_windowed_traceback=False,
|
|
argv_emulation=False,
|
|
target_arch=None,
|
|
codesign_identity=None,
|
|
entitlements_file=None,
|
|
icon=str(project_root / "assets" / "brand" / "zhenyangtang-icon.ico"),
|
|
version=version_resource,
|
|
)
|
|
|
|
coll = COLLECT(
|
|
exe,
|
|
a.binaries,
|
|
a.datas,
|
|
strip=False,
|
|
upx=False,
|
|
name=app_name,
|
|
)
|