macOS 构建与发布

在 macOS 上构建 ShogiBoardQ,并以 DMG 文件发布

macOS Qt 6 DMG 发布 GitHub Releases

Release 只附加发布用 ZIP(ShogiBoardQ-macos.zip),不另行附加 Qt 源代码或相关文档。应用程序内许可文档的准备请参阅 Qt 文档与发布附件的方针(日文)。

DMG 中只放入运行 ShogiBoardQ 所需的文件(最小构成)。与 Linux 版一样,用于普通对局的 Hayanagi(USI 引擎)和诘棋题集放在 DMG 之外,与 DMG 一起放入发布用 ZIP(5.2)。界面语言有日语、英语、中文(简体、繁体)4 种,附带 4 个翻译文件。

目录

  1. 前提条件
  2. 搭建开发环境
  3. 构建
  4. 制作应用程序包
  5. 制作 DMG 文件
  6. 代码签名与公证
  7. 在 GitHub Releases 上发布
  8. 故障排除

1. 前提条件

项目版本
macOS推荐 12 (Monterey) 以上
Xcode14 以上(含 Command Line Tools)
CMake3.16 以上
Qt6.7 以上(Widgets、Charts、Network、Concurrent、Multimedia、Sql、LinguistTools)
C++支持 C++17 的编译器

2. 搭建开发环境

2.1 Xcode Command Line Tools

xcode-select --install

2.2 用 Homebrew 安装依赖工具

brew install cmake ninja

2.3 安装 Qt 6

方法 A:Qt Online Installer(推荐)

从 Qt 官方网站下载安装程序,并选择以下组件:

  • Qt 6.x > macOS
  • Qt 6.x > Qt Charts
  • Qt 6.x > Qt Multimedia
  • Developer and Designer Tools > CMake

安装后,将 Qt 的路径设置到环境变量中:

# 添加到 ~/.zshrc(Qt 的安装路径请根据实际环境修改)
export Qt6_DIR="$HOME/Qt/6.x.x/macos/lib/cmake/Qt6"
export PATH="$HOME/Qt/6.x.x/macos/bin:$PATH"

方法 B:Homebrew

brew install qt@6

使用 Homebrew 时,CMake 会自动检测到 Qt,因此多数情况下不需要设置环境变量。

2.4 确认安装

cmake --version       # 3.16 以上
qmake --version       # Qt 6.x
clang++ --version     # Apple Clang

3. 构建

用于普通对局的 Hayanagi 是子模块,因此获取时要加上 --recurse-submodules(不加时 CMake 会停止):

git clone --recurse-submodules https://github.com/hnakada123/ShogiBoardQ.git
cd ShogiBoardQ

对于已获取的仓库,运行 git submodule update --init --recursive 获取所记录版本的 Hayanagi。

构建脚本(推荐)

使用 scripts/build-macos.sh,可以一次完成从 Release 构建到代码签名、制作 DMG 的全部步骤:

# 普通构建 + 制作 DMG
./scripts/build-macos.sh

# 构建通用二进制(arm64 + x86_64)
./scripts/build-macos.sh --universal

# 清理后构建,不制作 DMG
./scripts/build-macos.sh --clean --skip-dmg

# 尚未准备 Qt 许可文档时的测试用 DMG(不用于发布)
./scripts/build-macos.sh --clean --skip-qt-licenses

# 用 Developer ID 签名(默认为临时签名)
./scripts/build-macos.sh --sign-identity "Developer ID Application: Your Name (TEAMID)"

# 用 Developer ID 签名,公证 DMG 后再制作 ZIP
export APPLE_ID="your@email.com" APPLE_TEAM_ID="TEAMID" APPLE_APP_PASSWORD="app-specific-password"
./scripts/build-macos.sh --sign-identity "Developer ID Application: Your Name (TEAMID)" --notarize
选项说明
--universal构建通用二进制(arm64 + x86_64)
--deployment-target VER最低支持的 macOS 版本(默认:环境变量 MACOSX_DEPLOYMENT_TARGET,未设置时为 26.0)
--sign-identity ID代码签名身份(默认:- = 临时(ad hoc)签名)。指定 Developer ID 时,会启用强化运行时(Hardened Runtime)并附加时间戳进行签名,DMG 也会签名
--notarize将 DMG 提交 Apple 公证,装订公证结果后再制作发布用 ZIP。需要用 --sign-identity 指定 Developer ID,并设置环境变量 APPLE_ID、APPLE_TEAM_ID、APPLE_APP_PASSWORD(App 专用密码)
--skip-dmg跳过 DMG 和发布用 ZIP 的制作(只生成 .app 包)
--skip-qt-licenses跳过添加 Qt 文档(日文),只保留构建时附带的简易文档(Contents/MacOS/licenses)。发布时不要使用
--clean删除 build 目录后再构建
--help显示帮助

脚本会自动执行以下处理:

  1. 确认所需工具(cmake、ninja、macdeployqt、codesign、vtool、python3、create-dmg)
  2. CMake 配置(指定 CMAKE_OSX_DEPLOYMENT_TARGET)+ Ninja 构建
  3. 确认构建产物(.app、可执行文件的最低 macOS 版本、.qm 翻译文件)
  4. 用 macdeployqt 打包框架
  5. 删除未使用的 Qt 组件,并把 Qt 二进制精简为 arm64(4.4。使用 --universal 时不精简为 arm64)+ 验证
  6. 附带 Qt 许可文档(使用 --skip-qt-licenses 时跳过)
  7. 对整个应用程序包进行代码签名 + 用 codesign --verify --deep --strict 验证
  8. 用 create-dmg 制作 DMG(使用 Developer ID 时也对 DMG 签名)
  9. 使用 --notarize 时,对 DMG 进行公证和装订 + 验证
  10. 制作包含 DMG、Hayanagi 和诘棋题集的发布用 ZIP(ShogiBoardQ-macos.zip)(5.2)

关于最低 macOS 版本:如果不指定部署目标(deployment target),构建主机的 macOS 版本就会成为最低支持版本(例:在 macOS 27 上构建后,无法在 macOS 26 上启动)。脚本默认指定 26.0,构建后如果可执行文件的 minos 不一致就会停止。

关于代码签名:macdeployqt 会改写二进制,因此运行后的应用程序包只剩链接器签名,无法通过严格的签名验证。脚本在 macdeployqt 之后重新对整个应用程序包签名,然后再制作 DMG。

关于 macdeployqt 显示的 ERROR:会出现 .qm: is not an object file(对翻译文件运行了 otool)、找不到 ODBC / PostgreSQL / Mimer SQL 驱动的依赖项,以及 macdeployqt 自身的签名错误,但都无害。这些驱动会在下一步删除,签名也会由脚本重新进行并严格验证。

以下是手动逐条运行命令时的步骤。

3.1 Release 构建

cd /path/to/ShogiBoardQ

# 配置(Release 构建)
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release

# 构建(并行构建)
cmake --build build --config Release -- -j$(sysctl -n hw.ncpu)

使用 Ninja 时(更快):

cmake -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=Release
ninja -C build

3.2 确认构建产物

# 确认已生成 .app 包
ls -la build/ShogiBoardQ.app/

# 确认包内的结构
find build/ShogiBoardQ.app -type f | head -20

构建后的包结构:

ShogiBoardQ.app/
├── Contents/
│   ├── Info.plist
│   ├── MacOS/
│   │   ├── ShogiBoardQ              ← 可执行文件
│   │   ├── ShogiBoardQ_ja_JP.qm     ← 日语翻译
│   │   ├── ShogiBoardQ_en.qm        ← 英语翻译
│   │   ├── ShogiBoardQ_zh_CN.qm     ← 简体中文翻译
│   │   ├── ShogiBoardQ_zh_TW.qm     ← 繁体中文翻译
│   │   └── licenses/                ← 许可文档(简易版)
│   └── Resources/
│       └── shogiboardq.icns          ← 应用程序图标

3.3 运行确认

# 启动构建好的应用程序
open build/ShogiBoardQ.app

4. 制作应用程序包

注:使用 scripts/build-macos.sh 时,本节的步骤会自动执行,无需手动运行。

4.1 用 macdeployqt 打包 Qt 框架

仅仅构建的话,Qt 的动态库不会包含在应用程序包中。使用 macdeployqt 将所需的 Qt 框架和插件复制到包内。

macdeployqt build/ShogiBoardQ.app -verbose=2

macdeployqt 自动执行的处理:

  1. 将依赖的 Qt 框架(QtWidgets、QtCharts、QtNetwork、QtMultimedia、QtGui、QtCore 等)复制到 Contents/Frameworks/
  2. 将 Qt 插件(platforms/cocoa、imageformats/svg 等)复制到 Contents/PlugIns/
  3. 改写库的 @rpath(install_name_tool)
  4. 在 Contents/Resources/ 中生成 qt.conf

4.2 确认打包后的结构

# 确认框架已正确打包
ls build/ShogiBoardQ.app/Contents/Frameworks/

# 确认包含插件
ls build/ShogiBoardQ.app/Contents/PlugIns/

# 确认依赖关系中没有残留外部路径
otool -L build/ShogiBoardQ.app/Contents/MacOS/ShogiBoardQ

如果 otool 的输出中残留 /usr/local/ 或 /opt/homebrew/ 的路径,说明打包不完整。

4.3 确认翻译文件

CMake 的构建后步骤会把 .qm 文件放到 Contents/MacOS/。确认运行 macdeployqt 后文件仍然存在:

ls build/ShogiBoardQ.app/Contents/MacOS/*.qm   # 4 个(ja_JP / en / zh_CN / zh_TW)

Qt 标准对话框的翻译(qtbase_ja / qtbase_zh_CN / qtbase_zh_TW)嵌入在可执行文件的资源中,因此不需要单独的文件。

4.4 删除未使用的 Qt 组件(最小构成)

macdeployqt 会按类别放入所有 Qt 插件,并通过虚拟键盘插件连带引入 Qt Quick / QML(约 187MB)。删除 ShogiBoardQ 不使用的部分,在仅面向 Apple 芯片的发布中还要去掉 Qt 的 x86_64 部分(约 89MB,DMG 约 50MB)。

删除的内容理由
PlugIns/platforminputcontexts/、QtQml* / QtQuick / QtVirtualKeyboard*不使用虚拟键盘和 QML
PlugIns/tls/ 中 SecureTransport 以外的后端、PlugIns/networkinformation/网络对局的“TLS(加密)”使用基于 macOS 标准 Security 框架的 SecureTransport 后端(OpenSSL 后端需要同捆 libssl)。不使用网络信息插件
PlugIns/multimedia/libffmpegmediaplugin.dylib、libav* / libsw*棋子音效使用 macOS 标准的 darwin 后端播放
PlugIns/sqldrivers/ 中 SQLite 以外的驱动只使用 SQLite
PlugIns/imageformats/ 中的 gif / wbmp / macheif / icns / tga / macjp2只使用 SVG / ICO(图标)和 JPEG / TIFF / WebP(导出棋盘图片)
C=build/ShogiBoardQ.app/Contents
rm -rf $C/PlugIns/platforminputcontexts $C/PlugIns/networkinformation \
  $C/PlugIns/multimedia/libffmpegmediaplugin.dylib $C/Frameworks/libav*.dylib $C/Frameworks/libsw*.dylib \
  $C/Frameworks/QtQml*.framework $C/Frameworks/QtQuick.framework $C/Frameworks/QtVirtualKeyboard*.framework
find $C/PlugIns/tls -name '*.dylib' ! -name libqsecuretransportbackend.dylib -delete
find $C/PlugIns/sqldrivers -name '*.dylib' ! -name libqsqlite.dylib -delete
for p in gif wbmp macheif icns tga macjp2; do rm -f $C/PlugIns/imageformats/libq$p.dylib; done

# 把 Qt 精简为只含 arm64(发布通用二进制时不要执行)
find $C -type f \( -perm +111 -o -name '*.dylib' \) | while read -r f; do
  if lipo -archs "$f" 2>/dev/null | grep -q x86_64; then
    lipo "$f" -thin arm64 -output "$f.thin" && chmod "$(stat -f %Lp "$f")" "$f.thin" && mv "$f.thin" "$f"
  fi
done

保留的是 12 个 Qt 框架(Charts、Concurrent、Core、DBus、Gui、Multimedia、Network、OpenGL、OpenGLWidgets、Sql、Svg、Widgets)和 10 个插件(cocoa、macstyle、svgicon、darwin multimedia、sqlite、securetransport、ico / jpeg / tiff / webp)。删除后要重新对整个应用程序包签名(6.4。临时签名时为 codesign --force --deep --sign - build/ShogiBoardQ.app)。

启动后,确认加载的库全部来自包内:

open build/ShogiBoardQ.app
lsof -p "$(pgrep -x ShogiBoardQ)" | grep -E '/Qt/|homebrew'   # 没有任何输出即可

5. 制作 DMG 文件

注:使用 scripts/build-macos.sh 时,会用方法 B(create-dmg)自动制作,无需手动运行。也可以用 --skip-dmg 选项只跳过 DMG 的制作。

手动制作时,也要在放入 DMG 之前完成 4.4 的删除和签名。方法 A 由 macdeployqt 直接制作 DMG,因此不是最小构成。

方法 A:macdeployqt 的 -dmg 选项(简易)

macdeployqt build/ShogiBoardQ.app -dmg

会生成 build/ShogiBoardQ.dmg。方法简单,但没有背景图片,也没有指向 Applications 文件夹的链接。

方法 B:create-dmg(推荐 - 自定义 DMG)

制作外观美观、用户可以拖放到 Applications 文件夹的 DMG。

安装

brew install create-dmg

制作 DMG

# 用 macdeployqt 制作应用程序包(不加 -dmg)
macdeployqt build/ShogiBoardQ.app

# 制作 DMG
create-dmg \
  --volname "ShogiBoardQ" \
  --volicon "resources/icons/shogiboardq.icns" \
  --window-pos 200 120 \
  --window-size 600 400 \
  --icon-size 100 \
  --icon "ShogiBoardQ.app" 150 190 \
  --hide-extension "ShogiBoardQ.app" \
  --app-drop-link 450 190 \
  "ShogiBoardQ.dmg" \
  "build/ShogiBoardQ.app"

选项说明

选项说明
--volname装载时的卷名
--volicon卷图标
--window-posDMG 窗口的显示位置(x y)
--window-sizeDMG 窗口的大小(宽 高)
--icon-size图标大小(px)
--icon应用程序图标的显示位置(名称 x y)
--hide-extension隐藏 .app 扩展名
--app-drop-link指向 Applications 文件夹的链接位置(x y)

方法 C:hdiutil(手动 - 完全控制)

# 创建临时目录
mkdir -p dmg_staging
cp -R build/ShogiBoardQ.app dmg_staging/
ln -s /Applications dmg_staging/Applications

# 制作 DMG
hdiutil create -volname "ShogiBoardQ" \
  -srcfolder dmg_staging \
  -ov -format UDZO \
  "ShogiBoardQ.dmg"

# 清理
rm -rf dmg_staging

5.1 验证 DMG

# 装载后确认运行
hdiutil attach ShogiBoardQ.dmg
open /Volumes/ShogiBoardQ/ShogiBoardQ.app

# 推出
hdiutil detach /Volumes/ShogiBoardQ

5.2 发布用 ZIP

脚本在制作 DMG 之后,会在仓库根目录制作 ShogiBoardQ-macos.zip。结构与 Linux 版的 ZIP 相同。

ShogiBoardQ-macos/
├── ShogiBoardQ.dmg
├── README.md                      ← resources/platform/README-macos.md
├── LICENSE
├── Hayanagi/
│   ├── hayanagi                   ← 用于普通对局的 USI 引擎(arm64,最低 macOS 版本与应用程序相同)
│   ├── book/hayanagi_book.db      ← Hayanagi 的定式
│   └── README.md
└── data/tsumeshogi/
    ├── tsume_{3,5,7,9,11,13}ply_1000_YYYYMMDD.txt
    └── README.md
  • 题集每种手数只能有 1 个文件。如果残留旧版本等文件,脚本会停止。
  • hayanagi 使用对 build/Hayanagi/hayanagi 执行 strip -x 后的文件,并用与应用程序相同的身份(默认为临时签名)重新签名。
  • 定式文件 hayanagi_book.db 使用构建时复制到 build/Hayanagi/book/ 的文件,放入 Hayanagi/book/。Hayanagi 从工作目录的 book/ 读取定式,而 ShogiBoardQ 以引擎所在的目录作为工作目录启动引擎,因此解压后即可直接使用定式。
  • 下载的 hayanagi 会带有隔离属性,因此 README 中说明了 xattr -d com.apple.quarantine Hayanagi/hayanagi。
unzip -l ShogiBoardQ-macos.zip
# 确认 Hayanagi 能响应并读取定式(与 ShogiBoardQ 一样在 Hayanagi/ 中启动)
(cd build/ShogiBoardQ-macos/Hayanagi && (printf 'usi\nisready\nposition startpos\ngo movetime 300\n'; sleep 1.5; echo quit) | ./hayanagi | grep -E 'book|usiok|readyok|bestmove')

6. 代码签名与公证

对外发布时,为了通过 Apple 的 Gatekeeper,需要进行代码签名和公证。不经公证也能运行,但首次打开下载的应用程序时会显示 Gatekeeper 的警告,需要用户允许(6.6)。

6.1 加入 Apple Developer Program

代码签名和公证需要加入 Apple Developer Program(年费 99 美元)。

6.2 获取签名用证书

在 Xcode > Settings > Accounts 中创建以下证书:

  • Developer ID Application — 用于对应用程序本身签名
  • Developer ID Installer — 用于对 DMG / pkg 签名(可选)

6.3 用 macdeployqt 制作带代码签名的应用程序包

macdeployqt build/ShogiBoardQ.app \
  -sign-for-notarization="Developer ID Application: Your Name (TEAMID)"

使用这个选项时,macdeployqt 会:

  1. 打包 Qt 框架和插件
  2. 对所有框架、插件和可执行文件进行代码签名
  3. 启用 --options=runtime(强化运行时)

6.4 手动进行代码签名

不使用 macdeployqt 的签名选项时:

# 递归地对包内的所有二进制签名
codesign --deep --force --verify --verbose \
  --sign "Developer ID Application: Your Name (TEAMID)" \
  --options runtime \
  build/ShogiBoardQ.app

# 验证签名
codesign --verify --deep --strict --verbose=2 build/ShogiBoardQ.app

6.5 公证(Notarization)

使用脚本时,用 --sign-identity 指定 Developer ID 并加上 --notarize,脚本会把 DMG 提交公证并装订,然后再制作发布用 ZIP(3. 构建)。公证未通过时会显示提交 ID 并停止。以下是手动进行时的步骤。

# 制作 DMG(使用已签名的应用程序包)
# ...(按第 5 节的方法制作 DMG)

# 提交公证
xcrun notarytool submit ShogiBoardQ.dmg \
  --apple-id "your@email.com" \
  --team-id "TEAMID" \
  --password "app-specific-password" \
  --wait

# 装订(把公证结果嵌入 DMG)
xcrun stapler staple ShogiBoardQ.dmg

# 验证装订
xcrun stapler validate ShogiBoardQ.dmg

注意:--password 使用的不是 Apple 账户的密码,而是 App 专用密码。

6.6 不经公证发布时

不经公证就发布时,下载的应用程序在首次启动时会被 Gatekeeper 拦截。从 macOS 15 开始,无法再通过在 Finder 中右键点按 → “打开”来打开,因此请向用户说明以下步骤:

  1. 打开应用程序,显示警告后将其关闭
  2. 打开“系统设置”→“隐私与安全性”,点按“仍要打开”,并按提示允许

或者在终端中解除 Gatekeeper 的隔离属性(没有显示“仍要打开”时也使用这个方法):

xattr -cr /Applications/ShogiBoardQ.app

7. 在 GitHub Releases 上发布

7.1 创建标签

版本号只在发布当天改为当天的日期(版本号与发布的运作方式,日文)。制作发布包之前,先修改 CMakeLists.txt 中的 APP_VERSION 并提交,用该提交制作发布包,然后在同一提交上打上相同版本号的标签。标签不加 v。

git tag -a 2026.10.07 -m "ShogiBoardQ 2026.10.07"
git push origin main 2026.10.07

7.2 用 GitHub CLI 创建 Release

使用 GitHub CLI (gh):

# 安装
brew install gh

# 登录(仅首次)
gh auth login

创建 Release 并上传附件:

gh release create 2026.10.07 \
  --title "ShogiBoardQ 2026.10.07" \
  --notes-file RELEASE_NOTES.md \
  ShogiBoardQ-macos.zip

同时发布 Windows 的 ZIP 时,添加附件:

gh release create 2026.10.07 \
  --title "ShogiBoardQ 2026.10.07" \
  --notes-file RELEASE_NOTES.md \
  ShogiBoardQ-macos.zip \
  ShogiBoardQ-windows.zip

自动生成发布说明

不准备发布说明文件时,由 GitHub 自动生成:

gh release create 2026.10.07 \
  --title "ShogiBoardQ 2026.10.07" \
  --generate-notes \
  ShogiBoardQ-macos.zip

向已有的 Release 添加附件

已在 Windows 端先创建了 Release 时,添加 macOS 的 ZIP:

gh release upload 2026.10.07 ShogiBoardQ-macos.zip

7.3 在网页界面上创建 Release(替代方法)

  1. GitHub 仓库 → Releases → Draft a new release
  2. Choose a tag → 输入新标签(例:2026.10.07)并创建
  3. 输入 Release title(例:ShogiBoardQ 2026.10.07)
  4. 在 Description 中填写发布说明
  5. 将 ShogiBoardQ-macos.zip 拖放到 Attach binaries
  6. 点击 Publish release

7.4 发布说明的写法(模板)

## ShogiBoardQ 2026.10.07

### 下载

| OS | 文件 |
|---|---|
| macOS | `ShogiBoardQ-macos.zip` |
| Windows (64-bit) | `ShogiBoardQ-windows.zip` |

### 在 macOS 上启动

1. 解压 ZIP,打开 `ShogiBoardQ.dmg`
2. 将 ShogiBoardQ.app 拖放到 Applications 文件夹
3. 从 Applications 启动

> 由于未经 Apple 公证,首次启动时会显示警告而无法打开。
> 在“系统设置”→“隐私与安全性”中点按“仍要打开”即可启动。

### 变更内容

- ...

8. 故障排除

找不到 Qt

CMake Error: Could not find a package configuration file provided by "Qt6"

明确指定 Qt 的安装路径:

cmake -B build -S . -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_PREFIX_PATH="$HOME/Qt/6.x.x/macos"

找不到 macdeployqt

# 确认 Qt 的安装路径
which macdeployqt

# 找不到时,添加到 PATH
export PATH="$HOME/Qt/6.x.x/macos/bin:$PATH"

# 使用 Homebrew 时
export PATH="$(brew --prefix qt@6)/bin:$PATH"

macdeployqt 没有打包 Qt Charts

macdeployqt 有时无法自动检测到 Qt Charts。这时需要手动复制:

# 确认是否遗漏打包
otool -L build/ShogiBoardQ.app/Contents/MacOS/ShogiBoardQ | grep Charts

# 手动复制(需要时)
cp -R "$HOME/Qt/6.x.x/macos/lib/QtCharts.framework" \
  build/ShogiBoardQ.app/Contents/Frameworks/

# 用 install_name_tool 修正引用路径
install_name_tool -change \
  @rpath/QtCharts.framework/Versions/A/QtCharts \
  @executable_path/../Frameworks/QtCharts.framework/Versions/A/QtCharts \
  build/ShogiBoardQ.app/Contents/MacOS/ShogiBoardQ

首次启动时无法打开(未经公证时)

向用户说明以下任一方法:

  • 在“系统设置”→“隐私与安全性”中点按“仍要打开”(仅首次。从 macOS 15 开始,在 Finder 中右键点按 → “打开”无法打开)
  • 在终端中运行 xattr -cr /Applications/ShogiBoardQ.app

无法加载翻译

确认包内存在 .qm 文件:

find build/ShogiBoardQ.app -name "*.qm"

Contents/MacOS/ 中没有 .qm 文件时,手动复制:

cp build/ShogiBoardQ_{ja_JP,en,zh_CN,zh_TW}.qm build/ShogiBoardQ.app/Contents/MacOS/

制作通用二进制(Intel + Apple 芯片)

制作同时支持两种架构的二进制时:

cmake -B build -S . -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"

cmake --build build --config Release

注意:Qt 本身也必须安装通用二进制版本。