Ubuntu18.04下Qt5.15程序的发布
- 前言
- 现有的方法
-
- 脚本法
-
- 坑1
- 坑2
- 坑3
- 举一反三
- 进阶1
- 进阶2
- 静态编译法
-
- 静态编译实战
- 静态编译发布程序总结
- linuxdeployqt方法
-
- linuxdeployqt方法实战
- linuxdeployqt方法小结
- 总结
前言
Qt程序打包发行一直是一个不大不小的问题,在windows下Qt官方提供了windeployqt工具,可以通过简单的cmd命令实现Qt程序的打包发布,绝大部分情况下能正常工作(使用msvc编译器的话有可能需要在目标机上安装vs运行库),而在linux中不知为何官方没有提供类似的工具(可能是因为linux发行版种类很多,碎片化严重,qt官方没有足够的人手针对每个平台进行维护,这就需要我们自己动手了。
作为linux主要的发行版之一Ubuntu,在其上发布Qt程序方法的文章可以说是汗牛充栋,但是其中大量文章已经过时,在比较新的环境下已经行不通或者需要改进,甚至很多还存在误导读者的现象,而且普遍地只讲方法不讲原理思路,这篇文章在实测的基础上,对前人方法做一个总结和提高,把其中比较关键的难点加以梳理,以馈读者。
现有的方法
脚本法
这也是关于在Ubuntu上打包程序的最常用方法,首先使用一个脚本生成程序的依赖文件,然后再编写另一个执行脚本,用于启动程序,下面我们实际操作一下——
编写脚本copylib.sh
Ubuntu程序搜索依赖库的优先级 |
---|
编译环境下的动态库路径, 可由环境变量LIBRARY_PATH指定 |
运行环境下的动态库路径, 由环境变量LD_LIBRARY_PATH指定 |
配置文件/etc/ld.so.conf中指定的动态库搜索路径 |
默认的动态库搜索路径/lib |
默认的动态库搜索路径/usr/lib |
坑1
解决方式——复制Qt/5.15.2/gcc_64/plugins下platforms文件夹至程序文件同级目录(也和lib目录同级),里面实测仅有libqxcb.so有用。
xcb是Ubuntu用于生成视窗系统的工具,所有Qt带GUI的程序都必须包含libqxcb.so这个插件。
坑2
好家伙,找到了这个插件,但是不能用,还建议你重装~~
解决方式——复制Qt/5.15.2/gcc_64/lib中的libQt5XcbQpa.so.5.15.2,libQt5XcbQpa.so.5.15,libQt5XcbQpa.so.5,libQt5DBus.so.5.15.2,libQt5DBus.so.5.15,libQt5DBus.so.5六个文件至程序目录/lib,实际上六个文件只有2个是库文件,其余4个是链接,可以把libQt5XcbQpa.so.5.15.2改名成libQt5XcbQpa.so.5这样子,这样库文件会少一点点。
然后发现问题还是没有解决
进入/usr/lib/x86_64-linux-gnu,找到libxcb-xinerama.so.0.0.0和libxcb-xinerama.so.0,复制到程序目录/lib,如果在/usr/lib/x86_64-linux-gnu下面找不到这两个文件,运行
如果没有意外,这次打包应该成功了,事实上我通过这种方法,成功打包了几个之前用Qt做的GUI程序,把打包好的文件夹复制到一个全新安装的纯净的Ubunt18.04系统上,可以完美运行。
但是在我同样的方法打包带有Qt Data Visualization模块的时候,又遇到了问题
坑3
解决方式——拷贝Qt/5.15.2/gcc_64/plugins下xcbglintegrations文件夹至程序文件夹下的plugins目录。
举一反三
我猜测Qt打包之所以出现这么多问题,根源就是这个Qt特有的插件系统,插件系统为Qt库提供驱动,但不直接被用户程序使用,所以能骗过ldd命令,如果qt用到特殊的模块(比如 Data Visualization)就需要把相关的插件(比如xcbglintegrations,用于OpenGL的支持)手动加入程序目录,而且还可能需要插件库的支持库(比如libqxcb.so需要libQt5XcbQpa.so和libQt5DBus.so),这就变得异常复杂,而且每个人遇到的问题可能都是不同的,除非你把所有插件全部拷贝,Qt5.15插件库600多M,如果用到QML,还需要QML库,这显然不现实。
总结一下解决问题的思路吧——
- 使用ldd命令,分析缺失的库,想办法把库补全
- 根据提示的错误信息,找到对应的插件动态库,拷贝过来,如果问题依旧,则继续用ldd找到插件的依赖,循环这个步骤
- 如果还是不行,请百度谷歌吧
所以这种方法永远可以用,但需要分析问题的能力比较强,正所谓手动挡有自己的乐趣/p>
进阶1
plugins目录只有放在程序文件同级才能被自动识别,如果非要放到别处或者改名,也不是不可以,就需要在执行脚本中修改临时变量QT_PLUGIN_PATH,比如
同理还有
如果用到其它插件,可以去查找对应的宏,这里不再赘述
进阶2
之前讲的方法都是改变临时的运行时搜索路径,所以不得不使用运行脚本启动程序,在windows下被惯坏了的我们怎么看都感觉不舒服,有种脱了裤子放屁的感觉,有没有办法直接运行程序呢案是肯定的,我们可以选择在编译时就指定搜索路径(且具有更高的优先级),做法其实很简单,在Qt工程的pro文件中加入一行代码即可:
这实际上是指定了gcc编译器的-rpath参数,使得我们的可执行程序拥有了主动搜索./lib目录的能力,这样上面的打包过程可以省略第二个脚本了,目标机上直接终端输入./你的程序名,即可启动程序,是不是舒服多了。
多说一句,编译时就用QMAKE_RPATHDIR指定搜索路径是更好的写法,因为在一些系统中LD_LIBRARY_PATH是无法生效的,不过很遗憾,国内众多讲解打包发布的文章没有发现主推这种方法的,这里我起个头,强力推荐!
静态编译法
笔者在windows上一直习惯使用静态编译的Qt版本,至少有2个优点:
- 发布方便,只需要一个可执行文件,拷贝过去就能执行
- 体积小,有朋友说这不对啊,静态编译只会增大可执行文件,怎么会减小体积呢时没有计算动态编译发布时附带的dll库,如果计算进来整体大小还是静态编译更小一些。
同样在我看来也有两个缺点:
- Qt官方不提供编译好的静态库,用户只能自己从Qt源码编译,而这个过程不但漫长(笔者的电脑大约需要2个小时),而且颇有难度(需要配置一套复杂的参数,不同版本参数可能不同,而且在linux下还需要下载一堆奇奇怪怪的前置软件包),难倒了一大堆初学者
- 作商用发布时可能面临版权麻烦,因为Qt虽然开源,但是大多组件的开源协议是LGPL,如果你的软件中使用了Qt,且静态发布,那么就犯规了,具体条款比较复杂,这里不再赘述。
静态编译实战
windows下的静态版Qt一直非常好用,那么在Ubuntu下面是否同样好用呢者亲自实践了一下,查阅了一些资料,针对Qt5.15.2版本做了静态编译,编写配置脚本autoConfigure.sh
运行autoConfigure.sh,可能 xcb错误,我这里比较粗爆,直接
- 在裸机中下载安装运行库,之后就能“直接”运行了
- 在开发环境下,用第一部分介绍的“脚本法”打包静态程序,把需要的运行库找全,一起复制到新机中
静态编译发布程序总结
上面两种方法可以看出都不完美,第一种方法对程序安装者要求较高,第二种方法和动态编译类似,可是又失去了静态编译的优势。究其根源还是linux各版本之间碎片化太严重,Qt编译起来都困难重重,就难以指望沙滩上堆出来的城堡能到处移动了。
而且Qt官方文档中讲解了一个静态发布的例子,其中提到
Since we cannot deploy plugins using the static linking approach, the executable we have prepared so far is incomplete. The application will run, but the functionality will be disabled due to the missing plugins. To deploy plugin-based applications we should use the shared library approach.
说的是这个例子含有插件,而静待编译法是无法发布含有插件的程序的,如果你的程序含有插件,只能选择动态发布。具体参考官方文档。
笔者尝试静态发布带Qt Data Visualization的程序,果然尝试了多种方法,都无法成功,看来官方文档所言不虚。
总的来说在Ubuntu上用静态编译的方法来发布程序,是一件吃力不讨好的事情。
linuxdeployqt方法
除了上面两种方法,还有没有别的方法呢是一定要有的,win系统下官方出了个自动拷贝依赖文件的工具windeployqt,而linux系统下也有个类似的工具,叫linuxdeployqt,可惜不是官方出品, git地址
一般个人出品的工具质量是参差不齐的,那么这个linuxdeployqt能否像windeployqt那样好用呢者也进行了测试
linuxdeployqt方法实战
下载linuxdeployqt-6-x86_64.AppImage版本,改权限,尝试运行,提示系统版本过高,什么鬼,现在是2021年了,Ubuntu20.04都出来了,竟然说18.04版本过高!
无奈,打开尘封已久的Ubuntu16.04虚拟机,把linuxdeployqt程序拷贝进去,改名放入/usr/bin目录,然后编译好一个qt例子程序,执行
等待片刻,生成了如下文件:
linuxdeployqt方法小结
总结
下面表格尝试对比了三种方法,可以说都不是非常完美,全都需要大量的手动操作,脚本法是完全手动挡,linuxdeployqt法是半自动挡,静态编译法则在小部分情况下非常好用。
打包方式 | 打包前准备工作难易程度 | 打包时难易程度 | 适用范围 |
---|---|---|---|
脚本法 | 简单 | 非常难 | 广泛 |
静态编译法 | 非常难 | 简单 | 仅能编译不带插件程序 |
linuxdeployqt工具 | 难 | 简单 | 广泛 |
相对来说使用linuxdeployqt工具算是一个不错的方案,但还是不算完美,还是希望Qt官方能负起责任,针对主流linux发行版提供出真正的linuxdeployqt工具,更少的bug,更频繁的维护。
文章知识点与官方知识档案匹配,可进一步学习相关知识CS入门技能树Linux入门初识Linux24962 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!