Qt for macOS - 部署
本文档介绍了如何创建macOS捆绑程序,并确保应用程序能在运行时找到所需的资源。我们将通过部署 Qt 源代码包中的Plug & Paint示例应用程序来演示相关步骤。
适用于 macOS 的 Qt 安装程序包含一个部署工具,可自动执行此处描述的程序。
软件包
在 macOS 上,GUI 应用程序必须通过捆绑包构建和运行,捆绑包是一个目录结构,在 Finder 中显示为一个单独的实体。应用程序的捆绑包通常包含可执行文件及其所需的所有资源。下面是应用程序捆绑包结构的快照:
捆绑包为用户提供了许多优势:
- 它易于安装,因为它被识别为一个单独的实体。
- 可从代码中获取有关捆绑包的信息。
这是 macOS 特有的功能,超出了本文档的讨论范围。有关捆绑包的更多信息,请参阅Apple 的开发者网站。
要使用 CMake 将应用程序作为捆绑包构建,请在可执行目标上设置 MACOSX_BUNDLE
属性:
set_target_properties(plugandpaint PROPERTIES MACOSX_BUNDLE TRUE )
qmake
自动为应用程序生成捆绑包。要禁用此功能,请在应用程序的项目文件 ( ) 中添加以下语句:.pro
CONFIG-=app_bundle
静态链接
如果您想保持简单,只需部署少量文件,您可以使用静态链接库构建应用程序。
静态构建 Qt
首先安装静态版本的 Qt 库。请记住,您不能使用插件,而且必须使用静态链接构建依赖库,如图像格式、SQL 驱动程序等。
cd /path/to/Qt ./configure -static <other parameters>
您可以运行configure
-help 查看各种可用选项。
将应用程序链接到静态版 Qt
静态构建 Qt 后,下一步就是重新生成构建文件并重建应用程序。
使用 CMake
确保使用qt_add_executable
wrapper 命令,它提供了额外的逻辑,如在静态 Qt 构建中链接 Qt 插件。
要为苹果平台构建程序,需要将cmake_minimum_required()
设置为 3.21.1 或更新版本:
cmake_minimum_required(VERSION 3.21.1)
进入包含应用程序的目录:
cd /path/to/Qt/examples/widgets/tools/plugandpaint/app
然后,设置CMAKE_PREFIX_PATH
变量,使其指向安装前缀。如果你已经创建了 Cmake,请删除CMakeCache.txt
文件。然后,重新运行 CMake:
cmake -DCMAKE_PREFIX_PATH=path/to/Qt/6.9.0/your_platform -S <source-dir> -B <build-dir> -G Ninja
或者使用方便的脚本qt-cmake
,它会为你设置CMAKE_PREFIX_PATH
变量。
path/to/Qt/6.9.0/your_platform/bin/qt-cmake -S <source-dir> -B <build-dir> -G Ninja
最后,进入你的编译目录,运行你喜欢的编译系统。在本例中,我们使用Ninja
。
cd path/to/build/dir ninja
现在,如果一切编译和链接都没有任何错误,您就应该有一个可以部署的plugandpaint.app
捆绑程序了。请尝试在运行 macOS 且未安装 Qt 或任何 Qt 应用程序的机器上安装捆绑包。
使用 qmake
首先,进入包含应用程序的目录:
cd /path/to/Qt/examples/widgets/tools/plugandpaint/app
然后,运行qmake
为应用程序创建一个新的 makefile,并进行一次干净的编译以创建静态链接的可执行文件:
make clean
qmake -config release
make
您可能希望与发行版库进行链接,可以在调用qmake
时指定这一点。您可能想利用 "死代码剥离 "来进一步缩小二进制文件的大小。为此,除了-config release
参数外,还可以将LIBS+= -dead_strip
传递给qmake
。
同样,只要编译和链接过程中没有出现任何错误,您就可以得到一个plugandpaint.app
的捆绑包供部署使用。请尝试在运行 macOS 的机器上安装该捆绑包,该机器未安装 Qt 或任何 Qt 应用程序。
检查链接的库
您可以使用otool
检查应用程序链接到了哪些其他库:
otool -L plugandpaint.app/Contents/MacOs/plugandpaint
以下是静态链接的Plug & Paint 的输出结果:
plugandpaint.app/Contents/MacOS/plugandpaint: /System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 128.0.0) /System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime (compatibility version 1.0.0, current version 10.0.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 22.0.0) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.3.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libmx.A.dylib (compatibility version 1.0.0, current version 92.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)
如果你在输出中看到Qt库,这可能意味着你的机器上同时安装了动态和静态 Qt 库。链接器总是选择动态链接而不是静态链接。如果你只想使用静态库,你可以
- 在链接应用程序时,将 Qt 动态库 (
.dylibs
) 移到另一个目录,然后再移回来、 - 或编辑
Makefile
,将 Qt XML 库的链接行替换为静态库的绝对路径。
例如,将以下内容替换为
-lQtGui
替换为
/where/static/qt/lib/is/libQtGui.a
Plug & Paint示例由多个组件组成:核心应用程序(Plug & Paint)以及基本工具和额外过滤器插件。由于我们不能使用静态链接方法部署插件,因此我们迄今为止准备的捆绑包是不完整的。应用程序可以运行,但由于缺少插件,其功能将被禁用。要部署基于插件的应用程序,我们应该使用 macOS 特有的框架方法。
框架
在这种方法中,要确保 Qt 运行时与应用程序捆绑包一起正确重新分发,并确保插件安装在正确的位置,以便应用程序找到它们。
在框架方法中,有两种方法将 Qt 与应用程序一起发布:
- 应用程序捆绑包中的私有框架。
- 标准框架(或者使用已安装二进制文件中的 Qt Installer Framework)。
如果您以特殊方式构建 Qt,或希望确保框架存在,那么第一种方法是不错的选择。只需确定 Qt 框架的位置即可。
如果你有许多 Qt 应用程序,并希望它们使用一个 Qt 框架而不是多个版本,那么第二种方案就很不错。
以框架形式构建 Qt
我们假设你已经在 /path/to/Qt 目录中安装了 Qt 作为框架,这是安装 Qt 时的默认设置。有关如何不使用框架构建 Qt的更多信息,请访问Qt for macOS - Specific Issues文档。
安装时,会设置框架的标识名称。动态链接器 (dyld
) 会使用该名称为应用程序查找库。
将应用程序链接到 Qt 框架
将 Qt 作为框架构建后,我们就可以构建Plug & Paint应用程序了。
使用 CMake
要为苹果平台构建应用程序,需要将cmake_minimum_required()
设置为 3.21.1 或更新版本:
cmake_minimum_required(VERSION 3.21.1)
进入包含应用程序的目录:
cd /path/to/Qt/examples/widgets/tools/plugandpaint/app
然后,设置CMAKE_PREFIX_PATH
变量,使其指向安装前缀。如果你已经创建了 Cmake,请删除CMakeCache.txt
文件。然后,重新运行 CMake:
cmake -DCMAKE_PREFIX_PATH=path/to/Qt/6.9.0/your_platform -S <source-dir> -B <build-dir> -G Ninja
或者使用方便的脚本qt-cmake
,它会为你设置CMAKE_PREFIX_PATH
变量。
path/to/Qt/6.9.0/your_platform/bin/qt-cmake -S <source-dir> -B <build-dir> -G Ninja
最后,进入你的编译目录,运行你喜欢的编译系统。在本例中,我们使用Ninja
。
cd path/to/build/dir ninja
现在,如果一切编译和链接都没有任何错误,您就应该有一个可以部署的plugandpaint.app
捆绑程序了。请尝试在运行 macOS 的机器上安装捆绑包,该机器未安装 Qt 或任何 Qt 应用程序。
使用 qmake
首先,进入包含应用程序的目录:
cd /path/to/Qt/examples/widgets/tools/plugandpaint/app
运行qmake
为应用程序创建一个新的 makefile,然后执行一次干净的编译以创建动态链接的可执行文件:
make clean
qmake -config release
make
这将构建核心应用程序。使用以下步骤构建插件:
cd ../plugandpaint/plugins make clean qmake -config release make
现在运行 Qt 框架的otool
,例如 Qt GUI:
您将得到以下输出:
QtGui.framework/QtGui: /path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui (compatibility version 4.0.0, current version 4.0.1) /System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 128.0.0) /System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime (compatibility version 1.0.0, current version 10.0.0) /path/to/Qt/QtCore.framework/Versions/4.0/QtCore (compatibility version 4.0.0, current version 4.0.1) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 22.0.0) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.3.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libmx.A.dylib (compatibility version 1.0.0, current version 92.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)
对于 Qt 框架,第一行(即path/to/Qt/lib/QtGui.framework/Versions/4/QtGui (compatibility version 4.0.0, current version 4.0.1)
)成为框架的标识名称,动态链接器会使用该名称 (dyld
)。
但在部署应用程序时,用户可能没有在指定位置安装 Qt Installer Framework。因此,您必须在约定的位置提供框架,或者将框架存储在捆绑包中。无论您选择哪种解决方案,都必须确保框架为自己返回正确的标识名称,并且应用程序会查找这些名称。幸运的是,我们可以使用install_name_tool
命令行工具来控制这一点。
install_name_tool
有两种工作模式:-id
和-change
。-id
模式适用于库和框架,允许我们指定新的标识名称。我们使用-change
模式来更改应用程序中的路径。
让我们通过将 Qt 框架复制到 Plug & Paint 捆绑程序中来测试一下。查看otool
的捆绑包输出,我们可以看到必须将QtCore 和QtGui 框架都复制到捆绑包中。我们将假定我们在构建捆绑包的目录中。
mkdir plugandpaint.app/Contents/Frameworks cp -R /path/to/Qt/lib/QtCore.framework plugandpaint.app/Contents/Frameworks cp -R /path/to/Qt/lib/QtGui.framework plugandpaint.app/Contents/Frameworks
首先,我们在 bundle 中创建Frameworks
目录。这符合 macOS 应用程序惯例。然后,我们将框架复制到新目录中。由于框架包含符号链接,我们使用-R
选项。
install_name_tool -id @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore plugandpaint.app/Contents/Frameworks/QtCore.framework/Versions/4.0/QtCore install_name_tool -id @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui plugandpaint.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui
然后运行install_name_tool
,为框架设置标识名称。-id
后的第一个参数是新名称,第二个参数是我们要重命名的框架。文本@executable_path
是一个特殊的dyld
变量,它告诉dyld
开始查找可执行文件的位置。新名称指定这些框架位于Frameworks
目录的正下方。
install_name_tool -change path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore plugandpaint.app/Contents/MacOs/plugandpaint install_name_tool -change path/to/qt/lib/QtGui.framework/Versions/4.0/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui plugandpaint.app/Contents/MacOs/plugandpaint
现在,动态链接器知道在哪里可以找到QtCore 和QtGui 。我们必须确保应用程序也能通过install_name_tool
的-change
模式找到该库。这基本上就是字符串替换,将我们之前设置的标识名称与框架相匹配。
最后,QtGui 框架依赖于QtCore ,因此我们必须记住更改QtGui 的引用:
install_name_tool -change path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore plugandpaint.app/Contents/Frameworks/QtGui.framework/Versions/4.0/QtGui
之后,我们再次运行otool
,发现应用程序可以找到这些库。
Plug & Paint示例的插件非常有趣。使用插件的基本步骤如下
- 将插件放入捆绑包、
- 运行
install_name_tool
检查插件是否使用了正确的库、 - 并确保应用程序知道在哪里查找插件。
我们可以将插件放在捆绑包中的任何位置,但最佳位置是放在 Contents/Plugins 下。当我们构建 Plug & Paint 插件时,根据其.pro
文件中的DESTDIR
变量,插件的.dylib
文件位于plugandpaint
目录下的plugins
子目录中。我们只需将该目录移到正确的位置即可。
mv plugins plugandpaint.app/Contents
例如,如果我们在Basic Tools插件的.dylib
文件上运行otool
,就会得到以下信息。
libpnp_basictools.dylib: libpnp_basictools.dylib (compatibility version 0.0.0, current version 0.0.0) /path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui (compatibility version 4.0.0, current version 4.0.1) /System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 128.0.0) /System/Library/Frameworks/QuickTime.framework/Versions/A/QuickTime (compatibility version 1.0.0, current version 10.0.0) /path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore (compatibility version 4.0.0, current version 4.0.1) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 22.0.0) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.3.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libmx.A.dylib (compatibility version 1.0.0, current version 92.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)
然后我们可以看到,该插件链接到了它所构建的 Qt 框架。由于我们希望插件使用应用程序捆绑包中的框架,因此我们要以与应用程序相同的方式更改它们。例如 Basic Tools 插件:
install_name_tool -change /path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore plugandpaint.app/Contents/plugins/libpnp_basictools.dylib install_name_tool -change /path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui plugandpaint.app/Contents/plugins/libpnp_basictools.dylib
我们还必须将tools/plugandpaint/mainwindow.cpp
中的代码修改为cdUp() 以确保应用程序能找到插件。在mainwindow.cpp
文件中添加以下代码:
#elif defined(Q_OS_MAC) if (pluginsDir.dirName() == "MacOS") { pluginsDir.cdUp(); } #endif
![]() | tools/plugandpaint/mainwindow.cpp 中的附加代码还能让我们在 Finder 中查看插件,如图所示。我们还可以添加扩展 Qt 的插件,例如添加 SQL 驱动程序或图像格式。我们只需遵循插件文档中列出的目录结构,并确保它们包含在QCoreApplication::libraryPaths() 中。让我们按照前面概述的步骤,快速完成图像格式的添加。 将 Qt Image Formats 插件复制到捆绑包中: cp -R /path/to/Qt/plugins/imageformats pluginandpaint.app/Contents/plugins 使用 install_name_tool -change /path/to/Qt/lib/QtGui.framework/Versions/4.0/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/4.0/QtGui plugandpaint.app/Contents/plugins/imageformats/libqjpeg.dylib install_name_tool -change /path/to/Qt/lib/QtCore.framework/Versions/4.0/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/4.0/QtCore plugandpaint.app/Contents/plugins/imageformats/libqjpeg.dylib 更新 QDir dir(QCoreApplication::applicationDirPath()); dir.cdUp(); dir.cd("plugins"); QCoreApplication::setLibraryPaths(QStringList(dir.absolutePath())); 首先,我们告诉应用程序只查找该目录中的插件。在我们的例子中,我们希望应用程序只查找我们随捆绑包一起发布的插件。如果我们是更大 Qt 安装的一部分,我们可以使用QCoreApplication::addLibraryPath() 代替。 |
警告: 在部署插件时,我们会更改源代码,这将在应用程序重建时重置默认标识名称。因此,您必须重复使用install_name_tool
使应用程序链接到捆绑包中正确的 Qt XML 框架。
现在,你应该可以将应用程序移到另一台 macOS 机器上,在未安装 Qt 的情况下运行。或者,你也可以将捆绑包之外的框架移到另一个目录,看看应用程序是否还能运行。
如果你将框架存放在捆绑包之外的另一个位置,那么链接应用程序的技术也是类似的;你必须确保应用程序和框架一致同意在哪里寻找 Qt 库以及插件。
创建应用程序软件包
将应用程序静态链接或作为框架链接到 Qt 后,应用程序就可以发布了。有关详细信息,请参阅Apple Developer网站。
尽管在部署应用程序的过程中会遇到一些陷阱,但只要了解了各种问题,就能轻松创建让所有 macOS 用户都喜欢的软件包。
应用程序依赖性
Qt 插件
所有Qt GUI 应用程序都需要一个在 Qt 中实现Qt Platform Abstraction(QPA) 层的插件。对于 macOS,平台插件的名称是libqcocoa.dylib
。该文件必须位于发布目录下的特定子目录中(默认情况下为platforms
)。另外,也可以调整 Qt 查找插件的搜索路径,如下所述。
您的应用程序也可能依赖于一个或多个 Qt 插件,如 JPEG Image Formats 插件或 SQL 驱动程序插件。请务必将您需要的任何 Qt 插件与您的应用程序一起发布。与平台插件类似,每种插件都必须位于分发目录中的特定子目录(如imageformats
或sqldrivers
)中。
Qt 插件的搜索路径(以及其他一些路径)被硬编码到QtCore 库中。默认情况下,第一个插件搜索路径将被硬编码为/path/to/Qt/plugins
。但使用预设路径有一些缺点。例如,目标计算机上可能不存在这些路径。因此,您必须检查各种替代路径,以确保找到 Qt 插件:
- 使用
qt.conf
。这是推荐的方法,因为它提供了最大的灵活性。 - 使用QApplication::addLibraryPath() 或QApplication::setLibraryPaths() 。
- 使用第三方安装工具更改QtCore 库中的硬编码路径。
如何创建 Qt 插件》文档概述了为 Qt 应用程序构建和部署插件时需要注意的问题。
附加库
您可以使用otool
来检查应用程序正在链接哪些库。将应用程序路径作为参数运行该程序:
otool -L MyApp.app/Contents/MacOS/MyApp
编译器专用库很少需要与应用程序一起重新发布。不过,部署应用程序有多种方式,因为 Qt 可以在 macOS 上以多种方式配置、构建和安装。通常情况下,您的目标有助于决定如何部署应用程序。最后几节将介绍部署应用程序时必须注意的一些事项。
Mac 部署工具
Mac 部署工具可在 QTDIR/bin/macdeployqt 中找到。 它旨在自动创建一个可部署的应用程序捆绑包,其中包含作为私有框架的 Qt 库。
mac 部署工具还根据以下规则部署 Qt XML 插件(除非使用-no-plugins
选项):
- 始终部署平台插件。
- 不部署插件的调试版本。
- 不部署设计器插件。
- 始终部署图像格式插件,但 SVG 图像格式插件除外,该插件仅在应用程序使用了 Qt SVG模块时才部署。
- 始终部署图标引擎插件。
- 始终部署打印支持插件。
- 如果应用程序使用了 SQL 驱动程序插件,则会部署 SQL 驱动程序插件。 Qt SQL模块时,才会部署 SQL 驱动程序插件。
- 始终部署辅助功能插件。
- 始终部署样式插件。
重要: 如果选择不使用 Mac 部署工具,则必须确保部署包包含这些插件。
要在应用程序捆绑包中包含第三方库,请在创建捆绑包后手动将库复制到捆绑包中。
macdeployqt
支持以下选项:
选项 | 说明 |
---|---|
-verbose=<0-3> | 0 = 无输出,1 = 错误/警告(默认),2 = 正常,3 = 调试 |
-no-plugins | 跳过插件部署 |
-dmg | 创建 .dmg 磁盘映像 |
-no-strip | 不在二进制文件上运行 "剥离 |
-use-debug-libs | 使用框架和插件的调试版本进行部署(意味着-no-strip ) |
-executable=<path> | 让给定的可执行文件也使用已部署的框架 |
-qmldir=<path> | 部署指定路径中 .qml 文件使用的导入程序 |
-qmlimport=<path> | 将给定路径添加到 QML 导入搜索位置中 |
-always-overwrite | 复制文件,即使目标文件已经存在 |
-codesign=<ident> | 在所有可执行文件上以指定身份运行codesign |
-hardened-runtime | 代码签名时启用加固运行时 |
-timestamp | 代码签名时包含安全时间戳(需要互联网连接) |
-sign-for-notarization=<ident> | 激活公证所需的选项(需要互联网连接)。激活的选项有-hardened-runtime ,-timestamp 和-codesign=<ident> |
-appstore-compliant | 跳过部署使用私有 API 的组件 |
-libpath=<path> | 将给定路径添加到库搜索路径 |
-fs=<filesystem> | 设置 .dmg 磁盘映像使用的文件系统(默认为 HFS+) |
注意: macOS High Sierra 引入了新的 Apple 文件系统 (APFS)。旧版本的 macOS 无法读取使用 APFS 格式化的 .dmg 文件。默认情况下,macdeployqt
使用较旧的 HFS+ 文件系统,以兼容 Qt 目前支持的所有 macOS 版本。使用-fs
选项可指定不同的文件系统。
卷名称
使用-dmg
创建的磁盘镜像的卷名(在打开的.dmg
文件的窗口标题中显示的文本)基于运行macdeployqt
时应用程序的路径。例如,以下命令为Qt Quick 应用程序创建磁盘镜像:
macdeployqt /Users/foo/myapp-build/MyApp.app -qmldir=/Users/foo/myapp/qml -dmg
生成的卷名为
/Users/foo/myapp-build/MyApp.app
为确保卷名只包含应用程序名称,而不包含部署计算机上的路径,请在同一目录下运行macdeployqt
:
cd /Users/foo/myapp-build macdeployqt MyApp.app -qmldir=/Users/foo/myapp/qml -dmg
这样,生成的加密卷名称就会是
MyApp.app
应享权利
When
签署后, 将自动使用在应用程序捆绑包中 Contents/Resources/ 子目录下找到的第一个 .entitlements 文件(如果存在)。为防止出错,请确保该文件夹中最多有一个 .entitlements 文件。macdeployqt
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.