多读书多实践,勤思考善领悟

Qt应用windows和Linux打包发布

在对Qt应用程序的打包发布的时候,经过多方查阅资料和自身实践,本文将总结Qt程序在不同操作系统下的打包发布问题

Windows

通过windeployqt打包

Windows 部署工具windeployqt旨在自动化创建可部署文件夹的过程,该文件夹包含从该文件夹运行应用程序所需的Qt相关依赖项(库、QML 导入、插件和翻译)。

该工具可以在QTDIR/bin/windeployqt. 它需要在构建环境中运行才能正常运行。

windeployqt.exe文件或包含文件的目录.exe作为参数,并扫描可执行文件的依赖关系。如果使用--qmldir参数传递目录,则windeployqt使用该qmlimportscanner工具扫描目录内的 QML 文件以查找 QML 导入依赖项。然后将识别的依赖项复制到可执行文件的目录中。

如果 Qt 是在配置开关-relocatable关闭的情况下构建的,则将windeployqtQt5Core.dll 中的硬编码本地路径替换为相关路径。

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

应用程序可能需要额外的第 3 方库(例如,数据库库),windeployqt 没有考虑到这些库。

工具的帮助输出中描述了其他参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
用法:windeployqt [选项] [文件]
Qt 部署工具 5.14.1

使用windeployqt最简单的方法是添加你的Qt的bin目录
安装(例如 <QT_DIR\bin>)到 PATH 变量,然后运行:
windeployqt <path-to-app-binary>
如果ICU、ANGLE等不在bin目录下,则需要在PATH中
多变的。如果您的应用程序使用 Qt Quick,请运行:
windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>

选项:
-?, -h, --help 显示有关命令行选项的帮助。
--help-all 显示帮助,包括 Qt 特定选项。
-v, --version 显示版本信息。
--dir <directory> 使用目录而不是二进制目录。
--libdir <path> 将库复制到路径。
--plugindir <path> 将插件复制到路径。
--debug 假设调试二进制文件。
--release 假设发布二进制文件。
--pdb 部署 .pdb 文件 (MSVC)。
--force 强制更新文件。
--dry-run 模拟模式。表现正常,但不要
复制/更新任何文件。
--no-patchqt 不要修补 Qt5Core 库。
--no-plugins 跳过插件部署。
--no-libraries 跳过库部署。
--qmldir <directory> 从目录开始扫描 QML 导入。
--qmlimport <directory> 将给定的路径添加到 QML 模块搜索中
地点。
--no-quick-import 跳过 Qt Quick 导入的部署。
--no-translations 跳过翻译部署。
--no-system-d3d-compiler 跳过系统 D3D 编译器的部署。
--compiler-runtime 部署编译器运行时(仅限桌面)。
--no-virtualkeyboard 禁用虚拟键盘的部署。
--no-compiler-runtime 不部署编译器运行时(仅限桌面)。
--webkit2 部署 WebKit2(网络进程)。
--no-webkit2 跳过部署 WebKit2。
--json 以 JSON 格式打印到标准输出。
--angle 强制部署 ANGLE。
--no-angle 禁用 ANGLE 的部署。
--no-opengl-sw 不部署软件光栅化库。
--list <option> 仅打印复制文件的名称。
可用选项:
source:源文件的绝对路径
target:目标文件的绝对路径
相对:目标文件的路径,相对
到目标目录
映射:输出源和相对
目标,适用于
Appx 映射文件
--verbose <级别> 详细级别 (0-2)。

Qt 库可以通过传递它们的名称 (-xml) 添加或通过传递删除
以--no- (--no-xml) 开头的名称。可用库:
蓝牙并发核心声明式设计器设计器组件引擎
游戏手柄 gui qthelp 多媒体 多媒体小部件 多媒体快速网络 nfc
opengl 定位 printsupport qml qmltooling quick quickparticles quickwidgets
脚本脚本工具传感器串行端口 sql svg 测试 webkit webkitwidgets
websockets 小部件 winextras xml xmlpatterns webenginecore webengine
webenginewidgets 3dcore 3drenderer 3dquick 3dquickrenderer 3dinput 3danimation
3dextras geoservices webchannel texttospeech serialbus webview

参数:
[files] 二进制文件或包含二进制文件的目录。

Linux

共享库的主要问题是必须确保动态链接器会找到 Qt 库。除非另有说明,动态链接器不会搜索您的应用程序所在的目录。有很多方法可以解决这个问题:

  • 您可以在系统库路径之一(例如/usr/lib在大多数系统上)安装 Qt 库。

  • -rpath链接应用程序时,您可以将预定路径传递给命令行选项。这将告诉动态链接器在启动应用程序时查看此目录。

  • 您可以为应用程序编写启动脚本,在其中修改动态链接器配置(例如,将应用程序的目录添加到LD_LIBRARY_PATH环境变量中。

    注意:如果您的应用程序将使用“在执行时设置用户 ID”运行,并且它由 root 拥有,则 LD_LIBRARY_PATH 在某些平台上将被忽略。在这种情况下,不能使用 LD_LIBRARY_PATH 方法)。

通过shell脚本

1).首先执行第一种方法的第4步 (如新建目录名为 test,下面所有test都表示我们的打包目录)

2).在test中新建脚本copy.sh

1
2
3
4
5
6
7
8
9
#!/bin/bash
LibDir=$PWD"/lib" #获取lib目录路径
Target=$1
lib_array=($(ldd $Target | grep -o "/.*" | grep -o "/.*/[^[:space:]]*"))
$(mkdir $LibDir) #创建lib目录
for Variable in ${lib_array[@]} #循环所有依赖的动态库路径
do
cp "$Variable" $LibDir #拷贝到lib目录中
done

3).给脚本赋予权限

1
chmod 777 copy.sh

4).执行脚本,参数是可执行文件名

1
./copy.sh checkServer

5).将生成的lib目录中的所有文件移动到上层,与可执行文件同级,然后将lib目录删除

6).进入QT安装目录的/usr/local/Qt5.15.2/plugins/platforms 将copy.sh放到该目录下并执行

1
./copy.sh libqxcb.so

7). 将生成的platforms整个目录移动到test下,将lib中的所有文件放到test目录下(即test/platforms/lib* 移动到test),然后将test/platforms/的lib目录及copy.sh脚本文件删除

8).在test目录下编写.sh运行脚本,脚本名必须与可执行程序一致,比如这里叫checkServer.sh(因为如果目前将程序发送到其他电脑上,执行程序则因系统无法获取so库路径,将无法运行)

1
2
3
4
5
6
7
8
9
10
11
12
#!/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 "$@"

9).赋予运行脚本权限

1
Chmod 777 checkServer.sh

10).现在可以将test目录拷贝到其他主机上,通过./checkServer.sh来运行 (脚本作用是在运行可执行文件前,先将test目录中的依赖库路径加入到环境变量中,使系统知道这些库的存在)

rpath链接的方法

不需要编写.sh运行脚本,即不需要设置LD_LIBRARY_PATH。

查看libqxcb.so 的ldd 输出时,它会进入 lib 文件夹。

1
libQt5Core.so.5 => <*>/plugins/platforms/./../../lib/libQt5Core.so.5 (0x00007f5f8374a000)

因此目录结构应如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app
|-- lib
| |-- libQt5Core.so.5
| |-- libQt5Gui.so.5
| |-- libQt5DBus.so.5
| |-- libQt5XcbQpa.so.5
| |-- libicui18n.so.56
| |-- libicuuc.so.56
| `-- libicudata.so.56
|-- qt.conf
|-- app_exec
`-- plugins
`-- platforms
`-- libqxcb.so

在 project.pro 中为 lib 文件夹设置您的应用程序 rpath:

1
2
3
unix:!mac{  
QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN/lib\'"
}

最后,您需要为您的应用程序设置qt.conf以便能够找到插件(默认情况下从平台文件夹中查找):

1
2
3
4
[Paths]
Prefix=./
Libraries=lib
Plugins=plugins