WeHack BBS
探索一下 AppImage 打包 - 可打印的版本

+- WeHack BBS (https://bbs.wehack.space)
+-- 版块: 计算机技术 (https://bbs.wehack.space/forum-5.html)
+--- 版块: GNU/Linux 讨论区 (https://bbs.wehack.space/forum-6.html)
+--- 主题: 探索一下 AppImage 打包 (/thread-130.html)



探索一下 AppImage 打包 - vimacs - 01-26-2020

由于我想给一台 CentOS 的服务器传一些软件,在服务器上传源码编译又不方便,因此我就有了在自己的机器上打 AppImage 包再上传的想法。于是我这几天在尝试 AppImage 打包。

根据 AppImage 官方文档的介绍,打包工具主要是 AppImage 包构建工具 AppImageKit 里面的 appimagetool. 它使用 mksquashfs 将 AppDir 打成 squashfs 包,然后再包装一个带有 squashfuse 的运行时,最终输出一个可执行的 ELF 文件。这个 ELF 文件执行的时候利用 squashfuse 用内核的 FUSE 功能挂载 squashfs,然后链式加载里面的应用。而所谓的 AppDir 就是一个有 GNU/Linux 目录层次结构的一个目录树,除了应用的可执行文件和它依赖的库之外,需要添加一个入口可执行文件 AppRun (最简单就是一个到应用程序的链接,其实这很像 initramfs 里的 /init). 由于一般的构建系统用 make DESTDIR=pkg install 之后就会在 pkg 下建一个这样的目录树,因此只要把应用程序依赖的库和 AppRun 加进去就行了。AppImage 官方推荐了 linuxdeploy 工具。

这次我测试用的应用是我维护的 FQTerm,它是个用 C/C++ 写的 Qt 应用,编译出来并安装之后,只要添加它所需要的动态库就好了。

首先我尝试在一个 Debian 10.2.0 的虚拟机里面构建 FQTerm,然后用 linuxdeploy 和 appimagetool 做成一个 AppImage. 之后先在那个 Debian 虚拟机里运行,运行成功。然后把它复制到我的 Arch 里面运行,也运行成功。

但是把它复制到我的 CentOS 7 虚拟机之后,就运行失败了,提示找不到 glibc 相关的符号。由于 linuxdeploy 有个 excludelist,会把各个发行版一般会有的文件排除在 AppDir 之外,因此 AppImage 里面其实没有 glibc 的动态库,最终用的是目标机器上的 C 库,而 CentOS 7 的 glibc 2.17 实在太老了,AppImage 在上面跑不起来。

为了满足我的好奇心,我到 AppImageHub 看了一下,根据上面的链接下了一个 Krita 官方的 AppImage,发现在 CentOS 7 上居然能运行起来,看来这个包是在老版本 glibc 的环境下打的。我用的 Debian 10 还是新了点,毕竟是 2019 年的系统。

接下来我很好奇在 musl 的环境下打包会怎样,于是我装了份 Alpine 的 chroot 并把各种包装好,编译出一份 FQTerm. 然后在 Alpine 下又装好了 linuxdeploy 和 AppImageKit. 由于 musl 和 glibc 在一些地方不一样,alpine 和一般的 GNU 操作系统也不一样,安装 linuxdeploy 和 AppImageKit 的时候还需要改一下源码,最终把东西装好,又打了份 AppImage.

最后复制出来发现,在 Alpine 下构建的 AppImage 变成了一份依赖于 musl 的可执行文件,在用 glibc 的系统上都跑不了。其实解决办法应该不难,就是把 AppImage 的运行时静态链接,但是 AppImageKit 还没有这样做。

最后做个总结,如果要做个通用的 AppImage 包的话,需要找一个老版本 glibc 的系统(或许也可以忽视 excludelist 把所有静态库打进去?)。此外,对于我这种情况,也许最好的办法就是直接在 CentOS 里面打包了,虽然不敢保证通用,但是用到我的服务器上应该没问题,只是这样搞又要折腾了,因为 CentOS 里面缺的软件太多了,构建应用之前先要手动编译大量依赖。也可以考虑试试在 Ubuntu 14.04/16.04 或者老版本的 Debian 上打包。总之,AppImage 其实并没有想象中那么好的兼容性,为了兼容,就要用一个老版本的系统作为构建应用的环境,这样就需要消耗更多的人力。如果把 AppImage 拆包的话,其实也就是一个可执行程序加上一堆动态库,和一些私有软件的做法也是很类似的,而那些软件一般也有运行系统的要求,并不是说所有新老系统都兼容,这样想想,也可以分析出 AppImage 的兼容性并没有想象中那么好。真要把兼容性做好的话,可能最好的办法还是用静态链接(这样还是对内核版本有依赖),或者用脚本语言/字节码做程序(但这需要目标系统装有解释器或虚拟机)。

刚才到 AppImageKit 的 issue 区看了一下,有人做了个 Go 语言的静态 AppImage 实现:https://github.com/kost/static-appimage
里面用的是 zip 而不是 squashfs.


RE: 探索一下 AppImage 打包 - vimacs - 01-28-2020

刚刚尝试把在 Alpine 下构建的 FQTerm 复制到 Debian 下打 AppImage,结果发现复制到 Debian 之后就运行不了了,用 patchelf 改了 interpreter 都没用,在这个过程中发现了两个问题:
1. linuxdeploy 不知道为什么没把 musl 的 C 库 libc.musl-x86_64.so.1 复制到 AppDir
2. linuxdeploy 之后运行 fqterm.bin 就报 libQt5Core.so 的符号查找错误,不知道是不是 patchelf 把文件搞坏了