Qt for Windows - Deployment
本文档描述了 Windows 的部署过程. 我们在整个文档中引用了 plug & paint 示例应用程序来演示部署过程.
The Windows Deployment Tool
Windows 部署工具 (windeployqt
) 旨在自动创建可部署文件夹的过程, 其中包含从该文件夹运行应用程序所需的 Qt 相关依赖项 (库, QML 导入, 差人, 翻译). 它为通用 Windows 平台 (UWP) 创建沙箱或 Windows 桌面应用程序的安装树, 可以轻松地将其捆绑到安装包中.
这个工具可以在 QTDIR/bin/windeployqt
中找到. 它需要在构建环境中运行才能正常运行. 使用 Qt Installer 时, 应使用脚本 QTDIR/bin/qtenv2.bat 设置.
windeployqt
将 .exe
文件的目录作为参数, 并扫描可执行文件的依赖项. 如果使用 --qmldir
参数传递目录, windeployqt
将使用 qmlimportscanner
工具扫描目录内的 QML 文件以获取 QML 导入依赖项。 然后将识别出的依赖项复制到可执行文件的目录中.
如果 Qt 是在配置开关 -relocatable 关闭的情况下构建的, windeployqt 将用相对路径替换 Qt5Core.dll 中的硬编码本地路径.
对于 Windows 桌面应用程序, 默认情况下编译器所需的运行时文件也会复制到可部署文件夹中 (除非指定了 --no-compiler-runtime
选项). 对于使用 Microsoft Visual C++ 的发行版本, 这些版本由 Visual C++ 可再发行组件包组成, 旨在由应用程序安装程序在目标计算机上递归安装. 否则, 将使用编译器运行时的共享库.
应用程序可能需要额外的第 3 方库 (例如数据库库), windeployqt 未考虑这些库.
工具的帮助输出中描述了其他参数:
Usage: windeployqt [options] [files] Qt Deploy Tool 5.9.0 The simplest way to use windeployqt is to add the bin directory of your Qt installation (e.g. <QT_DIR\bin>) to the PATH variable and then run: windeployqt <path-to-app-binary> If ICU, ANGLE, etc. are not in the bin directory, they need to be in the PATH variable. If your application uses Qt Quick, run: windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary> Options: -?, -h, --help Displays this help. -v, --version Displays version information. --dir <directory> Use directory instead of binary directory. --libdir <path> Copy libraries to path. --plugindir <path> Copy plugins to path. --debug Assume debug binaries. --release Assume release binaries. --pdb Deploy .pdb files (MSVC). --force Force updating files. --dry-run Simulation mode. Behave normally, but do not copy/update any files. --no-patchqt Do not patch the Qt5Core library. --no-plugins Skip plugin deployment. --no-libraries Skip library deployment. --qmldir <directory> Scan for QML-imports starting from directory. --no-quick-import Skip deployment of Qt Quick imports. --no-translations Skip deployment of translations. --no-system-d3d-compiler Skip deployment of the system D3D compiler. --compiler-runtime Deploy compiler runtime (Desktop only). --no-compiler-runtime Do not deploy compiler runtime (Desktop only). --webkit2 Deployment of WebKit2 (web process). --no-webkit2 Skip deployment of WebKit2. --json Print to stdout in JSON format. --angle Force deployment of ANGLE. --no-angle Disable deployment of ANGLE. --no-opengl-sw Do not deploy the software rasterizer library. --list <option> Print only the names of the files copied. Available options: source: absolute path of the source files target: absolute path of the target files relative: paths of the target files, relative to the target directory mapping: outputs the source and the relative target, suitable for use within an Appx mapping file --verbose <level> Verbose level. Qt libraries can be added by passing their name (-xml) or removed by passing the name prepended by --no- (--no-xml). Available libraries: bluetooth concurrent core declarative designer designercomponents enginio gui qthelp multimedia multimediawidgets multimediaquick network nfc opengl positioning printsupport qml qmltooling quick quickparticles quickwidgets script scripttools sensors serialport sql svg test webkit webkitwidgets websockets widgets winextras xml xmlpatterns webenginecore webengine webenginewidgets 3dcore 3drenderer 3dquick 3dquickrenderer 3dinput geoservices webchannel texttospeech serialbus Arguments: [files] Binaries or directory containing the binary.
Static Linking
要构建静态应用程序, 使用 -static
配置静态构建 Qt:
cd C:\path\to\Qt configure -static <any other options you need>
如果稍后需要从同一位置重新配置和重建 Qt, 确保在再次运行 configure
之前进入构建目录并运行 nmake distclean
或 mingw32-make distclean
删除先前配置的所有痕迹.
Linking the Application to the Static Version of Qt
作为示例, 本节将静态构建 Plug & Paint 示例.
Qt 完成构建后, 构建 Plug & Paint 应用程序. 首先我们必须进入包含应用程序的目录:
cd examples\tools\plugandpaint
运行 qmake
为应用程序创建一个新的 makefile, 并执行干净构建以创建静态链接的可执行文件:
nmake clean
qmake -config release
nmake
你可能想要链接到发布库, 并且可以在调用 qmake
时指定这一点. 现在, 只要编译和链接的所有内容都没有任何错误, 我们就应该有一个可供部署的 plugandpaint.exe
文件. 要检查应用程序是否具有所需的库, 请将可执行文件复制到未安装 Qt 或任何 Qt 应用程序的计算机, 然后在该计算机上运行它.
记住, 如果你的应用程序依赖于编译器特定的库, 则这些库仍必须与你的应用程序一起重新分发. 你可以使用 depends
工具检查你的应用程序正在链接哪些库. 详见 Application Dependencies.
由于我们无法使用静态链接方法部署插件, 因此我们准备的应用程序是不完整的. 它将运行, 但由于缺少插件, 该功能将被禁用. 要部署基于插件的应用程序, 我们应该使用共享库方法.
Shared Libraries
使用共享库方法部署 Plug & Paint 应用程序时, 我们面临两个挑战: Qt 运行时必须与应用程序可执行文件一起正确重新分发, 并且插件必须安装在目标系统上的正确位置, 以便应用程序能够正常运行, 可以找到他们.
Building Qt as a Shared Library
对于此示例, 我们假设 Qt 作为共享库安装, 这是安装 Qt 时的默认设置, 位于 C:\path\to\Qt 目录.
Linking the Application to Qt as a Shared Library
确保 Qt 构建为共享库后, 我们可以构建 Plug & Paint 应用程序. 首先, 我们必须进入包含应用程序的目录:
cd examples\tools\plugandpaint
现在, 运行 qmake
为应用程序创建一个新的 makefile, 并执行干净构建以创建动态链接的可执行文件:
nmake clean
qmake -config release
nmake
以下将构建插件:
cd ..\plugandpaint/plugins nmake clean qmake -config release nmake
如果所有内容都编译和链接没有任何错误, 我们将获得 plugandpaint.exe
可执行文件, pnp_basictools.dll
和 pnp_extrafilters.dll
插件文件.
Creating the Application Package
要部署应用程序, 我们必须确保将相关的 Qt DLL (对应于应用程序中使用的 Qt 模块) 和 Windows 平台插件 qwindows.dll
, 及可执行文件复制到同一目录中.
与用户插件相反, Qt 插件必须放入与插件类型匹配的子目录中. 平台插件的正确位置是名为 platforms
的子目录. Qt Plugins 提供了有关插件以及 Qt 如何搜索它们的附加信息.
如果使用动态 OpenGL, 你还需要包含 ANGLE 和软件渲染所需的库. 对于 ANGLE, 需要 Qt 的 lib 目录中的 libEGL.dll 和 libGLESv2.dll 以及 DirectX 中的 HLSL 编译器. HLSL 编译器库 d3dcompiler_XX.dll, 其中 XX 是 ANGLE (libGLESv2) 链接的版本号.
如果 Qt 配置为链接到 ICU 或 OpenSSL, 则还需要将相应的 DLL 添加到发布文件夹中.
注意: Qt WebEngine 应用程序有 部署 Qt WebEngine 应用程序 中列出的要求.
记住, 如果你的应用程序依赖于编译器特定的库, 则这些库必须与你的应用程序一起重新分发. 你可以使用 depends
工具检查你的应用程序正在链接哪些库. 详见 Application Dependencies.
我们将很快介绍这些插件, 但首先我们将检查应用程序是否可以在已部署的环境中运行: 将可执行文件和 Qt DLL 复制到未安装 Qt 或任何 Qt 应用程序的计算机, 或者如果你想要在构建机器上测试, 确保该机器的环境中没有 Qt.
如果应用程序启动没有任何问题, 那么我们就成功制作了 Plug & Paint 应用程序的动态链接版本. 但由于我们尚未部署关联的插件, 因此应用程序的功能仍然会丢失.
插件的工作方式与普通 DLL 不同, 因此我们不能像使用 Qt DLL 那样将它们复制到与应用程序可执行文件相同的目录中. 查找插件时, 应用程序会在应用程序可执行文件目录内的 plugins
子目录中搜索.
因此, 为了使插件可供我们的应用程序使用, 我们必须创建 plugins
子目录并复制相关的 DLL:
plugins\pnp_basictools.dll plugins\pnp_extrafilters.dll
发布运行 Plug & Paint 应用程序所需的所有 Qt DLL 和应用程序特定插件的存档必须包含以下文件:
Component | File Name | |
---|---|---|
The executable | plugandpaint.exe | |
The Basic Tools plugin | plugins\pnp_basictools.dll | |
The ExtraFilters plugin | plugins\pnp_extrafilters.dll | |
The Qt Windows platform plugin | platforms\qwindows.dll | |
The Qt Core module | Qt5Core.dll | |
The Qt GUI module | Qt5Gui.dll | |
The Qt Widgets module | Qt5Widgets.dll |
根据应用程序使用的功能 (图标引擎, 图像格式), 可能需要其他插件.
此外, 存档必须包含以下编译器特定库 (假设 Visual Studio 14.0 (2015) 或 15.0 (2017) 或 16.0 (2019)):
VC++ 12.0 (2013) | VC++ 14.0 (2015) | |
---|---|---|
The C run-time | msvcr120.dll | vccorlib140.dll , vcruntime140.dll |
The C++ run-time | msvcp120.dll | msvcp140.dll |
如果 Qt 使用 ICU, 发布包必须包含:
File Name | ||
---|---|---|
icudtXX.dll | icuinXX.dll | icuucXX.dll |
最后, 如果使用 ANGLE, 发布包必须包含:
File Name | |
---|---|
QtANGLE.dll | d3dcompiler_XX.dll |
要验证应用程序现在是否可以成功部署, 你可以在没有 Qt 且没有安装任何编译器的计算机上提取此存档, 并尝试运行它.
将插件放在插件子目录中的另一种方法是在调用 QCoreApplication::addLibraryPath() 或 QCoreApplication::setLibraryPaths() 启动应用程序时添加自定义搜索路径.
QCoreApplication::addLibraryPath("C:/some/other/path");
使用插件的一个好处是可以轻松地将它们提供给整个应用程序系列.
在创建 QApplication 对象之后, 在应用程序的 main()
函数中添加路径通常是最方便的. 添加路径后, 应用程序除了在所在目录的 plugins
子目录中查找之外, 还将在其中搜索插件. 可以添加任意数量的附加路径.
Manifest files
部署使用 Visual Studio 编译的应用程序时, 需要执行一些额外的步骤.
首先, 我们需要复制链接应用程序时创建的清单文件. 此清单文件包含有关应用程序对并行程序集 (例如运行时库) 的依赖关系的信息.
清单文件需要复制到与应用程序可执行文件相同的文件夹中. 你不需要复制共享库 (DLL) 的清单文件, 因为它们不被使用.
如果共享库具有与使用它的应用程序不同的依赖项, 则需要将清单文件嵌入到 DLL 二进制文件中. 从 Qt 4.1.3 开始, 下列 CONFIG
选项可用于嵌入清单:
embed_manifest_dll embed_manifest_exe
默认情况下, 两个选项是启用的. 若想移除 embed_manifest_exe
, 添加
CONFIG -= embed_manifest_exe
到你的 .pro 文件.
您可以在 MSDN 网站找到有关清单文件和并行程序集的更多信息.
将运行时库包含在应用程序中的正确方法是确保它们安装在最终用户的系统上.
要在最终用户的系统上安装运行时库, 你需要在应用程序中包含适当的 Visual C++ Redistributable Package (VCRedist) 可执行文件, 并确保在用户安装应用程序时执行它.
它们被称为 vcredist_x64.exe
(IA64 and 64-bit) 或 vcredist_x86.exe
(32-bit), 可以在目录 c{Visual Studio install path>/VC/redist/<language-code>} 找到.
或者, 你可以从官网下载, 例如 vcredist_x64.exe for Visual Studio 2015.
注意: 你发布的应用程序必须使用完全相同的编译器版本针对相同的 C 运行时版本编译. 这可以防止由不同版本的 C 运行时库引起的部署错误.
Application Dependencies
Additional Libraries
根据配置, 编译器特定的库必须与你的应用程序一起分发.
例如, 如果 Qt 是使用 ANGLE 构建的, 则其共享库和来自 DirectX 的 HLSL 编译器也将附带.
你可以使用 Dependency Walker 工具检查你的应用程序正在链接哪些库. 你需要做的就是像这样运行它:
depends <application executable>
这将提供你的应用程序所依赖的库的列表以及其他信息.
当使用 depends
工具查看 Plug & Paint 可执行文件 (plugandpaint.exe
) 时, 该工具列出了以下对非系统库的直接依赖项:
Qt | VC++ 12.0 (2013) | VC++ 14.0 (2015) | MinGW |
---|---|---|---|
|
|
|
当查看插件 DLL 时, 会列出完全相同的依赖项.
从 Qt 5.2 版本开始, OpenSSL 官方支持的版本为 1.0.0 或更高版本. >= 0.9.7 和 < 1.0.0 的版本可能有效, 但不保证有效.
Qt Plugins
所有 Qt GUI 应用程序都需要一个在 Qt 5 中实现 Qt Platform Abstraction (QPA) 层的插件. 对于 Windows, 平台插件的名称是 qwindows.dll
. 该文件必须位于分发目录下的特定子目录 (默认情况下为 platforms
) 中. 或者, 可以调整 Qt 用于查找其插件的搜索路径, 如下所述.
你的应用程序还可能依赖于一个或多个 Qt 插件, 例如打印支持插件, JPEG 图像格式插件或 SQL 驱动程序插件. 请务必随应用程序分发你需要的所有 Qt 插件. 与平台插件类似, 每种类型的插件必须位于分发目录中的特定子目录 (如 printsupport
, imageformats
或 sqldrivers
) 中.
从 Qt 5.14 开始, 库是可重定位的, 除非 Qt 是在关闭配置开关 -relocatable 的情况下构建的. Qt 插件的搜索路径与 QtCore 库的位置相关, 无需进一步的步骤即可确保在目标计算机上安装应用程序后找到插件.
Ensuring Plugins Are Found when Using Non-Relocatable Builds
对于不可重定位的构建, 必须采取额外的步骤来确保在目标计算机上安装应用程序后找到插件.
在这种情况下, Qt 插件的搜索路径被硬编码到 QtCore 库中. 默认情况下, Qt安装的 plugins
子目录是第一个插件搜索路径. 然而, 像默认路径这样的预先确定的路径有一定的缺点. 例如, 它们可能不存在于目标计算机上. 因此, 你需要检查各种替代方案以确保找到 Qt 插件:
- 使用
qt.conf
. 如果你在不同位置有共享相同插件的可执行文件, 则建议使用此方法. - 使用 QApplication::addLibraryPath() 或 QApplication::setLibraryPaths(). 如果你只有一个将使用该插件的可执行文件, 则建议使用此方法.
- 使用第三方安装实用程序更改 QtCore 库中的硬编码路径.
如果你使用 QApplication::addLibraryPath 添加自定义路径, 它可能如下所示:
QCoreApplication::addLibraryPath("C:/customPath/plugins");
QCoreApplication::libraryPaths() 返回值可能如下:
C:/customPath/plugins
C:/Qt/%VERSION%/plugins
E:/myApplication/directory
可执行文件将在这些目录中查找插件, 顺序与 QCoreApplication::libraryPaths() 返回的 QStringList 相同. 新添加的路径被添加到 QCoreApplication::libraryPaths() 之前, 这意味着它将首先被搜索. 但是, 如果你使用 QCoreApplication::setLibraryPaths(), 你能自定义搜索哪些路径以及搜索顺序.
How to Create Qt Plugins 概述了为 Qt 应用程序构建和部署插件时需要注意的问题.