Files
svfs/packaging/README.md
chakcy ae9fc8dbe4
tests / ubuntu-latest / py3.13 (push) Waiting to run
tests / windows-latest / py3.13 (push) Waiting to run
tests / ubuntu-latest / py3.13 (pull_request) Has been cancelled
tests / windows-latest / py3.13 (pull_request) Has been cancelled
chore: 三包一起分发、桌面应用打包脚本与 CI,README 对齐实现
* pyproject:wheel 打三个包(sqlite_vfs + svfs_studio + svfs_client)——两个桌面
  应用的 console script 都声明在同一个发行版里,只打核心包会让 pip 装完
  `svfs-client` 命令存在而模块缺失(跑起来是 ImportError);extras 拆成
  client(完整 PySide6,WebEngine 必需)/ studio(PySide6-Essentials 够用)/
  gui(两者合集,保留旧写法)。实测重建 wheel 三个包齐全、纯标准库层在无
  PySide6 的环境里可导入;
* packaging/:PyInstaller spec(白名单裁剪 Qt 体积,DevTools/QML/opengl32sw 的
  裁剪依据都记在注释里)与 build.py 编排;Nuitka 路线的已知问题(编译后
  Shiboken 无法导入)写在脚本注释里,推荐走 PyInstaller;
* .github/workflows/tests.yml:windows + ubuntu 双跑。文件头注明了两个尚未在真
  runner 上验证过的点——依赖索引(pyproject 默认指清华镜像,CI 里用
  UV_DEFAULT_INDEX 覆盖回 PyPI)与 Linux 上 Qt 需要的系统库;
* README:按实现重新对齐——登录与认证(凭据存储、会话失效反馈、离线宽限)、
  可靠性与诊断(客户端自更新、网络超时/重试/代理/CA、日志落盘)、plugin.json
  的 net_hosts、数据目录(credentials.dat / logs)、桥 API、安装方式与 extras。

uv.lock 同步(dev 组与 extras)。
2026-09-16 17:15:55 +08:00

135 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 打包 svfs-studio / svfs-client 为独立可执行文件
把两个 PySide6 应用打成"发给谁都能双击运行"的形态。支持两个后端:
**PyInstaller**(推荐:构建快、Qt/QtWebEngine 钩子成熟)和
**Nuitka**(真编译:启动更快、更难反编译,代价是构建慢 + 需要 C 编译器)。
## 环境准备(一次性)
```bash
uv sync --extra gui # PySide6(完整版,含 WebEngine)
uv pip install pyinstaller # PyInstaller 路线
# 或
uv pip install nuitka # Nuitka 路线(还需 C 编译器,见下文)
```
## PyInstaller(推荐)
```bash
# 目录版(推荐分发方式:启动快;整个目录一起拷贝分发)
uv run python packaging/build.py --tool pyinstaller --app both
# 只打一个
uv run python packaging/build.py --tool pyinstaller --app client
uv run python packaging/build.py --tool pyinstaller --app studio
# 单文件版(SVFS_ONEFILE=1 传给 spec;QtWebEngine 启动前要先解包,明显更慢)
uv run python packaging/build.py --tool pyinstaller --app both --onefile
```
产物:
```text
dist/SVFS-Studio/SVFS-Studio.exe
dist/SVFS-Client/SVFS-Client.exe # --onefile 时是 dist/SVFS-Client.exe
```
要点:
* 配置在 `svfs_studio.spec` / `svfs_client.spec`(数据文件、隐藏导入都在那里声明);
* QtWebEngine 的运行时(`QtWebEngineProcess.exe`、icudtl.dat、资源包、locale)
由 pyinstaller-hooks-contrib 的 PySide6 钩子自动收集,**请保持 PyInstaller ≥ 6**;
* 入口不是 `src/*/__main__.py`(有相对导入),而是本目录的
`run_studio.py` / `run_client.py`(绝对导入);
* `svfs_studio` 的图标资源随包打进 `resources/`,
`sample.repo_root()` 已适配冻结环境(PyInstaller 找 `sys._MEIPASS`,
Nuitka 找可执行文件同级目录)。
## Nuitka
```bash
# 先看看会执行什么命令
uv run python packaging/build.py --tool nuitka --app client --dry-run
# 正式构建(首次缺 C 编译器时会自动下载 MinGW64;也可自备 MSVC)
uv run python packaging/build.py --tool nuitka --app both
```
产物在 `<输出目录>/run_<app>.dist/` 下(默认输出目录 `build/nuitka`;
**路径含非 ASCII 字符时自动退到系统临时目录**——Windows 的 MSVC 链接器
在中文路径下会 LNK1104)。
要点:
* `--enable-plugin=pyside6` 负责 Qt 全家桶(含 WebEngine 子进程与资源);
* standalone 模式 + `--windows-console-mode=disable`(GUI 程序不弹控制台);
* 构建慢(整包编译,几分钟到十几分钟),但产物启动比 PyInstaller 快、
没有解包临时目录的问题。
**⚠ 已知问题(2026-09 实测)**:Nuitka 4.2.1 + PySide6 6.11.2 的产物运行时报
`Unable to import Shiboken`(编译后的 `shiboken6.Shiboken` 无法导入)。
用最小 PySide6 程序即可复现,属工具链兼容问题,与本仓库代码无关;
`--include-package=shiboken6`、`--nofollow-import-to` + 数据目录两种绕法均无效。
解决途径:等 Nuitka 发版修复,或临时降级 PySide6(`uv pip install "PySide6<6.10"`)
后再构建。**PyInstaller 路线不受影响,当前请优先使用。**
## 体积裁剪
两个 spec 在 Analysis 之后按**白名单**过滤 `a.binaries` / `a.datas`
(PySide6 的钩子会把 wheel 里所有 Qt 模块的 DLL/pyd 整包搬进来,黑名单删不干净)。
实测效果(PyInstaller 6.22 + PySide6 6.11,Windows x64):
| 应用 | 原始 | 目录版(白名单) | 单文件版(LZMA) |
|---|---|---|---|
| SVFS-Studio | 116 MB | **66 MB** | **29 MB** |
| SVFS-Client | 559 MB | **300 MB** | **126 MB** |
Client 的白名单来自 objdump 实测的导入表(不是猜的):
* `Qt6WebEngineCore` 链 Core/Gui/Network/Positioning/Qml/Quick/WebChannel,
`WebEngineWidgets` 另需 PrintSupport/QuickWidgets/Widgets,`Qt6Quick` 另需
Qt6OpenGL 与 QmlMeta/QmlModels —— 这些 DLL 必须留;
* pyd 只留 Python 层实测 import 链用到的 8 个绑定(QtCore/QtGui/QtWidgets/
QtNetwork/QtPrintSupport/QtWebChannel/QtWebEngineCore/QtWebEngineWidgets),
注意 pyd 的导入链比 DLL 的依赖多两个(QtNetwork/QtPrintSupport);
* 删掉的:Quick3D 全家、QuickControls2 样式、Pdf、ShaderTools、Svg、
opengl32sw.dll(20M,Chromium 自带 SwiftShader 兜底)、DevTools 资源包(85M)、
QML 组件目录(29M)、Qt 译文、虚拟键盘、qmltooling。
裁不动的:`Qt6WebEngineCore.dll` ~195M(Chromium 本体)+ `icudtl.dat` 10M。
已知取舍:
* DevTools 资源包删了 → 插件页面里 F12 检查元素无资源,调试请用开发环境;
* opengl32sw 删了 → 极端无 GPU 环境若渲染异常,把它加回 spec 的保留例外重打;
* Qt 译文删了 → Qt 内建对话框退回英文,界面自有文案不受影响。
**UPX 实测无效**:对 Qt DLL 用 `--best` 只把 Client 目录版从 300M 压到 290M
(pyd 全部压缩失败),收益 3%,还要承担杀软误报——不推荐。
想要更小就用单文件版(上表第三列),代价是每次启动先解包到临时目录
(Client 单文件启动明显慢,QtWebEngine 尤甚)。
## 冒烟验证
```bash
# client:--help 在进入 GUI 之前返回(退出码 0 即装配成功)
./dist/SVFS-Client/SVFS-Client.exe --help
# client:真实加载一个插件页面(--open 启动后直开指定插件;
# 页面加载后 web/ 下会出现 Chromium 的 Local Storage/GPUCache 等痕迹)
./dist/SVFS-Client/SVFS-Client.exe --data <已装插件的数据目录> --open <插件id>
# studio:offscreen 截图模式,跑完自动退出并在目录里留下每页一张 PNG
./dist/SVFS-Studio/SVFS-Studio.exe --shots ./smoke-shots
```
## 常见问题
* **杀软误报**:PyInstaller 单文件版常见,目录版较少;Nuitka 编译产物基本不误报。
* **体积**:QtWebEngine 应用的目录版约 150~250 MB,属正常(Chromium 运行时)。
* **数据目录**:两个应用的用户数据都写在
`%LOCALAPPDATA%/<应用名>`(client)与 QSettings 里(studio 的窗口几何),
与安装位置无关,覆盖更新不需要迁移。
* **离线构建**:`--assume-yes-for-downloads` 会在缺编译器时下载 MinGW64;
全离线环境请预装 MSVC Build Tools 或自行提供 MinGW64。