Qt for Linux/X11 - Deployment
这篇文档讨论 Qt for Linux/X11 的部署事项. 我们将演示部署 Qt 示例目录中提供的 Plug & Paint 应用程序的过程.
由于 Unix 系统 (例如商业 Unix, Linux 发行版等) 的激增, Unix 上的部署是一个复杂的话题. 在开始之前, 请注意, 为一种 Unix 风格编译的程序可能无法在不同的 Unix 系统上运行. 例如, 除非使用交叉编译器, 否则无法在 Irix 上编译应用程序并在 AIX 上分发它.
Static Linking
静态链接通常是在 Unix 上分发应用程序的最安全, 最简单的方法, 因为它使你免于分发 Qt 库并确保它们位于目标系统上库的默认搜索路径中的任务.
Building Qt Statically
要使用此方法, 你必须首先安装 Qt 库的静态版本:
cd /path/to/Qt ./configure -static -prefix /path/to/Qt <other parameters> make
我们指定 prefix, 这样就不会覆盖现有的 Qt 安装. 上面的示例仅构建 Qt 库, 即. 不会构建示例和 Qt Designer. make
完成后, 你将在 /path/to/Qt/lib
目录找到 Qt 库.
将应用程序链接到静态 Qt 库时, 请注意, 你可能需要将更多库添加到项目文件中的 LIBS
行. 详见 Application Dependencies.
Linking the Application to the Static Version of Qt
静态构建 Qt 后, 下一步是重新生成 makefile 并重建应用程序. 首先, 我们必须进入包含应用程序的目录:
cd /path/to/Qt/examples/widgets/tools/plugandpaint/app
现在运行 qmake 为应用程序创建一个新的 makefile文件, 并执行干净构建以创建静态链接的可执行文件:
make clean PATH=/path/to/Qt/bin:$PATH export PATH qmake -config release make
你可能想要链接到发布库, 并且可以在调用 qmake
时指定这一点. 注意, 我们必须设置刚刚构建的静态 Qt 的路径.
要检查应用程序是否确实与 Qt 静态链接, 请运行 ldd
工具 (在大多数 Unices 上可用):
ldd ./application
检查输出中是否提及 Qt 库.
现在, 只要编译和链接的所有内容都没有任何错误, 我们就应该有一个可供部署的 plugandpaint
文件. 检查应用程序是否确实可以独立运行的一种简单方法是将其复制到未安装 Qt 或任何 Qt 应用程序的计算机, 并在该计算机上运行它.
记住, 如果你的应用程序依赖于编译器特定的库, 则这些库仍必须与你的应用程序一起重新分发. 详见 Application Dependencies.
Plug & Paint 示例由多个组件组成: 核心应用程序 (Plug & Paint), Basic Tools 和 Extra Filters 插件. 由于我们无法使用静态链接方法部署插件, 因此我们到目前为止准备的可执行文件是不完整的. 该应用程序将运行, 但由于缺少插件, 该功能将被禁用. 要部署基于插件的应用程序, 我们应该使用共享库方法.
Shared Libraries
使用共享库方法部署 Plug & Paint 应用程序时, 我们面临两个挑战: Qt 运行库必须与应用程序可执行文件一起正确重新分发, 并且插件必须安装在目标系统上的正确位置, 以便应用程序能够正常运行, 可以找到他们.
Building Qt as a Shared Library
我们假设你已经将 Qt 作为共享库安装, 这是安装 Qt 时的默认设置, 位于 /path/to/Qt
目录中.
Linking the Application to Qt as a Shared Library
确保 Qt 构建为共享库后, 我们可以构建 Plug & Paint 应用程序. 首先, 我们必须进入包含应用程序的目录:
cd /path/to/Qt/examples/tools/plugandpaint
现在运行 qmake 为应用程序创建一个新的 makefile, 并执行干净构建以创建动态链接的可执行文件:
make clean
qmake -config release
make
这将构建核心应用程序, 以下将构建插件:
cd ../plugandpaint/plugins make clean qmake -config release make
如果所有内容都编译和链接没有任何错误, 我们将得到一个 plugandpaint
可执行文件, libpnp_basictools.so
和 libpnp_extrafilters.so
插件文件.
Creating the Application Package
Unix 上没有标准的包管理, 因此我们下面介绍的方法是一个通用的解决方案. 有关如何创建包的信息, 请参阅目标系统的文档.
要部署应用程序, 我们必须确保将相关的 Qt 库 (对应于应用程序中使用的 Qt 模块), platform plugin, 和可执行文件复制到同一目录树. 记住, 如果你的应用程序依赖于编译器特定的库, 则这些库也必须与你的应用程序一起重新分发. 详见 Application Dependencies.
我们很快就会介绍这些插件, 但共享库的主要问题是你必须确保动态链接器能够找到 Qt 库. 除非另有说明, 否则动态链接器不会搜索应用程序所在的目录. 有很多方法可以解决这个问题:
- 你可以将 Qt 库安装在系统库路径之一 (如.
/usr/lib
多数系统的默认库路径). - 链接应用程序时, 你可以将预定路径传递给
-rpath
命令行选项. 这将告诉动态链接器在启动应用程序时查看此目录. - 你可以为应用程序编写启动脚本, 在其中修改动态链接器配置 (如., 将应用程序的目录添加到
LD_LIBRARY_PATH
环境变量中.注意: 如果你的应用程序将使用 "Set user ID on execution," 运行,并且由 root 拥有, 然后 LD_LIBRARY_PATH 在某些平台上将被忽略. 在这种情况下, 不能选择使用 LD_LIBRARY_PATH 方法).
第一种方法的缺点是用户必须具有超级用户权限. 第二种方法的缺点是用户可能没有权限安装到预定路径. 无论哪种情况, 用户都无法选择安装到其主目录. 我们建议使用第三种方法, 因为它是最灵活的. 例如, a plugandpaint.sh
脚本将如下所示:
#!/bin/sh appname=`basename $0 | sed s,\.sh$,,` dirname=`dirname $0` tmp="${dirname#?}" if [ "${dirname%$tmp}" != "/" ]; then dirname=$PWD/$dirname fi LD_LIBRARY_PATH=$dirname export LD_LIBRARY_PATH $dirname/$appname "$@"
通过运行此脚本而不是可执行文件, 你可以确定动态链接器将找到 Qt 库. 请注意, 你只需重命名该脚本即可将其与其他应用程序一起使用.
查找插件时, 应用程序会在可执行文件目录内的 plugins
子目录中搜索, 你必须手动将插件复制到插件目录中, 或者你可以在插件的配置文件中设置 DESTDIR
:
DESTDIR = /path/to/Qt/plugandpaint/plugins
分发运行 Plug & Paint 应用程序所需的所有 Qt 库和所有插件的依赖文件, 必须包含以下文件:
Component | File Name | |
---|---|---|
The executable | plugandpaint | |
The script to run the executable | plugandpaint.sh | |
The Basic Tools plugin | plugins\libpnp_basictools.so | |
The ExtraFilters plugin | plugins\libpnp_extrafilters.so | |
The Qt xcb platform plugin | platforms\libqxcb.so | |
The Qt Core module | libQt5Core.so.5 | |
The Qt GUI module | libQt5Gui.so.5 | |
The Qt Widgets module | libQt5Widgets.so.5 |
在大多数系统上, 共享库的扩展名是 .so
. 一个值得注意的例外是 HP-UX, 它使用 .sl
.
记住, 如果你的应用程序依赖于编译器特定的库, 则这些库仍必须与你的应用程序一起重新分发. 详见 Application Dependencies.
要验证应用程序现在是否可以成功部署, 你可以在没有 Qt 且没有安装任何编译器的计算机上提取此存档, 并尝试运行它, 即. 运行 plugandpaint.sh
脚本.
将插件放在 plugins
子目录中的另一种方法是在使用 QApplication::addLibraryPath() 或 QApplication::setLibraryPaths() 启动应用程序时添加自定义搜索路径.
QCoreApplication::addLibraryPath("/some/other/path");
Application Dependencies
Additional Libraries
要找出你的应用程序依赖哪些库, 运行 ldd
工具 (多数 Unices 上可用):
ldd ./application
这将列出你的应用程序的所有共享库依赖项. 根据配置, 这些库必须与您的应用程序一起重新分发. 特别是, 如果你使用与系统编译器二进制不兼容的编译器来编译应用程序, 则必须重新分发标准 C++ 库. 如果可能, 最安全的解决方案是静态链接这些库.
你可能希望与常规 X11 库动态链接, 因为某些实现将尝试使用 dlopen()
打开其他共享库, 如果失败, X11 库可能会导致您的应用程序崩溃.
还值得一提的是, Qt 将查找某些 X11 扩展, 例如 Xinerama 和 Xrandr, 并可能将它们引入, 包括它们链接的所有库. 如果你不能保证某个扩展的存在, 最安全的方法是在配置 Qt 时禁用它 (如. ./configure -no-xrandr
).
FontConfig 和 FreeType 并不总是可用或并不总是二进制兼容的库的其他示例. 听起来可能很奇怪, 一些软件供应商通过在非常旧的机器上编译他们的软件而取得了成功, 并且非常小心地不升级在这些机器上运行的任何软件.
将应用程序链接到静态 Qt 库时, 必须显式链接上述依赖库. 通过将它们添加到项目配置文件中的 LIBS
变量执行此操作.
从 Qt 5.2 版本开始, OpenSSL 官方支持的版本为 1.0.0 或更高版本. >= 0.9.7 和 < 1.0.0 的版本可能有效, 但不保证有效.
Qt Plugins
所有 Qt GUI 应用程序都需要一个在 Qt 5 中实现 Qt Platform Abstraction (QPA) 层的插件. 对于 Linux/X11, 平台插件的名称是 libqxcb.so
. 该文件必须位于分发目录下的特定子目录 (默认情况下是 platforms
). 或者, 可以调整 Qt 用于查找其插件的搜索路径, 如下所述.
你的应用程序还可能依赖于一个或多个 Qt 插件, 例如 JPEG 图像格式插件或 SQL 驱动程序插件. 请务必随应用程序分发你需要的所有 Qt 插件. 与平台插件类似, 每种类型的插件必须位于分发目录中的特定子目录 (如 imageformats
或 sqldrivers
).
Qt 插件的搜索路径 (以及一些其他路径) 被硬编码到 QtCore 库中. 默认情况下, 第一个插件搜索路径将被硬编码为 /path/to/Qt/plugins
. 如上所述, 使用预定路径有一定的缺点, 因此你需要检查各种替代方案以确保找到 Qt 插件:
- 使用
qt.conf
. 这是推荐的方法, 因为它提供了最大的灵活性. - 使用 QApplication::addLibraryPath() 或 QApplication::setLibraryPaths().
- 使用第三方安装实用程序或目标系统的包管理器来更改 QtCore 库中的硬编码路径.
How to Create Qt Plugins 概述了为 Qt 应用程序构建和部署插件时需要注意的问题.