我们知道,在 Windows 中部署 Qt 应用程序时,通常会将可执行程序、相关的 Qt 库、以及平台插件放在同一目录中。但对于 Linux 而言,如果仅仅这样做是不够的,因为它通常不会搜索程序所在路径(默认搜索的是 /lib 和 /usr/lib),以至于无法找到程序的依赖库。为了指定动态库的查找路径,我们可以采取以下方式:
将动态库安装在系统库路径中(例如:/usr/lib)
缺点:用户必须具有超级用户权限,而且会污染系统库路径。
在链接应用程序时,可以将一个预定的路径传递给 -rpath 选项,这会告诉动态链接器在启动程序时查看此目录。
缺点:用户可能没有权限安装到预定的路径中。
为应用程序编写一个启动脚本,在其中修改动态链接器的配置(例如:将库目录添加到 LD_LIBRARY_PATH 环境变量中)。
推荐该方式,因为它是最灵活的。
在有了这些了解之后,Linux 下的打包就会简单很多。另外,由于 Linux 上没有标准的包管理,因此我们下面提供一种通用的解决方案。
1
编译程序
无论是哪个平台(Window/Linux/MacOS),要记得我们最终发布的程序一定是 Release 版本的。
在 Qt Creator 中选择 Release 模式,然后编译程序:
如果编译成功,将会生成一个可执行文件(例如:MyApp)。
为了便于后期发布,新建一个目录(例如:deploy),并将可执行文件拷贝进去。
2
拷贝依赖库
进入上述新建的目录,然后使用 ldd MyApp 命令查看程序的依赖库。如果要将这些库导出,可以编写一个脚本(例如:pack.sh):
#!/bin/sh
exe="MyApp" #可执行文件名称
des="/home/waleon/workspace/deploy" #依赖库被拷贝的最终位置
deplist=$(ldd $exe | awk '{if (match($3,"/")){ printf("%s "),$3 } }')
cp $deplist $des
接下来,为该脚本赋予可执行权限并运行:
$ chmod u+x pack.sh
$ ./pack.sh
稍等片刻,依赖库就会被自动拷贝到目标目录中:
注意:如果程序依赖于编译器特定的库,那么这些库也必须包含进来。
3
拷贝依赖插件
对于所有的 Qt GUI 程序来说,都需要一个实现 Qt 平台抽象(QPA)层的插件。对于 Linux/X11 来说,平台插件的名称是 libqxcb.so。所以,需要将该插件拷贝进来。
当然,如果程序还依赖于其他 Qt 插件(例如:JPEG 图像格式插件或 SQL 驱动程序插件),也需要将这些插件(例如:imageformats 或 sqldrivers)拷贝进来。
这些插件都位于 QTDIR/plugins 下,如下所示:
4
编写启动脚本
现在,是时候编写启动脚本了,建议脚本名称和可执行文件名称保持一致(即:MyApp.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 "$@"
通过运行此脚本(而不是可执行文件),就可以确保找到所依赖的动态库了。
5
测试程序
要验证程序能否成功部署,最好的办法是将其拷贝到没有安装任何 Qt 和编译器的机器上,并尝试运行它(即:MyApp.sh):
哎呀,出错了,提示 xcb 无法加载,查看一下缺失什么:
可以发现少了 libQt5XcbQpa.so.5 和 libQt5DBus.so.5,在编译机器上找到缺失的库,注意一下这两个文件是链接文件:
所以要找到对应的源文件,然后将其拷贝到部署目录下并进行重命名,再次运行:
问题完美解决,程序正常启动了。