avatar Nihil

Nichts Hsu

  • 首页
  • 子域
  • 分类
  • 标签
  • 归档
  • 关于
首页 在高版本系统上为 Qt6 生成 AppImage
文章

在高版本系统上为 Qt6 生成 AppImage

发表于 2022/11/01 更新于 2022/11/21
作者 Nichts Hsu
9 分钟阅读
在高版本系统上为 Qt6 生成 AppImage
在高版本系统上为 Qt6 生成 AppImage

前言

目前,大部分的 AppImage 的教程与工具都建议你在最低所支持的系统上进行编译打包,这是由于 Linux 系统的兼容性,在旧版本系统打包的软件可以正常在新版本系统中运行,反过来则不行。

但是这一点对于 Qt 用户尤其是 Qt6 用户而言很不友好:一方面,在旧版本的 Linux 系统上很难安装高版本的 Qt,另一方面,过于老旧的 GCC 不支持大量的 C++ 新特性,需要对代码进行大改特改才能让它编译成功。

相比于这一条坎坷之路,我更倾向于走另一条路:在最新版本的系统上编译打包,然后解决它在低版本系统上遇到的所有问题。

软件

本文需要用到两个软件,分别是 linuxdeploy 与 appimage-builder。本文默认您已经从 Github 上下载好了软件,赋予其可执行权限,并且已经软链接到 /usr/bin/linuxdeploy 与 /usr/bin/appimage-builder。

另外,appimage-builder 依赖 docker 进行测试,也请先安装好 docker 并测试其功能正常。

最后需要提一点,我在尝试 Ubuntu 22.04 系统官方的 Qt 6.2.4 时,在某一步骤始终无法获得有效的进展,因此本文是基于使用 Qt 官方工具安装的 Qt 6.4.0 来写的。

打包 AppImage

首先来到项目目录下,通过下面的命令进行编译:

1
2
3
4
mkdir build
cd build
qmake ../
make

在该目录下创建 Desktop Entry 文件,以 demo 来举例,创建 demo.desktop,然后输入:

1
2
3
4
5
6
7
8
9
[Desktop Entry]
Type=Application
Name=Demo App
Comment=
TryExec=demo
Exec=demo
Icon=demo
Terminal=false
Categories=Qt;

准备好图标文件 demo.png,然后使用下面的命令生成 AppDir 目录:

1
linuxdeploy --appdir AppDir -e demo -i demo.png -d demo.desktop --plugin qt

这一步的目的主要是将 Qt 库打包到 AppDir 下。

不要使用参数 --output appimage,因为这样打包出来的 AppImage 只能在当前或更高版本的系统上才能运行,无法兼容低版本系统。

至此,linuxdeploy 的任务就结束了,但是在进行下一步之前,我们要先把 AppDir/demo.desktop 与 AppDir/demo.png 删除,否则 appimage-builder 将抱怨无法创建这两个文件。

接下来我们执行 appimage-builder:

1
appimage-builder --generate

这一步会询问你一些项目的基本信息,并生成一个 AppImageBuilder.yml。填项目信息时不需要太过担心,因为都可以在 AppImageBuilder.yml 里面修改。

在配置文件中的 AppDir.apt.include 中,你需要将你项目依赖的包给包含进去,这样在构建 AppImage 时,appimage-builder 会下载这些包打包进去。

配置文件改好后,我们可以来初步进行尝试打包:

1
appimage-builder --appdir ./AppDir

这一步,appimage-builder 会在 Github 上拉取一些必要的文件,并且会拉取五个 docker 镜像。如果你的网速太慢,或者干脆连不上,请尝试科学上网,然后通过设置 HTTPS_PROXY 与 HTTP_PROXY 让 appimage-builder 走科学上网途径。

不出意外的话,这一次尝试,我们会停在这个报错上:

1
2
3
4
INFO:TEST CASE 'fedora-30':command
$ ./AppRun
qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

这个报错可能不太明了,我们可以通过添加环境变量 QT_DEBUG_PLUGINS 来获得更详细的报错:

1
2
3
4
5
6
7
8
...
  test:
    fedora-30:
      env:
        QT_DEBUG_PLUGINS: 1
      image: appimagecrafters/tests-env:fedora-30
      command: ./AppRun
...

得到报错:

1
2
3
4
5
INFO:TEST CASE 'fedora-30':command
$ ./AppRun
qt.core.plugin.factoryloader: checking directory path "/app/usr/bin/platforms" ...
qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

看来是 platform 打包错位置了,我们将 AppDir/usr/plugins/platforms 拷贝到 AppDir/usr/bin/platforms,继续测试。

此时,我们会发现,在 fedora-30,debian-stable,archlinux-latest 三个测试环境下,我们的程序都成功打开了,但是在 centos-7 测试环境下,报了下面的错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
INFO:root:
INFO:root:Running test: centos-7
INFO:root:-----------------------------
INFO:TEST CASE 'centos-7':before command
$ useradd -mu 1000 nihil

$ mkdir -p /home/nihil/.config

INFO:TEST CASE 'centos-7':command
$ ./AppRun
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /app/usr/bin/usb-regulus)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /app/usr/lib/libQt6Widgets.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /app/usr/lib/libQt6Widgets.so.6)
/app/usr/bin/usb-regulus: /lib64/libz.so.1: version `ZLIB_1.2.9' not found (required by /app/usr/lib/libQt6Gui.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /app/usr/lib/libQt6Gui.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /app/usr/lib/libQt6Core.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `CXXABI_1.3.11' not found (required by /app/usr/lib/libQt6Core.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /app/usr/lib/libQt6Core.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /app/usr/lib/libQt6Core.so.6)
/app/usr/bin/usb-regulus: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /app/usr/lib/libQt6DBus.so.6)
/app/usr/bin/usb-regulus: /lib64/libgpg-error.so.0: no version information available (required by /app/usr/lib/libgcrypt.so.20)

这是因为低版本系统的 GLIBC 库版本太低,我们需要将本机系统的 libstdc++6 也打包进去:

1
2
3
4
5
AppDir:
  apt:
    include:
    # - libc6:amd64   # libstdc++6 包含了这个包,因此可以删除这一行
    - libstdc++6

之后继续打包,报错:

1
2
3
4
INFO:TEST CASE 'centos-7':command
$ ./AppRun
/app/usr/bin/usb-regulus: /lib64/libz.so.1: version `ZLIB_1.2.9' not found (required by /app/usr/lib/libQt6Gui.so.6)
/app/usr/bin/usb-regulus: /lib64/libgpg-error.so.0: no version information available (required by /app/usr/lib/libgcrypt.so.20)

同理,这是因为旧版本系统的 ZLIB 版本太低了,我们把本机的 zlib1g 也打包进去:

1
2
3
4
5
AppDir:
  apt:
    include:
    - libstdc++6
    - zlib1g

继续测试,仍然有报错:

1
2
3
4
INFO:TEST CASE 'centos-7':command
$ ./AppRun
/app/usr/bin/usb-regulus: /lib64/libgpg-error.so.0: no version information available (required by /app/usr/lib/libgcrypt.so.20)
/app/usr/bin/usb-regulus: symbol lookup error: /app/usr/lib/libgcrypt.so.20: undefined symbol: gpgrt_lock_lock, version GPG_ERROR_1.0

我们就不需要多说什么了,将 libgpg-error0 也打包进去:

1
2
3
4
5
6
AppDir:
  apt:
    include:
    - libstdc++6
    - zlib1g
    - libgpg-error0

完成到这一步之后,再进行打包测试,发现在五个测试系统中,我们的程序都顺利打开了。此时 appimage-builder 会继续完善剩下的工作,最终打包出 Demo App-1.0.0-x86_64.AppImage。

关于 Qt 6.2.4

在写完这篇博文之后,我尝试了 Ubuntu 22.04 官方打包的 qt6-base-dev 也就是 Qt 6.2.4 时,一直卡在了 xcb 问题上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
QFactoryLoader::QFactoryLoader() looking at "/app/usr/bin/platforms/libqxcb.so"
Found metadata in lib /app/usr/bin/platforms/libqxcb.so, metadata=
{
    "IID": "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.3",
    "MetaData": {
        "Keys": [
            "xcb"
        ]
    },
    "archreq": 0,
    "className": "QXcbIntegrationPlugin",
    "debug": false,
    "version": 393728
}


Got keys from plugin meta data QList("xcb")
Cannot load library /app/usr/bin/platforms/libqxcb.so: 
QLibraryPrivate::loadPlugin failed on "/app/usr/bin/platforms/libqxcb.so" : "Cannot load library /app/usr/bin/platforms/libqxcb.so: "
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

前后折腾了五六个小时,把谷歌上能找到的解决方案都试了个遍,仍然没有解决这个报错。如果有大佬成功打包过 Qt 6.2.4,还请不吝赐教。

教程, Qt
qt 教程 linux appimage
本文由作者按照 CC BY 4.0 进行授权
分享

最近更新

  • 『I Wanna』 Best Bye To 2016
  • [译] Rust 中的内联
  • [Rust] 幽灵索引类型与匿名结构体
  • [C++] 深入了解左值与右值
  • Android.bp 中启用 openmp
外部链接
  • 996.icu
  •  此博客的 Github 仓库
  •  Olimi 的个人博客

文章内容

相关文章

2021/09/15

Shell 中的管道、重定向、进程替换

管道 相信绝大部分 Linux 用户对管道(Pipe)都不陌生,其语法是: command1 | command2 它的作用是将 command1 的标准输出作为 command2 的标准输入来使用。一个非常典型的例子是 grep,相信大部分人都有使用过形如 command | grep xxx 的命令。 管道的两个命令分别在两个不同的子 shell 中执行,并且没有规定哪个...

2021/03/15

记一次 Manjaro 下 Grub 炸裂的问题

问题的发生 最近嫌弃 Ubuntu 实在是太傻逼了,遂卸载换 Manjaro。 我原本以为以自己的经验完全不成问题,没想到装好系统后,第二天就打不开了。具体的特征是,开机时电脑一直黑屏,不久后电源指示灯熄灭,电脑重启进入 BIOS 的引导提示界面。以前我遇到过系统进不去、Grub 只有命令行的问题,但是连 Grub 命令行都没有的情况我还是第一次见到。 我以为是自己哪里操作有问题,没在...

2022/01/11

Git 从基础到进阶

前言 Git 是什么 想象这么一个场景,你和你的几个同事一起开发一个应用,假设同事 A 修改了代码 1,同事 B 修改了代码 2,你自己修改了代码 3,要如何将你们的修改安全、准确地同步到所有人的电脑上?如果有一天,应用突然运行不了了,你尝试 debug 无果,想要回退到上一次能够正常运行的代码版本,你要如何操作? 这就是为什么我们需要版本控制工具。它们对代码的提交和修改进行纪录,方便...

GNU C 一些有趣的扩展语法

Qt 计时器 QTimer 的妙用

© 2024 Nichts Hsu. 保留部分权利。

本站采用 Jekyll 主题 Chirpy

热门标签

编程语言 教程 rust c++ android c++20 usb 翻译 linux qt

发现新版本的内容。