Qt - 跨平台程序打包发布

Qt跨平台

Qt 的跨平台为 一次编码,处处编译,这与 Java 依赖虚拟机实现的 一次编译,处处运行 是不同的。

Java跨平台实现

Java 是把针对不同平台与操作系统的跨系统核心代码抽象出来,形成单独的 JVM层(java virtual machine)Java 代码运行在 JVM 上,把跨平台 java语言框架问题 分解为在不同平台上设计 JVM的问题,结构简单、逻辑清晰、易于实现 1

为此,Java 付出了牺牲效率的代价。Java 语言需要先通过 JVM 再映射到操作系统里,最后由CPU 执行,执行过程多了一步。早期由于 Java语言 主要使用 解释性编译器,从而导致运行效率进一步降低。但是随着 即时编译技术(JIT) 的推出,尤其是硬件计算速度的大幅提升,Java运行效率 问题基本得到解决 1

Qt跨平台实现

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程序打包发布。

Qt - Windows打包发布

最小依赖库

QtCore 中,当我们把构建方式选为Release,将生成 *.exe 文件

在这里插入图片描述
直接打开该文件会提示 缺少xxx.dll 等错误。

我们可在 Qt 的编译器路径下搜索缺失的 dll文件

我的路径是 xxx\QT5.9.3\5.9.3\mingw53_32\bin,找到后将其放到可执行文件· 同一路径 下。

下图是我复制的 dll库,应该是 最小依赖库 了。

在这里插入图片描述

Windeployqt

作为懒鬼,我坚信懒是人类发展的原动力。
在这里插入图片描述
于是我发现了一个便捷的工具 Windeployqt,它将帮助我们将依赖库复制到指定目录中。

例如我这在 D盘 创建了一个 UHelper文件夹 ,然后将前面生成的 UHelper.exe 移动进去。

打开Qt for Desktop
在这里插入图片描述
执行 windeployqt *.exe

在这里插入图片描述
可以发现,依赖的库都复制进去了

在这里插入图片描述

Enigma Virtual Box 打包为可独立运行exe

我们可以将上面的 exe文件dll文件 打包给别人,但是这样一点也不酷。
在这里插入图片描述
有没有办法直接将 exe所需的dll库 都打包为一个exe文件呢?

还真有,Enigma Virtual Box 就是一个不错的选择。

下载地址见:Enigma Virtual Box官网

在这里插入图片描述
ps:如果是使用 Windeployqt 添加的库,把所有添加的 dll及文件夹都拖选添加至 Enigma Virtual Box 即可。

切记:文件夹也要,并且不要试图去改变它。

执行完成后,就生成了一个独立可执行程序。

在这里插入图片描述

Inno Setup 封装为安装包

下载地址见:Inno Setup官网

使用流程可参考 2 Inno setup 打包教程

生成安装程序如下:

在这里插入图片描述
安装完成后的文件目录如下:
在这里插入图片描述
有别于直接移植的是,在控制面板中是可以查找得到该程序的(即修改了注册表)。并且它给我们提供了一个卸载程序。

如何修改版本信息

到此为止我们生成的可执行文件都是没有详细信息的。

在这里插入图片描述

修改.pro

最简单的方法莫过于直接在 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资源文件

由于 rc文件Windows平台相关的东西,Qt助手 中对于 rc文件 几乎没有任何介绍 5

使用可查看本文参考 - 传送门

Qt - Linux打包发布

将项目拷贝到 Ubuntu18.04

Qt环境Windows下的相同,均为 Qt5.9.3

移过去后,UI 显示有点问题,于是修改了一下 UI

然后用 Release编译,即生成可执行文件 UHelper

在这里插入图片描述
由于 Ubuntu自带了Qt库 6,我们可以直接运行:

./UHelper

程序运行效果:

在这里插入图片描述
若非 Ubuntu 系统则需要拷贝so库

拷贝依赖库

可以使用 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

linuxdelpoyqt

linuxdeployqt 可以理解为 WindeployqtLinux 版本,让你的打包如丝般顺滑。

同时,linuxdelpoyqt 是一个开源项目,下载地址见 Github-linuxdelpoyqt

在这里插入图片描述
下载完成后我们给它重命名为 delpoyqt

执行一下:
在这里插入图片描述
但是,我们在其他路径下调用则显得很不方便。

为此可将其移动到 /usr/local/bin,这样在任何地方都可以使用 deployqt

sudo mv ./deployqt /usr/local/bin

验证:
在这里插入图片描述
打包:
在这里插入图片描述
too young too over
AppImage 的文档提到,其理念是:应用程序应建立在最旧的系统上,以使它们可以在较新的系统上运行 8

至于原因,其解释为:这样就可以排除某些 基础库,而这些 基础库 可以在所有主要的 桌面Linux发行版 中找到,从而减少了 One app = one file 的开销 8

显然 Linuxdeployqt 的作者是非常认同这种做法的。

probonopd :我认为应用程序开发人员只是为最新和最大的发行版进行开发而“懒惰”,并告诉用户“仅升级您的OS即可使用此应用程序” 9

目前所支持的最新Ubuntu LTS版本为16.04

安装Ubuntu16.04镜像,记得选 arm版 并安装 QtLinuxdeployqt

重复之前的步骤,再重新执行:

sudo deployqt Uhelper -appimage

在这里插入图片描述
这里提示图标的问题,可以暂时忽略。
在这里插入图片描述
如何设置图标,如何让软件开机自启,如何打包为 deb文件,可参考 在Linux下使用linuxdeployqt发布Qt程序 10

参考鸣谢


  1. 《Qt平台体系与应用——Qt5.5+核心方法、技巧与案例》 ↩︎ ↩︎ ↩︎

  2. Inno setup 打包教程 ↩︎

  3. How to Get Current App Version in Qt ↩︎

  4. Qt 之生成 Window 资源文件(.rc 文件) ↩︎

  5. Qt 之添加 Windows 资源文件(.rc文件) ↩︎

  6. 01-为什么要用Qt开发(Qt跨平台应用开发) ↩︎

  7. Qt for Linux/X11 - Deployment ↩︎

  8. AppImage-Docs-Introduction-Concepts ↩︎ ↩︎

  9. Latest continuous linuxdeployqt build does not work on Ubuntu 16.04 LTS and openSUSE Leap 15.0 #340 ↩︎

  10. 在Linux下使用linuxdeployqt发布Qt程序 ↩︎

猜你喜欢

转载自blog.csdn.net/weixin_40774605/article/details/105913281