最近在发布Qt应用时遇到了一些困难,Windows还好,在Linux上面发布遇到了不少的麻烦(实际Linux应该简单才对),经过在网络搜索发现帖子不少,但都比较片面,现把Qt应用程序在Linux&Windows打包部署总结如下。

核心

应用部署的核心是加载库,一个Qt应用程序至少包含以下库:

Windows

Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll

Linux

libQt5Core.so.5、libQt5Gui.so、libQt5Widgets.so

与其他应用程序一样,Qt应用程序也依赖于操作系统来加载这些库文件,这意味着它们必须放置在操作系统可以找到它们的位置。在Windows上,这意味着在.exe文件同一目录中或搜索路径中指定的目录(例如C: Windows System32)。将DLL复制到未安装Qt的另一台PC上时,我们通常不想弄乱其他计算机上的搜索路径,而是将DLL与.exe文件放在一起,这可能是最好的解决方案。

在Linux上,将.so文件放在可执行文件同一目录中不会像Windows中那样自动加载它们。但大多数Linux系统都预先安装好了Qt,例如在Ubuntu上,通常在/ usr / lib / i686-linux-gnu或/ usr / lib / x86_64-linux-gnu中安装了Qt,因此我们的应用仍然可以运行(但是请注意版本问题,Ubuntu提供的Qt可能太旧了,我们的应用无法启动)。

还有一点在部署Qt应用比较容易出错的是Qt的插件机制,除了上面提到的几个Qt核心库之外,程序想要运行还必须提供一些插件,例如:platforms、sqldrivers、imageformats,这些是插件目录,名字是Qt定的(当然可以修改,但是不建议修改)。其中最重要的是platforms插件目录,里面提供一些平台插件,所有Qt应用程序都需要这些库,对于Windows默认的是qwindows.dll,对于Linux,则为libqxcb.so。这是Qt的QPA(Qt Platform Abstraction层)它负责许多特定于OS的事情,例如将调用转换setWindowState(Qt::WindowMaximized)为Windows / Linux特定的系统调用。

插件设置

上面提到了一些Qt插件,这些插件Qt是如何加载的呢?默认Qt会在可执行文件所在的目录查找并加载这些插件。但是为了我们的程序目录更加简洁,可以自定义插件目录。

设置环境变量QT_PLUGIN_PATH:

使用环境变量可能是比较容易的选择,例如:

export QT_PLUGIN_PATH=plugins

Windows使用set

设置上面环境变量,QT将在pluginsplatforms寻找qwindows.dll。当然,也可以通过这种方式设置绝对路径。而且,如果想获得大量的插件目录,则可以附加以;分隔的其他路径。(在Linux中是:)。

在代码中指定插件路径:

#include "mainwindow.h"

#include

int main(int argc, char *argv[])

{

QApplication::addLibraryPath("plugins");//设置插件目录

QApplication a(argc, argv);

MainWindow w;

w.show();

return a.exec();

}

可以在代码中指定插件路径,但是由于Qt在构造QApplication时会加载插件,因此设置此项的机会就是在调用QApplication的构造函数之前进行。上述代码和设置环境变量效果一样。

创建一个qt.conf文件:

这是一种设置插件路径比较的流行方法。

[Paths]

Plugins=plugins

此文件与应用可执行文件位于同一目录中,Qt将读取该文件并将plugins= path路径添加到其插件目录列表中。上面的例子实现效果和之前的一样。

请注意,如果您使用的是Windows:该Plugins=设置中的反斜杠不起作用,应该使用Linux风格的正斜杠。

插件问题排查:

最后,如果仍然遇到插件加载问题,可以通过将环境变量QT_DEBUG_PLUGINS设置为非零值来打开插件加载过程的log

export QT_DEBUG_PLUGINS=1

然后从Terminal/CMD窗口启动应用程序。对于Linux,在终端中会看到类似QFactoryLoader :: QFactoryLoader()checking...这样的行,但是在Windows Qt中,输出会将输出路由到OutputDebugString()API,因此CMD窗口中将不会显示任何内容。使用Visual Studio或Qt Creator可以看到输出,如果在非开发PC上遇到插件问题,这没有太大帮助。另一种选择是下载一个实用程序,并在启动应用程序之前将其打开。

Windows部署

首先使用Release模式构建应用得到可执行文件myapp.exe。使用windeployqt.exe工具可以自动拷贝Qt库和插件到应用程序目录,但是这个工具会拷贝多于程序需要的好多东西。通常都需要手动删除一些程序不需要的库。

Windows部署Qt应用程序通常至少包含以下文件:

windows

platforms目录内包含qwindows.dll,这样简单的应用程序是可以运行的,并且可以打包分发。

这是一个最基础的应用,通常我们的应用会更复杂,比如需要插件sqldrivers、imageformats、translations。比如:

更多插件

所有插件都放在一个目录会显得很乱,现在我们创建一个qt.conf,内容如下:

[Paths]

Plugins=plugins

然后我们新建目录plugins,把所有插件移动到plugins。使我们的应用程序目录变得的更加简洁:

注意:plugins是Qt默认就会查找的目录,所以没有qt.conf也无所谓,但是如果想要指定一个特殊的插件目录名称,则必须使用qt.conf,如:third。

Linux部署

几乎所有的Linux发行版都会预装Qt,所以如果我们的应用是为特定发行版编译的,我们几乎是不需要进行任何配置,直接打包分发可执行文件即可(如果你是这种情况,就不需要看下面内容了)。

但是如果目标Linux没有安装Qt或者版本比较老,那我们的应用程序就可能不会运行了。这也是我遇到的问题,我的应用使用Qt5.12.5开发并编译,但目前非常主流的Ubuntu 18.04 lts最高只可使用Qt5.9.5。

那就没办法了么?是不是可以升级Linux系统的Qt安装呢?网上有相关文章,需要破坏系统基础配置,对于只想运行一个应用程序,就需要破坏系统环境配置,这是得不偿失的。能不能像Windows那样拷贝Qt5.12.5的动态库到其他安装了低版本的Qt,甚至没有安装Qt的Linux上面运行呢,答案是肯定的。下面内容就是说明如何完全体部署Qt应用到Linux。

修改连接库(不推荐)

就是把系统Qt库链接到新版本的Qt库,网上有类似方法,这种方式非常不推荐,原因是Linux发行版在开发时对所有库进行了预设,强行修改可能会造成一个应用程序好用了,十个应用程序崩溃了的情况。

带动态库一起打包发布(推荐)

首先同样使用Release模式构建应用,得到可执行文件myapp。GitHub上面有一个linuxdeployqt工具可以帮助构建.AppImage格式的Linux可执行程序,但是它的宗旨是构建可在所有平台运行的Qt程序,因此就会有同样的问题,如果我们编译环境过于新,也就无法使用这个工具了。

自己动手,丰衣足食

网上好多文章使用ldd命令来查看我们的应用依赖了哪些库,并且还有脚本可以自动复制这些库到应用目录,这通常是不可行的。因为ldd列表出来的大部分库并不是我们需要的,有些甚至是我们构建系统独有的库,并且我们并不能很好的确定该删除哪些库,还有就是ldd只能检查我们的应用程序直接依赖的哪些库,并不能查出依赖库的间接依赖,这些只有Qt自己知道,比如:libqxcb.so依赖libQt5DBus.so.5。

链接库

把.so文件放在可执行文件旁边并不会像Windows加载DLL那样自动被系统加载,Linux有几个可以配置加载动态库的方法,我这里使用设置环境变量LD_LIBRARY_PATH的方法,其他方法通常都需要root权限,并且破坏了系统原有运行模式,所以对于部署一个独立运行的应用程序使用LD_LIBRARY_PATH是最好的方法。

以下是我的应用程序在Linux下的目录,这是一个最基础应用程序的目录结构,你几乎不能删除其中任何文件。

linux

./bin 可执行文件目录

./bin/qt.conf qt特殊配置文件

./bin/myapp 可执行文件

./plugins 插件目录

./plugins/platforms

./plugins/platforms/libqxcb.so

./lib 动态库加载目录

./lib/libQt5Gui.so.5

./lib/libQt5Widgets.so.5

./lib/libQt5XcbQpa.so.5

./lib/libQt5DBus.so.5

./lib/libicudata.so.56

./lib/libicui18n.so.56

./lib/libQt5Core.so.5

./lib/libicuuc.so.56

./myapp.sh 应用启动脚本

启动脚本

因为我们的程序包含了所需要的库,所以需要让操作系统来加载我们自己的库,基于此我们需要使用脚本的方式启动我们的应用程序,来代替直接运行可执行文件。使用LD_LIBRARY_PATH来指定动态库加载目录,这里需要指定到了lib目录。

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/lib

export LD_LIBRARY_PATH

$dirname/bin/$appname "$@"

如果你是用以上脚本来启动应用程序,只需要修改脚本名称即可。

bin目录

bin目录存放可执行文件和qt.conf

qt.conf内容如下:

[Paths]

Prefix = ../

Plugins = plugins

Prefix指定程序工作目录,相对于可执行文件路径。默认为.当前目录,这里我为了是目录更加整洁,指定为..上级目录。

Plugins指定插件加载目录,相对于Prefix目录。上面这种指定方式,Qt就会在我的plugins目录下寻找插件了,例如plugins/platforms/libqxcb.so。

打包发布

以上目录结构和各种依赖即是一个Qt发布Linux环境的最小集了,可以使用以上目录针对各种不同Linux发行版进行构建打包,或者直接压缩进行分发。这样应用就可以在装有低版本Qt甚至没有安装Qt的环境上面运行了。

你还可以编写自己的.desktop文件使用myapp.sh作为应用启动命令。

qt程序部署在linux,Qt应用打包发布,部署真正的Qt程序LinuxWindows-Go语言中文社区...相关推荐

  1. linux redis最大连接数,并发编程-并发下redis连接数监测-Go语言中文社区

    背景 用go语言写的服务,之前在并发过后,redis连接很久没有释放,下面来做下监测的过程. 监测命名netstat 实战 netstat -nat | grep 6379 可以检测端口的情况 可以看 ...

  2. linux 网卡包存储,Linux下使用libpcap进行网络抓包并保存到文件-Go语言中文社区

    libpcap是一个抓取网络数据报文的C语言函数库,使用这个库可以非常方便的抓取网络上的报文,方便我们分析经过我们设备上的各种报文: 1.libpcap安装 下载文件:libpcap-x.x.x.ta ...

  3. linux apk 拆分 odex,android apk反编译和odex转dex-Go语言中文社区

    http://www.cnblogs.com/wanqieddy/archive/2012/03/01/2375424.html 大家好,这里介绍apk反编译操作. 1:apk反编译 2:odex转d ...

  4. linux控制协程参数,Linux高性能网络:协程系列06-协程实现之切换-Go语言中文社区...

    目录 6.协程实现之切换 问题:协程的上下文如何切换?切换代码如何实现? 首先来回顾一下x86_64寄存器的相关知识.x86_64 的寄存器有16个64位寄存器,分别是:%rax, %rbx, %rc ...

  5. C++ QT调用python脚本并将软件打包发布

    对我来说主要就是打包的问题比较难解决,弄了一个下午都没解决,不知道是minconda的问题,还是Qt更新的原因,网上的很多解决方法都不行,经过我的一项一项排查,最后发现就是少导了一个文件夹. 怎么调用 ...

  6. linux java jar打包_【Java】Java程序打包成jar包在Linux上运行

    当需要把在Windows上开发的Java程序用在Linux上运行时,就需要吧该Java程序打包成jar包上传到Linux上去运行. 1.Java程序用MyEclipse打包成可运行的jar包 (1)在 ...

  7. linux 运行go文件路径,go程序部署到linux上运行-Go语言中文社区

    go 语言版本:go1.9.2 开发环境:win10 部署环境:ubuntu 14.04.6 一.win10上的操作 1.在src目录下依次执行set CGO_ENABLED=0.set GOOS=l ...

  8. xmanager linux,教您用xmanager启动Linux上的图形界面程序-Go语言中文社区

    对于习惯实体化的开发人员来说,还是界面化用着比较习惯,所以这就涉及到掌握使用Xmanager启动Linux上的图形界面程序,为了方便大家的使用,本集小编就详细的为大家讲解具体操作. 具体步骤如下: 1 ...

  9. Java应用怎么打包发布_myeclipse中java应用程序打包发布步骤

    12.6 应用程序打包发布(Win) 1) 在Windows 中设置环境变量(安装JDK时要求) Set Path=%Path%;d:\Program Files\Java\jdk1.6.0_14\b ...

最新文章

  1. redis-deskmanager 连不上 虚拟机 - centos redis
  2. 程序员是复制粘贴的工具人?还是掌握“谜底”的魔术师?
  3. pandas中drop用法_如何使用drop方法对数据进行删减处理
  4. Windows Azure Storage (4) Windows Azure Storage Service存储服务之Blob Share Access Signature
  5. php 工厂模式 使用场景,PHP设计模式之工厂模式
  6. python中静态方法存在的意义
  7. 【转】基于easyui开发Web版Activiti流程定制器详解(一)——目录结构
  8. Visual Studio 2013中因Browser Link引起的Javascript错误
  9. 2019最新 Java商城秒杀系统的设计与实战视频教程(SpringBoot版)_1-3课程要求与收益...
  10. java获取不重复随机数_java实现生成不重复的随机数,可循环利用
  11. WPF ImageButton
  12. 为什么要玩FLTK(Fast Light Tool Kit)
  13. 美通社日历 | 媒体关注、会展信息、企业财报发布,节假日备忘(12月21日—12月27日)...
  14. 天正菜单栏不见了怎么显示出来_天正建筑菜单栏不见了怎么调出来? _ 设计学院_设计软件教程自学网...
  15. 告诉你猪身上最香的部位是什么?五花肉不是最香
  16. Linux使用C语言实现ls命令
  17. 温州科技职业学院 计算机网络技术,浙江【温州科技职业学院】_计算机网络技术专业建设方案.doc...
  18. mysql数据库中查询姓氏_数据库查询表添加姓氏
  19. centos7查看进程ps_Linux ps命令:查看所有进程信息
  20. bq3060电池控制芯片使用笔记

热门文章

  1. Java讲课笔记25:缓冲流、字符流与转换流
  2. 大数据学习笔记52:Flume Interceptors(Flume拦截器)
  3. 【BZOJ1568】【Tyvj3490】Blue Mary开公司 李超线段树
  4. 频率副词always,usually,often...用法_16
  5. python中o_Python O
  6. python读取数据库绘图_获取博客积分排名,存入数据库,读取数据进行绘图(python,selenium,matplotlib)...
  7. 2017.7.13 维修数列 思考记录
  8. python调用js文件报错_python - selenium 运行网页中js脚本报错,提示未定义
  9. asp.net 安装element ui_Vue+Element环境搭建
  10. eclipse可以写前端吗_学生:“老师,可以写老师吗?”语文老师怎么做才不尴尬?...