Jellyfin Desktop macOS 构建指南:Qt 6 + mpv 的依赖安装、编译、测试与 DMG 打包全流程

原创2026-10-01 21:46:081,112 阅读
文章标签:音视频桌面应用

Jellyfin Desktop macOS 构建指南:Qt 6 + mpv 的依赖安装、编译、测试与 DMG 打包全流程

Jellyfin Desktop 是 Jellyfin 媒体服务器的官方桌面客户端(仓库根目录见 README.md),本项目为其 C++/Qt 实现。本文以仓库内的 dev/macos/README.md 为骨架,结合 dev/macos/ 下的全套 shell 脚本、CMake 配置与源码,完整讲解在 macOS 上从零搭建 Qt 6 + mpv 开发环境、编译、运行、跑单测、制作 DMG 安装包的完整流程。读完本文,你将掌握这套脚本体系每一步背后的实际命令、环境变量与源码依据,能够独立完成一次 macOS 端到端构建与打包。

快速开始:四步跑通 macOS 开发流程

官方文档给出了极简的起步命令,整个过程分为「装依赖 → 编译 → 运行 → 测试」四步:

dev/macos/setup.sh   # 首次使用:安装全部依赖(Xcode CLT、Homebrew 工具、Qt、mpv)
dev/macos/build.sh   # 配置并编译
dev/macos/run.sh     # 运行构建出的应用(参数会原样透传给应用)
dev/macos/test.sh    # 运行单元测试(会自动把 Qt/mpv 加入 PATH)

其中 setup.sh 只需要在首次构建前运行一次;之后每次改代码只需执行 build.sh + run.sh 即可。注意:文档中只列出 setup.sh、build.sh、run.sh、test.sh 四个脚本,打包脚本 bundle.sh 用于生成分发的 DMG,详见下文「脚本体系详解」。

前置条件

在运行 setup.sh 之前,机器上需要先具备两样基础工具,其余全部由脚本自动安装:

  • Xcode Command Line Tools:执行 xcode-select --install 安装。setup.sh 会先检查(调用 xcode-select -p),若未安装则触发系统安装并提示「安装完成后请重新运行本脚本」后退出(见 setup.sh)。
  • Homebrew:官方安装地址见 https://brew.sh。setup.sh 检查 command -v brew,未找到直接报错退出(见 setup.sh)。

setup.sh 会安装其余全部依赖(见 setup.sh):

依赖 用途
CMake 项目构建系统(见根目录 CMakeLists.txt)
Ninja CMake 指定的构建器(-G Ninja)
aqtinstall 官方 Qt 安装工具,用于下载指定版本 Qt 及附加模块
Qt 6.10.1 应用运行所需的 Qt 框架(含 WebEngine 模块)
create-dmg 打包 DMG 安装镜像的工具
mpv 视频播放后端 libmpv(通过 mpvqt 集成)

需要说明的是,brew install 一行安装的是 aqtinstall mpv ninja cmake create-dmg 五个包;Qt 本身不是通过 brew 安装,而是由 aqt install-qt 下载到项目本地目录(详见下文「目录结构」),这一点保证了 Qt 版本与项目的精确锁定。

目录结构:依赖、开发构建与发布构建的区分

文档明确了三个关键目录(均相对仓库根目录):

  • dev/macos/deps/ —— 下载的依赖(主要是 Qt)。脚本中 DEPS_DIR="${SCRIPT_DIR}/deps",即 <仓库根>/dev/macos/deps(见 common.sh)。
  • build/ —— 构建输出目录,可以安全删除(干净构建即删它,见下文)。
  • build/src/Jellyfin Desktop.app —— 开发构建产物(未打包 Qt 框架,供 run.sh 直接运行,运行依赖通过环境变量注入,见下文 setup_runtime)。
  • build/output/Jellyfin Desktop.app —— 发布构建产物(由 bundle.sh 经 ninja install + macdeployqt 打入 Qt 框架后的完整 .app bundle)。

Qt 的具体路径为 dev/macos/deps/qt/6.10.1/macos,这个路径同时被写入 CMake 配置(QTROOT)和运行环境(DYLD_FRAMEWORK_PATH),是整套脚本体系的核心坐标。

脚本体系详解:五个脚本 + 一个公共库

dev/macos/ 下共有 6 个文件,除 README 外,5 个脚本均以 common.sh 为公共依赖(每个脚本开头都 . "${SCRIPT_DIR}/common.sh")。

common.sh:统一变量与运行环境

common.sh 定义的关键变量:

变量 值 说明
QT_VERSION 6.10.1 全项目锁定的 Qt 版本
DEPS_DIR dev/macos/deps 依赖下载目录
BUILD_DIR <仓库根>/build 构建输出目录
APP_NAME Jellyfin Desktop.app 应用包名(含空格,引用时需带引号)
PROJECT_ROOT 仓库根目录 由脚本自身定位,不依赖调用者当前目录

它还提供了 setup_runtime() 函数,为未打包的开发构建注入 Qt 运行环境:

export DYLD_FRAMEWORK_PATH="${QTROOT}/lib"
export QT_PLUGIN_PATH="${QTROOT}/plugins"
export QML_IMPORT_PATH="${QTROOT}/qml"

即:框架搜索路径指向 aqt 下载的 Qt 6.10.1、插件路径指向 plugins、QML 模块路径指向 qml。run.sh 与 test.sh 都调用该函数,所以开发构建不需要把 Qt 复制进 .app 也能运行。setup_runtime() 还会检查 BUILD_DIR 是否存在,不存在则提示「先运行 build.sh」。

setup.sh:五步完成全部依赖安装

setup.sh 的执行流程对应文档的 Quick Start:

  1. [1/5] 检查 Xcode Command Line Tools:xcode-select -p 失败则调用 xcode-select --install 并提示重跑。
  2. [2/5] 检查 Homebrew:不存在则报错退出。
  3. [3/5] 安装构建工具:brew install aqtinstall mpv ninja cmake create-dmg。
  4. [4/5] 安装 Qt 6.10.1:先检查 dev/macos/deps/qt/6.10.1/macos 是否已存在,存在则跳过(幂等设计);否则执行 aqt install-qt mac desktop 6.10.1 -m qtwebengine qtwebchannel qtpositioning -O qt。-m 指定的三个附加模块是关键:qtwebengine(Jellyfin Desktop 的界面由 QtWebEngine 承载)、qtwebchannel(C++ 与网页端通信通道)、qtpositioning。
  5. 5/5] 生成 CMakePresets.json:用 sed 将模板 [dev/CMakePresets.json.in 中的 @QT_VERSION@、@BREW_PREFIX@ 占位符替换为实际值(brew --prefix),生成根目录下的 CMakePresets.json。该 preset 名为 macos-dev,预设了 CMAKE_BUILD_TYPE=Debug、QTROOT、USE_STATIC_MPVQT=ON、MACOS_LAUNCH_WRAPPER=ON,并带 condition 仅当宿主系统为 Darwin(macOS)时生效。

build.sh:CMake 配置 + Ninja 编译

build.sh 在构建前做两道前置校验:QTROOT 目录存在(否则提示先跑 setup.sh)、mpv 命令可用(否则同样提示先跑 setup.sh)。随后在 build/ 下执行:

cmake -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=output \
    -DQTROOT="${QTROOT}" \
    -DCMAKE_PREFIX_PATH="${QTROOT}" \
    -DUSE_STATIC_MPVQT=ON \
    "${PROJECT_ROOT}"
ninja

参数说明:

  • -DCMAKE_BUILD_TYPE=Release:脚本默认走发布模式;CMake preset(macos-dev)则是 Debug,两种方式并存,可按需选用。
  • -DQTROOT / -DCMAKE_PREFIX_PATH:都指向 aqt 安装的 Qt,让 CMake 找到 Qt6 的 config 文件。
  • -DUSE_STATIC_MPVQT=ON:对应根目录 CMakeLists.txt 的 option(USE_STATIC_MPVQT "Build MpvQt from bundled submodule instead of using system library" OFF),即使用仓库内置的 external/mpvqt 子模块源码编译 mpvqt,而不是用系统库。
  • 项目还支持 MACOS_LAUNCH_WRAPPER(见 CMakeLists.txt),开启后由 CMakeModules/MacOSLaunchWrapper.cmake 把链接产物改名为 .bin,再生成一个 exec 真实二进制的 shell 包装脚本——这是为了阻止 Qt Creator 注入 DYLD_FRAMEWORK_PATH 导致加载到错误版本 Qt 的问题(例如与 Homebrew Qt 冲突)。
  • macOS 平台配置由 CMakeModules/AppleConfiguration.cmake 注入:链接 Foundation/AppKit/IOKit/Cocoa/Security/Carbon/MediaPlayer 等系统框架,并加入 HIDRemote、letsmove 两个外部库;C 编译标志含 -mmacosx-version-min=10.15。

编译完成后,产物即 build/src/Jellyfin Desktop.app。

bundle.sh:打包发布版 .app 与 DMG

bundle.sh 面向发布场景,流程为:

  1. 校验 build/src/Jellyfin Desktop.app 存在(必须先 build)。
  2. ninja install:执行 macdeployqt 将 Qt 框架打入应用包,安装到 build/output/(CMAKE_INSTALL_PREFIX=output)。
  3. codesign --force --deep -s - "build/output/Jellyfin Desktop.app":对应用包做临时签名(ad-hoc,-s -),满足 macOS 对签名应用的要求。
  4. create-dmg 制作 DMG:版本号取自仓库根目录 VERSION(当前为 2.0.0-dev),架构取 uname -m(如 arm64 / x86_64),产物命名为 JellyfinDesktop-<版本>-<架构>.dmg:
create-dmg \
    --volname "Jellyfin Desktop v${VERSION}" \
    --no-internet-enable \
    --window-size 500 300 \
    --icon-size 100 \
    --icon "Jellyfin Desktop.app" 125 150 \
    --app-drop-link 375 150 \
    "${DMG_NAME}" "${APP_BUNDLE}"

参数含义:--volname 设置磁盘卷名,--window-size/--icon-size 控制 Finder 打开时的窗口与图标尺寸,--icon 指定应用图标位置,--app-drop-link 生成一个「拖拽安装」快捷链接。最终 DMG 位于 build/JellyfinDesktop-<版本>-<架构>.dmg。

run.sh:直接运行开发构建

run.sh 调用 setup_runtime() 注入 Qt 运行环境,校验应用包存在后执行:

exec "${APP_PATH}/Contents/MacOS/Jellyfin Desktop" ${1+"$@"}

exec 让脚本进程直接替换为应用进程;${1+"$@"} 把所有命令行参数原样透传给应用——这正是文档「run.sh 透传参数」一说的来源。因此在运行期可以直接追加应用支持的命令行选项。

test.sh:运行单元测试

test.sh 同样先 setup_runtime() 注入 Qt/mpv 环境,然后:

cd "${BUILD_DIR}"
ctest --output-on-failure "$@"

即调用 CTest(由根目录 CMakeLists.txt 的 enable_testing() 与 add_subdirectory(tests) 开启,测试源文件见 tests/ 下的 test_log、test_settings、test_displaymanager、test_systemcomponent)。--output-on-failure 保证测试失败时打印完整输出,尾部的 "$@" 同样允许透传 ctest 参数(如 -R 过滤、-j 并行)。

干净构建

开发中若遇到诡异问题,文档推荐直接清空构建目录重新来过:

rm -rf build
dev/macos/build.sh

因为 build/ 是可再生的输出目录,删除后重新执行 build.sh 会完整重跑 CMake 配置与编译,setup.sh 装好的依赖(dev/macos/deps/)不受影响,无需重装 Qt。

故障排查

黑屏 / GPU 问题

文档给出的第一手段是强制软件渲染:

dev/macos/run.sh --software-rendering

通过 run.sh 的参数透传机制,该选项会到达应用进程。从 src/main.cpp 的命令行解析看,应用还提供了同类治理选项:--disable-gpu(禁用 QtWebEngine 的 GPU 加速)、--scale-factor <scale>(控制界面 DPI 缩放,默认 auto)、--platform <platform>(等价于 QT_QPA_PLATFORM)。另外主程序在启动早期会调用 detectOpenGLEarly()/detectOpenGLLate() 并显式设置 QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)(见 main.cpp),说明 OpenGL 检测是启动流程的固定环节,遇到黑屏优先尝试上述软件渲染开关是符合源码设计的排查路径。

日志文件位置

运行日志写入:

~/Library/Logs/Jellyfin Desktop/

对应代码中 Log::Init() 与 Log::RotateLog() 的日志体系(见 src/utils/Log.cpp)。需要排查崩溃或播放问题时,优先查看该目录下的日志文件。

平台要求与注意事项

文档 Notes 部分明确了三条硬性约束:

  • Intel 构建要求 macOS 12+
  • Apple Silicon 构建要求 macOS 14+
  • Qt 6.10+ 为 WebEngine 支持所必需——本项目锁定的正是 Qt 6.10.1,且 setup.sh 单独安装了 qtwebengine 模块,与应用界面依赖 QtWebEngine 的架构一致(src/ui/webview.qml 为主界面载体)。

此外还有两个 macOS 专属细节值得注意:其一,发布构建(NDEBUG)下,main.cpp 会调用 PFMoveToApplicationsFolderIfNecessary()(来自 external/letsmove),提示用户把应用移动到「应用程序」文件夹,这是 macOS 分发场景的常见惯例;其二,macOS 上不会显式设置窗口图标(见 main.cpp 的注释:在 OSX 上设置窗口图标会破坏用户更换图标的能力)。

小结

至此,一套完整的 macOS 构建链路已经清晰:setup.sh 一次性装好 Xcode CLT/Homebrew/CMake/Ninja/aqtinstall/create-dmg/mpv 与 Qt 6.10.1;build.sh 用 CMake+Ninja 产出开发版 .app;run.sh 借助 DYLD_FRAMEWORK_PATH 等环境变量免打包运行;test.sh 经 CTest 跑单元测试;bundle.sh 则负责 macdeployqt、临时签名与 create-dmg 产出可分发的 DMG。每一步都可以在上述脚本与 CMake 源码中找到一一对应的实现依据,照着这份流程即可在 macOS 上完成 Jellyfin Desktop 的开发、调试与发布。

登录后查看全文
jellyfin-desktop