Qt for Linux/X11 - 部署

本技术文档讨论了针对 Qt for Linux/X11 的特定部署问题。我们将通过部署 Qt 源代码包中的 Plug & amp; Paint 示例应用程序来说明这些流程。

由于 Unix 系统(如商业 Unix、Linux 发行版等)的普及,Unix 系统的部署是一个复杂的话题。在我们开始之前,请注意,为一种 Unix 版本编译的程序可能在不同的 Unix 系统上无法运行。例如,除非您使用交叉编译器,否则您不能在 Irix 上编译您的应用程序并分发到 AIX 上。

静态链接

静态链接通常是 Unix 系统上分发应用程序最安全、最简单的方法,因为它让您免去了分发 Qt 库并确保它们位于目标系统库默认搜索路径的任务。

静态构建 Qt

为了使用这种方法,您必须首先构建 Qt 库的 _静态_ 版本。按照 Qt for Linux/X11 - 从源码构建 页面上的步骤进行,但请记住,在配置时添加 -static 参数。

mkdir -p ~/dev/qt-build
cd ~/dev/qt-build
/tmp/qt-everywhere-src-6.7.2/configure -static

将应用程序链接到 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 工具(大多数 Unix 系统上都可用)

ldd ./application

请验证 Qt 库没有在输出中提到。

现在,假设编译和链接没有出错,我们应该有一个 plugandpaint 文件,它已准备好进行部署。一种简单的方法是检查应用程序是否真的可以独立运行,是将它复制到一个没有安装 Qt 或任何 Qt 应用程序的机器上,并在该机器上运行它。

请注意,如果您的应用程序依赖于特定编译器的库,这些库仍然需要与您的应用程序一起重新分发。有关更多信息,请参阅应用程序依赖关系部分。

Plug & Paint 示例由几个组件组成:核心应用程序(Plug & Paint),以及基本工具额外过滤器插件。由于我们无法使用静态链接方法部署插件,因此我们到目前为止准备的程序是不完整的。应用程序可以运行,但由于缺少插件,功能将无法启用。要部署基于插件的程序,我们应该使用共享库方法。

共享库

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

将 Qt 构建为共享库

我们假设您已经安装了位于 /path/to/Qt 目录下的 Qt 作为共享库,这是安装 Qt 的默认设置。

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

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

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.solibpnp_extrafilters.so 插件文件。

创建应用程序包

Unix 没有标准的包管理,所以我们下面介绍的方法是一个通用的解决方案。有关如何在您的目标系统上创建包的信息,请参阅该系统的文档。

要部署应用程序,我们必须确保将相关的 Qt 库(与应用程序中使用的 Qt 模块对应的 Qt 库)、平台插件和可执行文件复制到相同的目录树中。请记住,如果您的应用程序依赖于编译器特定的库,那么这些库也必须与您的应用程序一起重新分发。有关更多信息,请参阅应用程序依赖项部分。

我们将稍后介绍插件,但共享库的主要问题是必须确保动态链接器可以找到 Qt 库。除非另有说明,否则动态链接器不会搜索您的应用程序所在的目录。有许多方法可以解决这个问题

  • 您可以将 Qt 库安装在任何系统库路径中(例如,大多数系统中的 /usr/lib)。
  • 您可以在链接应用程序时传递一个预定的路径到 -rpath 命令行选项。这将告诉动态链接器在启动您的应用程序时查找此目录。
  • 您可以为应用程序编写一个启动脚本,其中修改动态链接器配置(例如,将您的应用程序的目录添加到 LD_LIBRARY_PATH 环境变量中。

    注意: 如果您的应用程序将使用“执行时设置用户ID”运行,并且如果它属于 root,则某些平台上将忽略 LD_LIBRARY_PATH。在这种情况下,使用 LD_LIBRARY_PATH 方法不是一种选择。

第一种方法的缺点是用户必须拥有超级用户权限。第二种方法的缺点是用户可能没有权限安装到指定的路径。在两种情况下,用户都没有将软件安装到个人目录的选项。我们建议使用第三种方法,因为它是最灵活的。例如,一个 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 子目录中搜索。您必须手动将插件复制到 plugins 目录中,或者可以在插件的工程文件中设置 DESTDIR

DESTDIR = /path/to/Qt/plugandpaint/plugins

分发所有必要的 Qt 库和插件,以便运行 plugandpaint 应用程序的存档必须包含以下文件

组件文件名
可执行文件plugandpaint
运行可执行文件的脚本plugandpaint.sh
基本工具插件plugins\libpnp_basictools.so
额外过滤器插件plugins\libpnp_extrafilters.so
Qt xcb 平台插件platforms\libqxcb.so
Qt 核心模块libQt6Core.so.6
Qt 图形用户界面模块libQt6Gui.so.6
Qt 小部件(Widgets)模块libQt6Widgets.so.6

在大多数系统中,共享库的扩展名是 .so。HP-UX 是一个显著的例外,它使用 .sl

请注意,如果您的应用程序依赖于特定编译器的库,这些库仍然需要与您的应用程序一起重新分发。有关更多信息,请参阅应用程序依赖关系部分。

为了验证应用程序现在能否成功部署,您可以在没有 Qt 和没有任何编译器的机器上解压缩此存档,并尝试运行它,即运行 plugandpaint.sh 脚本。

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

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

应用程序依赖项

附加库

为了找出应用程序依赖于哪些库,请运行 ldd 工具(大多数 Unix 系统上可用)

ldd ./application

这将列出应用程序的所有共享库依赖项。根据配置,这些库必须与应用程序一起重新分发。如果您使用与系统编译器二进制不兼容的编译器编译应用程序,则必须重新分发标准 C++ 库。如果可能,将库静态链接是安全的解决方案。

您可能希望与常规 X11 库进行动态链接,因为某些实现将尝试使用 dlopen() 打开其他共享库,如果这失败,X11 库可能会使您的应用程序崩溃。

还有一点值得提及,Qt 将寻找某些 X11 扩展,如 Xinerama 和 Xrandr,并可能将它们全部引入,包括它们链接的所有库。如果您不能保证某个特定扩展的存在,在配置 Qt 时禁用它是最安全的做法(例如,./configure -no-xrandr)。

FontConfig 和 FreeType 是其他库的例子,这些库不一定总是可用,或者不一定总是二进制兼容。尽管这听起来可能有些奇怪,但一些软件供应商通过在非常老的机器上编译他们的软件取得了成功,并且非常小心地不升级它们上运行的任何软件。

当你将应用程序与应用程序的静态 Qt 库链接时,你必须明确地将上述依赖库链接进去。通过将它们添加到项目文件中的 LIBS 变量中来实现。

Qt 插件

所有 Qt GUI 应用程序都需要一个实现 Qt 平台抽象(QPA)层的插件。对于 Linux/X11,平台插件的名称为 libqxcb.so。此文件必须位于你的发行版目录下的特定子目录中(默认为 platforms)。或者,你可以调整 Qt 用于查找其插件的路由,如下文所述。

你的应用程序也可能依赖于一个或多个 Qt 插件,例如 JPEG 图像格式插件或 SQL 驱动程序插件。确保将你需要的应用程序与其 Qt 插件一起分发。类似于平台插件,每种类型的插件都必须位于你的分发目录中的特定子目录(如 imageformatssqldrivers)内。

Qt 插件的路由(以及少数其他路由)是硬编码到 QtCore 库中的。默认情况下,第一个插件搜索路由将硬编码为 /path/to/Qt/plugins。如前所述,使用预定路径有某些缺点,因此你需要检查各种备选方案以确保 Qt 插件能被发现。

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

© 2024 Qt 公司有限公司。此处包含的文档贡献是该各自所有者的版权。提供的文档根据自由软件基金会出版的 GNU 自由文档许可证版本 1.3 的条款获得许可。Qt 及其相应标志是芬兰及其全球其他地区的 Qt 公司商标。所有其他商标均为其各自所有者的财产。