Qt
的跨平台为 一次编码,处处编译,这与 Java
依赖虚拟机实现的 一次编译,处处运行 是不同的。
Java
是把针对不同平台与操作系统的跨系统核心代码抽象出来,形成单独的 JVM层(java virtual machine)
,Java
代码运行在 JVM
上,把跨平台 java语言框架问题
分解为在不同平台上设计 JVM
的问题,结构简单、逻辑清晰、易于实现 1。
为此,Java
付出了牺牲效率的代价。Java
语言需要先通过 JVM
再映射到操作系统里,最后由CPU
执行,执行过程多了一步。早期由于 Java语言
主要使用 解释性编译器
,从而导致运行效率进一步降低。但是随着 即时编译技术(JIT)
的推出,尤其是硬件计算速度的大幅提升,Java运行效率
问题基本得到解决 1。
Qt平台
封装了针对不同平台的类库,API
。这些都被上层做了封装,对我们开发者来说操作各种平台的接口都是一样的 1。以我最近使用的 QSerialPort
为例子,在 Qserialport.h
中可发现以下蛛丝马迹:
Qserialport.h(源码可戳)
//line 74
#if defined(Q_OS_WIN32)
typedef void* Handle;
#else
typedef int Handle;
#endif
//line 307
#if defined(Q_OS_WIN32)
Q_PRIVATE_SLOT(d_func(), bool _q_startAsyncWrite())
#endif
QSerialPort.cpp(源码可戳)
//line 90
#if defined(Q_OS_WIN32)
: readChunkBuffer(QSERIALPORT_BUFFERSIZE, 0)
#endif
{
writeBufferChunkSize = QSERIALPORT_BUFFERSIZE;
readBufferChunkSize = QSERIALPORT_BUFFERSIZE;
}
//line 1253
qint64 QSerialPort::bytesToWrite() const
{
qint64 pendingBytes = QIODevice::bytesToWrite();
#if defined(Q_OS_WIN32)
pendingBytes += d_func()->writeChunkBuffer.size();
#endif
return pendingBytes;
}
可见 QSerialPort
提供的接口是使用 宏
做了跨平台处理的。
那么 Qt
为我们提供的跨平台接口有那些呢?
Qt的界面,Qt封装的一些库包,数据结构以及算法…
对于 Qt项目
中不提供跨平台的部分,则需要我们自己实现,常用的就是 宏
。
例如换行操作:
#ifdef Q_OS_WIN32
qDebug() << "Windows换行!\r\n";
#else
//假设是Linux
qDebug()<<"unWindows换行!\n ";
#endif
好了下面进入正题,Qt
- Windows
程序及Linux
程序打包发布。
在 QtCore
中,当我们把构建方式选为Release
,将生成 *.exe
文件
我们可在 Qt
的编译器路径下搜索缺失的 dll文件
;
我的路径是 xxx\QT5.9.3\5.9.3\mingw53_32\bin
,找到后将其放到可执行文件· 同一路径
下。
下图是我复制的 dll库
,应该是 最小依赖库
了。
作为懒鬼,我坚信懒是人类发展的原动力。
于是我发现了一个便捷的工具 Windeployqt
,它将帮助我们将依赖库复制到指定目录中。
例如我这在 D盘
创建了一个 UHelper文件夹
,然后将前面生成的 UHelper.exe
移动进去。
打开Qt for Desktop
;
执行 windeployqt *.exe
。
我们可以将上面的 exe文件
及 dll文件
打包给别人,但是这样一点也不酷。
有没有办法直接将 exe
和 所需的dll库
都打包为一个exe
文件呢?
还真有,Enigma Virtual Box
就是一个不错的选择。
下载地址见:Enigma Virtual Box官网
ps:如果是使用 Windeployqt
添加的库,把所有添加的 dll
及文件夹都拖选添加至 Enigma Virtual Box
即可。
切记:文件夹也要,并且不要试图去改变它。
执行完成后,就生成了一个独立可执行程序。
下载地址见:Inno Setup官网
使用流程可参考 2 Inno setup 打包教程
生成安装程序如下:
安装完成后的文件目录如下:
有别于直接移植的是,在控制面板中是可以查找得到该程序的(即修改了注册表)。并且它给我们提供了一个卸载程序。
到此为止我们生成的可执行文件都是没有详细信息的。
最简单的方法莫过于直接在 pro文件
中添加以下代码。
下面列举了一些常用信息 3 4:
//版本信息
VERSION = xx.xx.xx.xx
//图标
RC_ICONS = xxxx.ico
//公司名称
QMAKE_TARGET_COMPANY = ""
//产品名称
QMAKE_TARGET_PRODUCT = ""
//文件说明
QMAKE_TARGET_DESCRIPTION = ""
//版权信息
QMAKE_TARGET_COPYRIGHT = ""
//中文(简体)
RC_LANG = 0x0004
由于 rc文件
是 Windows
平台相关的东西,Qt助手
中对于 rc文件
几乎没有任何介绍 5。
使用可查看本文参考 - 传送门 。
将项目拷贝到 Ubuntu18.04
;
Qt环境
与 Windows
下的相同,均为 Qt5.9.3
;
移过去后,UI
显示有点问题,于是修改了一下 UI
;
然后用 Release
编译,即生成可执行文件 UHelper
。
./UHelper
程序运行效果:
可以使用 ldd命令
查看其依赖库及路径:
ldd UHelper
//or
ldd ./UHelper
一个个复制很麻烦,为此可以借助 shell脚本
完成 so库
的复制:
#!/bin/sh
# ldd $exe (所以这里写你的可执行文件名)
exe="UHelper"
# copy 目录
des="/home/hsy/SW/Qt5.9.3/Project/UHelper_test"
# awk 匹配第三个参数“/”,排除掉没有路径的
deplist=$(ldd $exe | awk '{if (match($3,"/")){printf("%s\n"),$3}}')
# 将so库拷贝至des目录
# -L:--dereference 始终遵循源中的符号链接
# -n: --no-clobber 不要覆盖已存在的文件
cp -L -n $deplist $des
执行:
chmod 777 pack.sh
./pack.sh
复制完以后的结果如下图所示:
UHelper.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库 7。
linuxdeployqt
可以理解为 Windeployqt
的 Linux
版本,让你的打包如丝般顺滑。
同时,linuxdelpoyqt
是一个开源项目,下载地址见 Github-linuxdelpoyqt
为此可将其移动到 /usr/local/bin
,这样在任何地方都可以使用 deployqt
。
sudo mv ./deployqt /usr/local/bin
验证:
打包:
AppImage
的文档提到,其理念是:应用程序应建立在最旧的系统上,以使它们可以在较新的系统上运行 8。
至于原因,其解释为:这样就可以排除某些 基础库
,而这些 基础库
可以在所有主要的 桌面Linux发行版
中找到,从而减少了 One app = one file
的开销 8。
显然 Linuxdeployqt
的作者是非常认同这种做法的。
probonopd :我认为应用程序开发人员只是为最新和最大的发行版进行开发而“懒惰”,并告诉用户“仅升级您的OS即可使用此应用程序” 9。
目前所支持的最新Ubuntu LTS
版本为16.04
。
安装Ubuntu16.04
镜像,记得选 arm版
并安装 Qt
、Linuxdeployqt
…
重复之前的步骤,再重新执行:
sudo deployqt Uhelper -appimage
这里提示图标的问题,可以暂时忽略。
如何设置图标,如何让软件开机自启,如何打包为 deb文件
,可参考 在Linux下使用linuxdeployqt发布Qt程序 10。
《Qt平台体系与应用——Qt5.5+核心方法、技巧与案例》 ↩︎ ↩︎ ↩︎
Inno setup 打包教程 ↩︎
How to Get Current App Version in Qt ↩︎
Qt 之生成 Window 资源文件(.rc 文件) ↩︎
Qt 之添加 Windows 资源文件(.rc文件) ↩︎
01-为什么要用Qt开发(Qt跨平台应用开发) ↩︎
Qt for Linux/X11 - Deployment ↩︎
AppImage-Docs-Introduction-Concepts ↩︎ ↩︎
Latest continuous linuxdeployqt build does not work on Ubuntu 16.04 LTS and openSUSE Leap 15.0 #340 ↩︎
在Linux下使用linuxdeployqt发布Qt程序 ↩︎