Gramps Windows AIO 安装包构建指南:从 MSYS2 环境搭建到 VSCode 调试工作流
Gramps Windows AIO 安装包构建指南:从 MSYS2 环境搭建到 VSCode 调试工作流
导读
本文以 Gramps 项目 aio 目录(即 Windows All-In-One 安装包的构建体系)为线索,完整讲解如何在 Windows 上从零搭建 MSYS2 UCRT64 构建环境、一键产出 GrampsAIO-*_win64.exe 自包含安装包,并利用构建过程中生成的 Python 虚拟环境在 Visual Studio Code 中搭建 Gramps 源码开发与调试工作流(mypy 静态检查、black 格式化、单元测试)。全文以 aio/README.md 为核心操作骨架,并补充 aio/build.sh、aio/setup.py、aio/grampsaio64.nsi.template 等源码级细节,帮助读者理解每一步背后的真实实现。
一、AIO 目录是什么:Windows 一体化安装包的构建体系
aio 目录(All In One)是 Gramps 在 Windows 平台上的发布构建产物生成地,其使命是用一套脚本把 Gramps 源码、Python 运行时、GTK 图形栈、拼写词典、翻译语言包等全部打包成一个可供终端用户双击安装的 exe 安装器。目录内文件分工如下:
| 文件 | 作用 |
|---|---|
| aio/README.md | 构建步骤与 VSCode 开发提示的权威说明 |
| aio/build.sh | 一键构建主脚本:装依赖、建 venv、构建 wheel、冻结可执行文件、调用 NSIS 打包 |
| aio/setup.py | cx_Freeze 冻结配置:声明需要打包的 Python 包、DLL、数据目录与四个可执行入口 |
| aio/grampsaio64.nsi.template | NSIS 安装脚本模板,构建时把版本号/构建号替换进去,生成真正的 grampsaio64.nsi |
| aio/grampsaioc.py、aio/grampsaiow.py、aio/grampsaiocd.py | 三个 Gramps 启动入口(控制台版 / 图形版 / 守护进程版) |
| aio/gramps.ico、grampsc.ico、grampsd.ico | 安装器与各可执行文件的图标资源 |
| aio/setup.py | 上述文件的打包清单 |
其中三个启动器在冻结后的职责由 aio/setup.py 中的 EXECUTABLES 定义:grampsaioc.py 冻结为 gramps.exe(Console 底座,对应控制台输出)、grampsaiow.py 冻结为 grampsw.exe(gui 底座,图形界面主程序)、grampsaiocd.py 冻结为 grampsd.exe(Console 底座,守护进程)。这些启动器在运行时设置 GRAMPS_RESOURCES、GI_TYPELIB_PATH、SSL_CERT_FILE 等环境变量指向安装目录内的资源(见 aio/grampsaioc.py),并用 Windows 互斥量(Mutex)防止多实例同时运行。
二、构建 master 分支的 AIO 安装包:逐步操作
1. 安装并升级 MSYS2
- 从 MSYS2 官网下载安装包,按默认选项完成安装。
- 从开始菜单启动 MSYS2 UCRT64(注意是 UCRT64 环境,这是 Gramps AIO 依赖
mingw-w64-ucrt-x86_64-*系列包的前提)。 - 升级系统软件包:
pacman -Syuu --noconfirm
如果第一次运行该命令导致 MSYS2 自动关闭,需要再执行一次,以完成滚动升级的收尾工作。
2. 安装 Git
若环境尚未安装 Git,执行:
pacman -S git --noconfirm
3. 获取 Gramps 源码
git clone https://github.com/gramps-project/gramps.git
说明:GitHub 地址仅为原 README 中记录的标准克隆方式;在镜像环境中可用等价方式同步源码后进入对应目录继续构建。
4. 一键构建
cd gramps/aio
./build.sh
如需完整保留构建日志,改用:
./build.sh >& build_log.txt
构建成功后,安装包位于:
gramps/aio/ucrt64/src/GrampsAIO-[appversion]-[appbuild]-[hash]_win64.exe
其中 appversion 来自 gramps/version.py 中的 VERSION_TUPLE(当前仓库为 6.1.0),appbuild 在开发版本下为 <分支名>-<短提交号>(如 master-abc1234),在正式发布版本下为 <VERSION_QUALIFIER>-<构建号>。这一逻辑在 aio/build.sh 中实现:当 gramps/version.py 中 DEV_VERSION = True 时取 Git 分支与短提交号,否则取 VERSION_QUALIFIER 与命令行传入的第二个参数(构建号)。
若想测试构建脚本本身、避免重复产出 venv,可传第一个参数清理 venv:
./build.sh true
这对应 aio/build.sh 中 $1 = "true" 时的收尾清理逻辑。
5. 构建完成后的产出物
构建会创建一个 Python 虚拟环境,默认位于 c:\msys64\tmp\grampspythonenv\bin\python.exe。这个 venv 可以被 Visual Studio Code 直接选用,用于调试、静态检查与单元测试(详见下文第三节)。
三、build.sh 背后发生了什么:源码级拆解
为了让读者在遇到构建失败时能快速定位问题,这里把 aio/build.sh 的主要阶段与仓库内对应证据展开说明。
阶段 1:安装 MSYS2 系统级依赖
脚本首先用 pacman -S --needed --noconfirm 安装一批包(aio/build.sh),主要包括:
- 工具链类:
base-devel、mingw-w64-ucrt-x86_64-toolchain、subversion、unzip、git、intltool、perl-XML-Parser; - Gramps 功能依赖:
mingw-w64-ucrt-x86_64-db(Berkeley DB 数据库后端)、geocode-glib(地理编码)、gexiv2(图片 EXIF 元数据)、ghostscript(PDF 支持)、goocanvas(图形画布)、hunspell(拼写检查)、iso-codes、osm-gps-map(地图组件); - Python 生态:
mingw-w64-ucrt-x86_64-python及python-cairo、python-gobject(GTK 绑定)、python-bsddb3、python-cx-freeze(应用冻结工具)、python-lief、python-lxml、python-networkx、python-pillow、python-psycopg2、python-icu、python-jsonschema等; - 打包工具:
mingw-w64-ucrt-x86_64-nsis。
随后,脚本用 pacman -U 从 MSYS2 归档仓库安装三个“降级”包(aio/build.sh):enchant-2.6.7、graphviz-12.2.1、gspell-1.14.0。注释明确说明这些是“因与最新版本存在兼容性问题而回退的旧版本”,一旦问题解决会移回上面的常规列表——这解释了为何构建环境对网络与版本高度敏感。
阶段 2:创建 Python 虚拟环境并安装 pip 包
pythonvenv=$TMP/grampspythonenv
rm -rf $pythonvenv
python -m venv $pythonvenv --system-site-packages
source $pythonvenv/bin/activate
python -m pip install --upgrade pip
pip install --upgrade orjson==3.11.7 pydot pydotplus pygraphviz requests selenium
注意 --system-site-packages 参数:venv 共享 MSYS2 的 system site-packages,从而复用上文通过 pacman 安装的 GTK/Python 绑定,只有少量纯 Python 包走 pip(aio/build.sh)。orjson 被固定到 3.11.7 版本,这一版本约束在 aio/setup.py 的 PACKAGES 中同样出现。
阶段 3:下载拼写词典与芬兰语 Voikko 词典
脚本从 wooorm/dictionaries 仓库批量下载 hunspell 词典(index.aff / index.dic 重命名为对应语言代码),覆盖 bg、ca、cs、da、de、el、en-AU、en-GB、en、eo、es、fr、he、hr、hu、is、it、lt、nb、nl、nn、pl、pt-BR、pt-PT、ru、sk、sl、sr、sv、tr、uk、vi 等 30 余种语言,安装到 /ucrt64/share/enchant/hunspell(aio/build.sh);同时下载 Voikko 快照词典解压到 /ucrt64/share/enchant/voikko(aio/build.sh)。这些词典最终通过 NSIS 脚本的 "Dictionaries" 组件组供用户在安装时勾选(见 aio/grampsaio64.nsi.template)。
阶段 4:准备 GNOME 图标主题
脚本在 /ucrt64/share/icons/gnome 下建立 mimetypes 与 places 目录结构,把仓库 images/gramps.png 与 images/hicolor 中的图标以 gnome-mime- 前缀复制进去(aio/build.sh),保证 Gramps 在 Windows 上也能渲染出完整图标主题。
阶段 5:构建 wheel 并解包
rm -rf dist gramps.egg-info aio/dist aio/GrampsAIO64.egg-info aio/$MSYSTEM
python -m build --wheel
随后依据 gramps/version.py 计算 appversion 与 appbuild,并把 wheel 解包到 aio/dist(aio/build.sh)。wheel 由 pyproject.toml 定义的 hatchling 后端构建,gramps/version.py 中 DEV_VERSION = True、VERSION_TUPLE = (6, 1, 0)、VERSION_QUALIFIER = "-beta2" 即当前仓库的版本事实。
阶段 6:生成 NSIS 脚本并用 cx_Freeze 冻结
cat grampsaio64.nsi.template | sed "s/yourVersion/$escapedappversion/;s/yourBuild/$escapedappbuild/" >grampsaio64.nsi
python setup.py build_exe
模板中的 yourVersion、yourBuild 占位符被替换为真实版本号与构建号(aio/build.sh)。python setup.py build_exe 则交给 aio/setup.py 中的 cx_Freeze 配置执行,它声明了:
INCLUDES/PACKAGES:gi、cairo、lxml、PIL、networkx、psycopg2、requests、graphviz、pydot、orjson、selenium、encodings、importlib等。注释说明这些包被 pip 及其 vendored 库(truststore、rich、urllib3、distlib)惰性/条件导入,cx_Freeze 静态扫描会漏掉,因此必须显式声明(aio/setup.py);EXCLUDES:排除tkinter、PyQt5、sip、pip、distlib等(aio/setup.py);MISSING_DLL/MISSING_LIBS:libgtk-3-0.dll、libgspell-1-3.dll、gswin64c.exe(Ghostscript)、dot.exe(Graphviz)、glib-compile-schemas.exe、gdk-pixbuf-query-loaders.exe、enchant-2、girepository-1.0、iso-codes、字体缓存、GNOME 图标主题等(aio/setup.py);INCLUDE_FILES:额外把pip与distlib两个包目录整包塞进lib/,使安装后的 Gramps 内置 pip 可用于 Addon Manager(aio/setup.py)。
阶段 7:pip 冒烟测试
在进入 NSIS 打包前,脚本对冻结出的 pip.exe 做两次冒烟测试:pip.exe --version 与 pip.exe install --dry-run --ignore-installed certifi。若输出中出现 ImportError / ModuleNotFoundError / Traceback 即判定失败并中止构建,提示开发者把缺失的惰性导入子模块加入 INCLUDES 或父包加入 PACKAGES(aio/build.sh)。这是防止“Addon Manager 首次运行时才崩溃”的关键防线。
阶段 8:NSIS 生成安装器
cd ucrt64/src
makensis grampsaio64.nsi
NSIS 脚本(aio/grampsaio64.nsi.template)执行以下关键动作:
- 安装类型为多用户模式(
MULTIUSER_EXECUTIONLEVEL Highest),要求 64 位 Windows 10 及以上(${RunningX64}、${AtLeastWin10}检查,见 aio/grampsaio64.nsi.template); - 主 Section 强制安装(
SectionIn RO),把lib、etc、share、gramps、ssl等目录递归复制进安装目录,并创建桌面与开始菜单快捷方式(grampsw.exe图形版与gramps.exe控制台版)(aio/grampsaio64.nsi.template); - 写入资源路径文件
gramps\gen\utils\resource-path内容为share,并在gramps\version.py末尾追加VERSION = 'AIO64-...'品牌信息(aio/grampsaio64.nsi.template); - 调用
glib-compile-schemas.exe编译 GLib 模式、gdk-pixbuf-query-loaders.exe --update-cache刷新 pixbuf 缓存(aio/grampsaio64.nsi.template); - "Dictionaries" 组件组提供 30 余种语言词典的可选安装(
/o表示默认不选,en_GB为强制安装),"Translations" 组件组提供 40 余种语言包可选安装(aio/grampsaio64.nsi.template),并按安装器当前界面语言自动预选对应语言(Function sel_langs,见 aio/grampsaio64.nsi.template); - 安装结束时配置 Graphviz(
dot.exe -c)、重建 fontconfig 缓存(fc-cache.exe -fv)、写入卸载注册表信息(aio/grampsaio64.nsi.template)。
阶段 9:清理
若以 ./build.sh true 启动,构建结束会 deactivate 并删除 venv(aio/build.sh)。
四、构建产物如何运行:启动器与运行环境
安装后的 Gramps 由三个启动器驱动,其冻结入口在 aio/setup.py 定义,运行时的环境变量初始化逻辑如下:
- aio/grampsaiow.py:GUI 主入口
grampsw.exe,设置SSL_CERT_FILE(指向ssl/certs/ca-bundle.trust.crt)、GI_TYPELIB_PATH(指向lib/girepository-1.0)、GRAMPS_RESOURCES(指向share),并将安装目录加入PATH;通过CreateMutexW互斥量防止多开,然后调用gramps.grampsapp的main()。 - aio/grampsaioc.py:控制台版
gramps.exe,额外设置PANGOCAIRO_BACKEND=fontconfig并抑制 GI 诊断输出,调用app.run()。 - aio/grampsaiocd.py:守护进程版
grampsd.exe,额外强制LANG=en,用于远程/服务化场景。
这套“冻结 Python + 就地资源目录”的设计,正是 AIO 安装包无需预装 Python 或 GTK 即可独立运行的原因。
五、用 VSCode 在 Windows 上开发 Gramps:官方推荐工作流
以下提示全部以“已完成一次 AIO 构建”为前提,且除非特别说明,所有命令都在 Gramps 源码树根目录执行。
1. 为 VSCode 添加 MSYS2 终端
- 在 Visual Studio Code 中按
Ctrl+Shift+P。 - 搜索 Preferences: Open Settings (JSON) 并打开。
- 加入以下配置:
"terminal.integrated.profiles.windows": {
"MSYS2 UCRT64": {
"path": "C:\\msys64\\usr\\bin\\bash.exe",
"args": [
"--login",
"-i"
],
"env": {
"MSYSTEM": "URCT64",
"CHERE_INVOKING": "1"
}
}
}
注意两点:一是原文配置中 MSYSTEM 值为 URCT64(保留原样即可,实际 MSYS2 环境变量应为 UCRT64,使用时可留意);二是如果 terminal.integrated.profiles.windows 键已存在,需要手动合并以上条目,避免覆盖已有终端配置。
CHERE_INVOKING=1 会让 bash 启动后停留在当前工作目录而非自动跳转到用户主目录,便于直接在 VSCode 打开的项目根目录中操作。
2. 激活构建时生成的 Python 虚拟环境
Gramps 运行需要较多 Python 包,最省事的方式是复用 AIO 构建产生的 venv:
source /tmp/grampspythonenv/bin/activate
官方提示:通常只需构建一次 AIO,即可长期复用它生成的 venv,修改代码后无需重新构建;但在切换 Gramps 版本(如 master 切到 gramps60)或改动 Gramps 依赖时,则需要重新构建。这是因为 venv 通过 --system-site-packages 共享 MSYS2 的 GTK/Python 绑定,版本切换会破坏依赖匹配。
3. 本地运行 mypy 静态检查
python3 -m pip install mypy types-requests
mypy
仓库根目录的 mypy.ini 配置了检查范围(./gramps, ./test)、排除规则(.*gpr\.py$),并为 berkeleydb、bsddb3、cairo、gi、icu、lxml、orjson、PIL 等第三方库统一开启 ignore_missing_imports = True,因此可直接执行 mypy 而不必逐包安装类型桩。
4. 本地运行 black 格式化
python3 -m pip install black
black . --check # 只检查,不改动文件
black . # 实际格式化全部文件
5. 本地运行单元测试
运行全部单元测试:
LANGUAGE= LANG=en_US.utf-8 GRAMPS_RESOURCES=. python -m unittest discover -p '*_test.py'
运行单个测试类,例如只跑 gramps.plugins.test.reports_test.TestDynamic:
LANGUAGE= LANG=en_US.utf-8 GRAMPS_RESOURCES=. python -m unittest gramps.plugins.test.reports_test.TestDynamic
两个命令都显式设置了三个环境变量:LANGUAGE=(清空语言覆盖)、LANG=en_US.utf-8(固定英语区域,避免日期/排序受本机区域影响)、GRAMPS_RESOURCES=.(指定资源目录为当前源码树,使测试使用仓库内 data、images 等资源而非安装版资源)。TestDynamic 位于 gramps/plugins/test/reports_test.py,通过 Gramps(user=User()) 在内存中运行报告/导出命令并断言输出(gramps/plugins/test/reports_test.py)。
注意:少量单元测试在 Windows 上会被跳过(如依赖类 Unix 特性的用例),这是官方文档明确声明的平台差异,并非测试失败。
六、构建与开发 FAQ:常见问题定位
| 现象 | 定位方向 |
|---|---|
pacman -Syuu 后 MSYS2 自动关闭 |
属正常滚动升级流程,需再次运行同一条命令完成升级 |
| 构建在 pip 冒烟测试阶段失败 | 查看日志中的 ImportError/Traceback,把缺失的惰性导入子模块加入 aio/setup.py 的 INCLUDES 或父包加入 PACKAGES |
| 切换 Gramps 版本后运行报错 | venv 与 MSYS2 系统包版本不匹配,重新构建 AIO 并重建 venv |
| 安装包体积/词典语言不满足需求 | 通过 NSIS 脚本的 "Dictionaries" 与 "Translations" 组件组控制(构建期定义,安装期勾选) |
| 想反复测试构建脚本本身 | 使用 ./build.sh true,构建结束自动删除 venv,避免残留 |
| 单元测试在 Windows 上被跳过 | 属已知平台差异,按 aio/README.md 说明无需处理 |
结语
Gramps 的 AIO 构建体系把 MSYS2 依赖管理、Python venv、cx_Freeze 冻结、NSIS 安装器封装为一条自动化流水线,同时通过保留构建期 venv 为 Windows 上的 Gramps 开发提供了一条开箱即用的调试路径。对维护者而言,理解 aio/build.sh 的分阶段职责与 aio/setup.py 的冻结清单,是排查打包问题的关键;对开发者而言,一次构建、反复使用 venv,即可获得与官方发布版完全一致的运行环境,并在 VSCode 中完成静态检查、格式化与单元测试的日常循环。