Gramps Windows AIO 安装包构建指南:从 MSYS2 环境搭建到 VSCode 调试工作流

原创2026-10-14 13:57:311,693 阅读
文章标签:桌面应用

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

  1. 从 MSYS2 官网下载安装包,按默认选项完成安装。
  2. 从开始菜单启动 MSYS2 UCRT64(注意是 UCRT64 环境,这是 Gramps AIO 依赖 mingw-w64-ucrt-x86_64-* 系列包的前提)。
  3. 升级系统软件包:
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 终端

  1. 在 Visual Studio Code 中按 Ctrl+Shift+P。
  2. 搜索 Preferences: Open Settings (JSON) 并打开。
  3. 加入以下配置:
"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 中完成静态检查、格式化与单元测试的日常循环。

登录后查看全文
gramps