Windows版Qt - 部署

本文档描述了Windows的部署过程。我们通过文档中的Plug & Paint示例应用程序来说明部署过程。

注意:请将您的Qt构建目录添加到任何在您的系统上运行的防病毒应用程序的排除目录列表中。

Windows部署工具

Windows部署工具windeployqt旨在自动化创建一个包含所需Qt-相关依赖项(库、QML导入、插件和翻译)的文件夹,这些依赖项用于从该文件夹运行应用程序。它为Windows桌面应用程序创建安装树,可以轻松地捆绑成安装包。

工具位于QTDIR/bin/windeployqt。必须在构建环境内运行以确保其正常工作。当使用Qt在线安装程序时,应使用脚本QTDIR/bin/qtenv2.bat来设置。

windeployqt将一个.exe文件或包含一个.exe文件的目录作为参数,并扫描可执行文件中的依赖项。如果携带了带有--qmldir参数的目录,windeployqt将使用qmlimportscanner工具扫描目录内的QML文件以查找QML导入依赖项。然后,这些已识别的依赖项将被复制到可执行文件的目录中。

如果Qt是在没有配置开关-relocatable的情况下构建的,windeployqt将用相对路径替换Qt6Core.dll中的硬编码本地路径。

对于Windows桌面应用程序,默认情况下还会将编译器所需的运行时文件复制到部署文件夹中(除非指定了--no-compiler-runtime选项)。在Microsoft Visual C++发布的构建中,这些由Visual C++ Redistributable Packages组成,它旨在由目标机器上的应用程序安装程序递归安装。否则,使用编译器运行时的共享库。

应用程序可能还需要其他第三方库(例如,数据库库),这些依赖于windeployqt。

更多信息请参阅工具的帮助输出

Usage: windeployqt [options] [files]
Qt Deploy Tool 6.0.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, 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 help on commandline options.
  --help-all                  Displays help including Qt specific options.
  -v, --version               Displays version information.
  --dir <directory>           Use directory instead of binary directory.
  --qmake <path>              Use specified qmake instead of qmake from PATH.
  --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 Qt6Core library.
  --ignore-library-errors     Ignore errors when libraries cannot be found.
  --no-plugins                Skip plugin deployment.
  --no-libraries              Skip library deployment.
  --qmldir <directory>        Scan for QML-imports starting from directory.
  --qmlimport <directory>     Add the given path to the QML module search
                              locations.
  --no-quick-import           Skip deployment of Qt Quick imports.
  --translations <languages>  A comma-separated list of languages to deploy
                              (de,fi).
  --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-virtualkeyboard        Disable deployment of the Virtual Keyboard.
  --no-compiler-runtime       Do not deploy compiler runtime (Desktop only).
  --json                      Print to stdout in JSON format.
  --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 (0-2).

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 gui qthelp
multimedia multimediawidgets multimediaquick network nfc opengl openglwidgets
positioning printsupport qml qmltooling quick quickparticles quickwidgets script
scripttools sensors serialport sql svg svgwidgets test websockets widgets xml
webenginecore webengine webenginewidgets 3dcore 3drenderer 3dquick
3dquickrenderer 3dinput 3danimation 3dextras geoservices webchannel serialbus
webview

Arguments:
  [files]                     Binaries or directory containing the binary.

静态链接

要构建静态应用程序,请使用-static配置静态构建Qt

cd C:\path\to\Qt
configure -static <any other options you need>

如果您后来需要从同一位置重新配置和重建Qt,确保在再次运行configure之前删除上一个配置的所有痕迹,通过在构建目录中运行nmake distcleanmingw32-make distclean

将应用程序链接到Qt的静态版本

例如,本节将构建Plug & Paint示例的静态版本。

Qt构建完成后,构建Plug & Paint应用程序。首先,我们必须进入包含应用程序的目录

cd examples\tools\plugandpaint

运行qmake为应用程序创建一个新的makefile,并执行清洁构建以创建静态链接的可执行文件

nmake clean
qmake -config release
nmake

你可能希望链接到发布库,你可以在调用qmake时指定这一点。现在,假设一切编译和链接均无错误,我们应该有一个plugandpaint.exe文件,该文件已准备好部署。为了检查应用程序是否具有所需的库,将可执行文件复制到没有安装Qt或任何Qt应用程序的机器上,并在该机器上运行它。

记住,如果你的应用程序依赖于编译器特定的库,这些库必须与你的应用程序一起重新分发。你可以使用depends工具来检查应用程序链接的库。更多信息,阅读应用程序依赖部分。

由于我们无法使用静态链接方法部署插件,我们准备的应用程序是不完整的。它会运行,但由于缺少插件,功能将禁用。要部署基于插件的应用程序,我们应该使用共享库方法。

共享库

当我们使用共享库方法部署plugandpaint应用程序时,我们面临着两个挑战:Qt运行时必须与应用程序的可执行文件一起正确重新分发,插件必须安装在目标系统上的正确位置,以便应用程序可以找到它们。

构建Qt作为共享库

在此示例中,我们假设Qt已作为共享库安装,这是安装Qt时的默认设置,位于C:\path\to\Qt目录。

将应用程序链接到Qt作为共享库

确保Qt作为共享库构建后,我们可以构建plugandpaint应用程序。首先,我们必须进入包含应用程序的目录

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.dllpnp_extrafilters.dll插件文件。

创建应用程序包

为了部署应用程序,我们必须确保将相关的Qt DLL(对应于应用程序中使用的Qt模块)和Windows平台插件qwindows.dll以及可执行文件复制到release子目录中的相同目录树。

与用户插件不同,Qt插件必须放入与插件类型匹配的子目录中。平台插件的正确位置是名为platforms的子目录。Qt插件部分有关于插件和Qt如何查找它们的更多信息。

如果使用动态OpenGL,你还可能想包含所需的基于软件的OpenGL库,如果应用程序与其兼容。

如果Qt配置为链接到ICU或OpenSSL,则需要将相应的DLL添加到release文件夹中。但是,Windows上的Qt的二进制包确实需要这样做。有关更多详细信息,请参阅第三方库

记住,如果您的应用程序依赖于特定编译器的库,这些库必须与您的应用程序一起重新分发。您可以使用depends工具检查您的应用程序链接了哪些库。更多信息,请参阅应用程序依赖项部分。

我们将稍后介绍插件,但首先我们要检查应用程序能否在已部署的环境中运行:要么把可执行文件和Qt DLLs复制到没有安装Qt或任何Qt应用程序的机器上,要么如果您想在构建机器上测试,请确保该机器的环境中没有Qt。

如果应用程序能够无任何问题启动,那么我们就成功地创建了一个plugandpaint应用程序的动态链接版本。但是,由于我们还没有部署相关的插件,应用程序的功能仍然缺失。

插件的工作方式与正常的DLL不同,所以我们不能像操作Qt DLLs那样直接将其复制到与应用程序可执行文件相同的目录中。查找插件时,应用程序会搜索应用程序可执行文件目录内的plugins子目录。

因此,为了使插件可供我们的应用程序使用,我们必须创建plugins子目录并将相关的DLL复制过去。

plugins\pnp_basictools.dll
plugins\pnp_extrafilters.dll

一个分发所有Qt DLLs和运行Plug & Paint应用程序所需的应用程序特定插件的存档,必须包含以下文件

组件文件名
可执行文件plugandpaint.exe
基本工具插件plugins\pnp_basictools.dll
额外过滤器插件plugins\pnp_extrafilters.dll
Qt Windows平台插件platforms\qwindows.dll
Qt Windows Vista样式插件styles\qwindowsvistastyle.dll
Qt Core模块Qt6Core.dll
Qt GUI模块Qt6Gui.dll
Qt Widgets模块Qt6Widgets.dll

根据应用程序使用的功能可能还需要其他插件(iconenginesimageformats)。

此外,存档必须包含以下特定编译器的库(假设使用Visual Studio 16(2019))

组件文件名
C运行时vcruntime140.dll
C++运行时msvcp160.dll

如果使用了动态OpenGL,则存档可能还包含

组件文件名
OpenGL软件渲染器库opengl32sw.dll

最后,如果Qt被配置为使用ICU,则存档必须包含

文件名
icudtXX.dllicuinXX.dllicuucXX.dll

要验证应用程序是否可以成功部署,您可以在没有Qt和没有编译器的机器上解压缩此存档,并尝试运行它。

将插件放在plugins子目录中的另一种方法是在使用QCoreApplication::addLibraryPath()或QCoreApplication::setLibraryPaths()开始应用程序时添加自定义搜索路径。

QCoreApplication::addLibraryPath("C:/some/other/path");

使用插件的一个好处是,它们可以轻松地提供给一组应用程序。

通常最方便的做法是在创建QApplication对象后,立即在应用程序的main()函数中添加路径。一旦添加了路径,应用程序将在这个路径上搜索插件,而不是在应用程序自己的目录中的plugins子目录中搜索。可以添加任意数量的附加路径。

清单文件

当使用Visual Studio编译应用程序时,有一些额外的步骤需要执行。

首先,我们需要复制创建链接时生成的清单文件。这个清单文件包含关于应用程序对侧边集合并行库的依赖信息,比如运行时库。

将清单文件复制到与应用程序可执行文件相同的文件夹中。您不需要复制共享库(DLL)的清单文件,因为它们不使用。

如果共享库有与应用程序使用的库不同的依赖项,清单文件需要嵌入到DLL二进制文件中。以下是可用于嵌入清单的CONFIG选项

embed_manifest_dll
embed_manifest_exe

默认情况下,这两个选项都启用。要移除embed_manifest_exe,请将以下内容添加到您的.pro文件中。

CONFIG -= embed_manifest_exe

添加到您的

您可以在并置组装文档页面中找到有关清单文件和并置组装的更多信息。

将运行时库与应用程序一起包含的正确方式是确保它们已经安装在终端用户系统上。

要在终端用户系统上安装运行时库,您需要在您的应用程序中包含适当的Visual C++ Redistributable Package (VCRedist)可执行文件,并确保它在用户安装您的应用程序时执行。

可 redistributable 命名为 vc_redist.x64.exe (64位) ,位于文件夹 <Visual Studio 安装路径>/VC/redist/<语言代码> 中。

或者,可以从网络上下载,例如 https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads

注意:您分发的应用程序必须使用与相同版本的编译器针对相同的C运行时版本编译。这可以防止由不同版本的C运行时库造成的部署错误。

应用程序依赖项

附加库

根据配置,特定的编译器库必须与您的应用程序一起重新分发。

您可以使用Dependency Walker工具检查您的应用程序链接到的库。您只需像这样运行它

depends <application executable>

这将提供一个列出您的应用程序所依赖的库及其他信息的列表。

当使用depends工具查看Plug & Paint可执行文件的发布构建(plugandpaint.exe)时,工具列出以下非系统库的即时依赖项。

QtVisual Studio 16 (2019)MinGW
  • QT6CORE.DLL - QtCore 运行时
  • QT6GUI.DLL - QtGui 运行时
  • QT6WIDGETS.DLL - QtWidgets 运行时
  • VCCORLIB140.DLL, VCRUNTIME140D.DLL - C 运行时
  • MSVCP140.DLL - C++ 运行时
  • LIBWINPTHREAD-1.DLL
  • LIBGCC_S_SEH-1.DLL
  • LIBSTDC++-6.DLL

查看插件DLL时,列出相同的依赖项。

Qt 插件

所有Qt GUI应用程序都需要一个实现Qt中的Qt 平台抽象(QPA)层的插件。对于Windows,平台插件名为qwindows.dll。此文件必须位于您的分发目录下的特定子目录中(默认情况下为platforms)。或者,可以调整Qt用于查找其插件的重试路径,如下所述。

您的应用程序也可能依赖于一个或多个 Qt 插件,例如打印支持插件、JPEG 图片格式插件或 SQL 驱动程序插件。请确保将您应用程序所需的任何 Qt 插件与您的应用程序一起分发。类似于平台插件,每种类型的插件必须在您的分发目录中的特定子目录(例如 printsupportimageformatssqldrivers)内。

除非 Qt 使用配置开关 -relocatable 关闭了重定位,否则这些库是可以重定位的。Qt 插件的搜索路径相对于 QtCore 库的位置,无需进一步步骤,即可确保在将应用程序安装到目标机器后能够找到插件。

确保在使用非重定位构建时找到插件

对于非重定位构建,必须在应用程序安装到目标机器后采取额外步骤以确保找到插件。

在这种情况下,Qt 插件的搜索路径被硬编码到 QtCore 库中。默认情况下,Qt 安装下的插件子目录是第一个插件搜索路径。然而,像默认路径这样的预定义路径有一定的缺点,例如,它们可能不在目标机器上存在。因此,您需要检查各种备选方案以确保可以找到 Qt 插件。

如果您使用 QApplication::addLibraryPath 添加自定义路径,则它可能看起来像这样

QCoreApplication::addLibraryPath("C:/customPath/plugins");

然后 QCoreApplication::libraryPaths() 将返回类似以下内容

  • C:/customPath/plugins
  • C:/Qt/%VERSION%/plugins
  • E:/myApplication/directory

可执行文件将在这些目录中查找插件,顺序与 QStringList 返回的顺序相同,该列表由 QCoreApplication::libraryPaths() 返回。新添加的路径被添加到 QCoreApplication::libraryPaths() 的前面,这意味着它将首先被搜索。然而,如果您使用 QCoreApplication::setLibraryPaths(),您将能够确定搜索的路径和顺序。

如何创建 Qt 插件 文档概述了在为 Qt 应用程序构建和部署插件时需要关注的问题。

© 2024 The Qt Company Ltd. 包含在本内的文档贡献是各自所有者的版权。所述文档是根据自由软件基金会发布的 GNU Free Documentation License 版本 1.3 的条款分发的。Qt 和相应标志是 The Qt Company Ltd. 在芬兰和/或其他国家的商标。所有其他商标均为各自所有者的财产。