- 开发工具
【免费下载链接】AppImageKit
Package desktop applications as AppImages that run on common Linux-based operating systems, such as RHEL, CentOS, openSUSE, SLED, Ubuntu, Fedora, debian and derivatives. Join #AppImage on irc.libera.chat
导读
本文以 AppImageKit 仓库的 README.md 为主线,系统讲解 AppImage 应用打包格式的核心设计、运行时(runtime)的工作机制,以及appimagetool从 AppDir 生成 AppImage 的完整流程。你将掌握:AppImage 的 7 个内置命令行参数与便携化目录用法、appimagetool全部选项的含义与底层实现、手动拼装 AppImage 的方法,以及仓库构建体系与 CI 测试的验证思路。
一、AppImage 是什么:一个文件、跨发行版运行
AppImage 是一种应用打包格式,它把桌面应用连同其依赖一起封装,使其可以在无需修改的情况下运行于多种目标系统(基础操作系统/发行版),例如 RHEL、CentOS、Ubuntu、Fedora、Debian 及其衍生发行版。
AppImageKit 正是 AppImage 格式的具体实现,尤其是那个会成为每个 AppImage 一部分的微型 runtime。根据 README.md 的说明,用 AppImage 分发应用的主要优势包括:
- 打包为 AppImage 的应用可以在许多发行版上运行(包括 Ubuntu、Fedora、openSUSE、CentOS、elementaryOS、Linux Mint 等);
- 一应用 = 一文件:用户只需下载一个 AppImage 文件,赋予可执行权限后直接运行;
- 无需解包或安装;
- 无需 root 权限;
- 不改变系统库;
- 开箱即用,无需安装运行时;
- 可通过
appimaged实现可选的桌面集成; - 可通过 AppImageUpdate 实现可选的二进制增量更新(只下载二进制差异);
- 可选地在文件内部用 GPG2 对 AppImage 签名;
- 可在 Live ISO 上工作;
- 双系统启动多个发行版时可以使用同一个 AppImage;
- 可被收录进 AppImageHub 集中目录;
- 可借助
--appimage-extract参数充当自解压压缩归档。
为什么需要 AppImage:Linux 桌面应用分发的痛点
README 引用 Linus Torvalds 在 DebConf 14 上的观点来阐述动机:Linux 桌面应用二进制分发是"一件非常痛苦的事情"——你不能为"Linux"制作二进制,只能为 Fedora 19、Fedora 20、debian stable 分别制作。原因在于发行版强制共享库策略、glibc 更新导致二进制兼容性被破坏等。
AppImage 的思路与 Windows/macOS 一致:把基础操作系统当作"平台",在其上运行我们关心的应用,即把不属于基础操作系统的依赖与应用捆绑在一起(application bundle)。关键结论是:
- 进入 AppImage 的二进制应该基于仍然受支持的最老的 LTS 或企业级发行版构建;
- 只需打包"私有副本"的库,成熟且各发行版都有的核心库(如稳定的 glibc 接口)不必捆绑;
- 只要避开任何发行版特有的打包方式、不使用过新的构建系统,理论上"编译一个二进制,让它尽量在所有发行版上一直工作"是可能的——这也解释了为什么 AppImageKit 选择 CentOS 6.x 与 Ubuntu 14.04 作为构建系统(见下文"构建"一节)。
二、AppImage 运行时(runtime):mount、环境变量与内置参数
运行一个 AppImage 时,runtime 会挂载文件系统镜像并透明地运行其中包含的应用。因此 AppImage 的用法通常等同于其内部应用的用法。但 AppImageKit 的 runtime 提供了一组特殊功能,下面逐一说明其行为与 src/runtime.c 中的对应实现。
2.1 命令行参数(由最新版 AppImageKit 构建的 AppImage 支持)
| 参数 | 作用 | runtime 源码实现 |
|---|---|---|
--appimage-help | 打印帮助选项 | src/runtime.c 中的print_help(),由 L622 处的参数判断触发 |
--appimage-offset | 打印内嵌文件系统镜像的起始偏移量后退出;可用于mount -o loop,offset=... | src/runtime.c,直接输出fs_offset(即该 ELF 的末尾) |
--appimage-extract | 从内嵌文件系统镜像中解包内容后退出;用于没有 FUSE 的系统 | src/runtime.c,调用extract_appimage()(L277 起),默认解包到squashfs-root/ |
--appimage-mount | 挂载内嵌文件系统镜像并打印挂载点,然后等待被杀死;用于不执行内部应用而只检视内容 | src/runtime.c,打印挂载点后fflush(stdout)并for (;;) pause() |
--appimage-version | 打印 AppImageKit 版本后退出;用于提交 issue | src/runtime.c,输出GIT_COMMIT |
--appimage-updateinformation | 打印内嵌的更新信息后退出;用于调试二进制增量更新 | src/runtime.c,读取 ELF 的.upd_info段 |
--appimage-signature | 打印内嵌的数字签名后退出;如需校验签名,应使用 AppImageKit 自带的validate工具 | src/runtime.c,读取 ELF 的.sha256_sig段 |
补充说明两点:
--appimage-extract支持可选的 pattern 参数:运行时(src/runtime.c)允许--appimage-extract [<pattern>],只解包匹配fnmatch模式的文件,并正确保留硬链接与符号链接。--appimage-mount之前会先加载 FUSE 库:runtime 在挂载前执行LOAD_LIBRARY(src/runtime.c),若系统缺少 libfuse,会提示改用--appimage-extract;挂载选项为只读加偏移(ro,offset=%lu,见 src/runtime.c)。
此外 runtime 还支持两个便携化辅助参数:--appimage-portable-home与--appimage-portable-config,它们会自动为 AppImage 创建.home/.config目录(详见下节),实现在 src/runtime.c 的portable_option()。
2.2 特殊目录:把配置随身携带(便携化)
正常情况下,AppImage 内的应用会把配置文件写到$HOME下的常规位置。但如果满足以下条件,配置文件会被存放到 AppImage 旁边,便于 USB 盘随身携带:
- 存在一个与 AppImage 同名、后缀为
.home的目录 → 执行内部应用前自动把$HOME指向它; - 存在一个与 AppImage 同名、后缀为
.config的目录 → 执行前自动把$XDG_CONFIG_HOME指向它。
该逻辑由 src/runtime.c 的set_portable_home_and_config()实现:运行时先检查对应目录是否可写(is_writable_directory,L79-L85),可写才setenv("HOME", ...)/setenv("XDG_CONFIG_HOME", ...)。
README 给出了完整示例(Leafpad 文本编辑器,把设置随身携带):
# 下载 Leafpad AppImage 并赋予可执行权限 wget -c "https://bintray.com/probono/AppImages/download_file?file_path=Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage" -O Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage chmod a+x Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage # 在与 AppImage 相同的目录下创建同名 + ".config" 后缀的目录 mkdir Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage.config # 运行 Leafpad,修改一些设置(例如改变默认字号)后关闭 ./Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage # 现在检查设置被写到了哪里: linux@linux:~> find Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage.config (...) Leafpad-0.8.18.1.glibc2.4-x86_64.AppImage.config/leafpad/leafpadrc注意leafpadrc被写入了预先创建的目录中——这就是便携化的效果。
2.3 runtime 执行内部应用的环境准备(AppRun)
挂载成功后,runtime 通过execv执行镜像内的AppRun(src/runtime.c)。AppRun 是每个 AppImage 内部的引导程序,其源码 src/AppRun.c 完成三件事:
- 解析
.desktop文件:扫描 AppDir 下第一个*.desktop文件(src/AppRun.c),提取Exec=行得到要执行的程序,并处理%f、%u、%F、%U等桌面文件字段码(L119-L148)。 - 切换到 AppDir 内的
usr目录:chdir(appdir/usr)(src/AppRun.c)。 - 设置隔离的应用环境变量(src/AppRun.c),包括:
PYTHONHOME=$APPDIR/usr/、PYTHONPATH=$APPDIR/usr/share/pyshared/;PATH=$APPDIR/usr/bin/:...前缀;LD_LIBRARY_PATH指向 AppDir 内的usr/lib、lib及常见多架构子目录(i386-linux-gnu、x86_64-linux-gnu、lib32、lib64等);XDG_DATA_DIRS、PERLLIB、GSETTINGS_SCHEMA_DIR、QT_PLUGIN_PATH、GST_PLUGIN_SYSTEM_PATH[_1_0];PYTHONDONTWRITEBYTECODE=1(避免 Python 因无法写__pycache__报错)。
同时 runtime 还会在 exec 前设置APPIMAGE(AppImage 完整路径)、ARGV0、APPDIR(挂载点)、OWD(原始工作目录)等环境变量(src/runtime.c),内部应用可据此感知自身运行环境。
三、appimagetool:从 AppDir 生成 AppImage
appimagetool用于从现成的AppDir生成 AppImage;更高层的工具(如linuxdeployqt)在内部使用它。README 给出的获取方式:
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" chmod a+x appimagetool-x86_64.AppImage最简单的用法(假设已有 AppDir):
./appimagetool-x86_64.AppImage some.AppDir3.1 完整命令行选项
README 完整列出了appimagetool的帮助输出:
Usage: appimagetool [OPTION...] SOURCE [DESTINATION] - Generate, extract, and inspect AppImages Help Options: -h, --help Show help options Application Options: -l, --list List files in SOURCE AppImage -u, --updateinformation Embed update information STRING; if zsyncmake is installed, generate zsync file -g, --guess Guess update information based on Travis CI or GitLab environment variables --bintray-user Bintray user name --bintray-repo Bintray repository --version Show version number -v, --verbose Produce verbose output -s, --sign Sign with gpg[2] --comp Squashfs compression -n, --no-appstream Do not check AppStream metadata --exclude-file Uses given file as exclude file for mksquashfs, in addition to .appimageignore. --runtime-file Runtime file to use --sign-key Key ID to use for gpg[2] signatures --sign-args Extra arguments to use when signing with gpg[2]这些选项与 src/appimagetool.c 中的GOptionEntry数组一一对应,以下是结合源码的补充说明:
--comp压缩算法:全局变量默认"gzip"(src/appimagetool.c)。源码强制只允许gzip与xz两种取值,否则报错退出(src/appimagetool.c);选择xz时还会附加-Xdict-size 100% -b 16384参数以改善压缩性能(L188-L195)。--mksquashfs-opt:可多次指定的透传参数,会原样追加到 mksquashfs 命令行(src/appimagetool.c)。--exclude-file与.appimageignore:除显式传入的排除文件外,如果当前目录存在.appimageignore,也会以-wildcards -ef方式传给 mksquashfs(src/appimagetool.c)。--sign/--sign-key/--sign-args:调用 gpg/gpg2 在文件内签名(详见本文第四节)。--runtime-file:默认使用内嵌进 appimagetool 二进制的 runtime(HAVE_BINARY_RUNTIME,L63-L66);指定此选项可改用外部 runtime 文件(L891-L904)。-l, --list:列出 AppImage 内嵌 squashfs 中的文件(src/appimagetool.c,sfs_ls()位于 L104-L131)。
此外,src/appimagetool.c 在启动时会做依赖检查:file与mksquashfs必须存在(否则直接报错退出);desktop-file-validate必须存在;zsyncmake、appstreamcli、gpg2/gpg、sha256sum/shasum缺失时仅打印警告。
3.2 打包流程与架构推断
当SOURCE是目录时,appimagetool 假定要打包它,流程如下(对应 src/appimagetool.c):
- 查找并校验
.desktop文件:必须存在且通过desktop-file-validate,并包含Categories=键(L713-L734)。 - 推断目标架构:优先读环境变量
ARCH(如ARCH=x86_64),否则递归扫描 AppDir 中可执行文件与*.so.*的 ELFe_machine字段(i386=3、x86_64=62、arm=40、aarch64=183,见 src/appimagetool.c);无法唯一确定时报错退出(L746-L763)。 - 构造输出文件名:默认形如
名称-版本-架构.AppImage,其中$VERSION环境变量若未设置且-g开启,会尝试用git rev-parse --short HEAD代替(L668-L707);$VERSION还会被写入 desktop 文件的X-AppImage-Version键(L790-L798)。 - 校验图标与
.DirIcon:desktop 文件中的Icon=必须在 AppDir 根目录存在png/svg/xpm之一,并据此创建.DirIcon符号链接(L801-L837)。 - AppStream 元数据检查:默认(除非
-n)检查usr/share/metainfo/<id>.appdata.xml,存在时用appstreamcli validate-tree与appstream-util validate-relax校验(L839-L881)。 - 生成 squashfs 并嵌入 runtime:调用
sfs_mksquashfs()(L135-L247)用-offset预留 runtime 大小的头部空间,然后fwrite写入 runtime、chmod 0755(L887-L928)。 - 嵌入更新信息(updateinformation):写入 ELF 的
.upd_info段(L988-L1030),格式必须是zsync|、bintray-zsync|、gh-releases-zsync|或pling-v1-zsync|前缀之一(L989-L993)。 - 计算并嵌入 MD5 摘要:写入
.digest_md5段(L1032-L1080),供 AppImageUpdate 做增量校验。 - 可选签名(
-s)与zsync 文件生成:签名失败时默认继续(除非设置FORCE_SIGN环境变量,L1082-L1091);若提供了 updateinformation 且系统装有zsyncmake,则生成对应的.zsync文件(L1093-L1133)。
3.3 手动生成 AppImage(不借助 appimagetool)
README 指出,如果想手动生成 AppImage,可以这样做:
mksquashfs Your.AppDir Your.squashfs -root-owned -noappend cat runtime >> Your.AppImage cat Your.squashfs >> Your.AppImage chmod a+x Your.AppImage其原理正是 appimagetool 自动化的过程:squashfs 镜像在文件末尾、runtime ELF 在文件头部。runtime 通过计算自身 ELF 大小来确定镜像起始偏移(fs_offset = appimage_get_elf_size(...),src/runtime.c),因此拼接顺序不可颠倒。另外,一个合格的 Type 2 AppImage 还要求 runtime 在偏移 8 处带有魔数414902(即AI\x01),由 Makefile.runtime 中的echo "8: 414902" | xxd -r -注入;ci/test-appimage.sh 中甚至有专门的测试,把魔数抹掉后验证 appimagetool 行为,可见魔数是类型 2 AppImage 的硬性要求。
四、签名与校验:GPG2 签名、validate 与 digest
AppImage 支持在文件内部嵌入 GPG2 数字签名。README 中--appimage-signature一节明确指出:打印签名用内置参数,校验签名应使用 AppImageKit 自带的validate工具。
4.1 签名(appimagetool -s)
appimagetool -s走 src/appimagetool_sign.c 的实现:
- 签名内容写入 ELF 段的
.sha256_sig,公钥写入.sig_key(src/appimagetool_sign.c); - 通过 gpgme 与 gpg 交互,密钥 ID 由
--sign-key指定,额外参数由--sign-args透传; - 密钥口令可通过环境变量
APPIMAGETOOL_SIGN_PASSPHRASE提供(src/appimagetool_sign.c),比命令行传参更安全; - 对应的 ELF 段预留在 runtime 中:
objcopy --add-section .upd_info=...与.sha256_sig=...(见 Makefile.runtime),每段预留 1024 字节空白。
4.2 校验(validate)
validate工具(源码 src/validate.c)的校验思路:
- 定位
.sha256_sig与.sig_key两个 ELF 段,要求两者在 ELF 头中连续(src/validate.c); - 计算整文件的 SHA-256,但跳过签名段与密钥段区域,按相同长度的 0x00 字节参与哈希(
sha256_file,src/validate.c); - 把签名和摘要分别写临时文件,调用
gpg2 --verify signaturefile digestfile完成验证(src/validate.c)。
用法:validate Signed.AppImage。
4.3 摘要工具(digest / digest_md5)
仓库还提供digest(src/digest.c)与digest_md5(src/digest_md5.c)两个工具:前者基于 OpenSSL 计算校验摘要,后者是验证 AppImage 内嵌 MD5 摘要的示例程序。二者均由 src/CMakeLists.txt 构建(digest依赖 libssl,未找到时自动跳过;digest_md5默认不参与全量构建,标注EXCLUDE_FROM_ALL)。
五、从源码构建 AppImageKit
5.1 Docker 构建
AppImageKit 官方提供的二进制由本仓库的 CI(Travis CI + CentOS 6 Docker)构建。作为应用开发者,通常不需要使用构建系统,直接下载预编译二进制即可;只有在为 AppImageKit 贡献代码、需要x86_64之外的其他架构,或想复现官方二进制时才需要自己构建。构建步骤:
git clone --single-branch --recursive https://github.com/AppImage/AppImageKit cd AppImageKit/ bash ci/build.sh产物会生成在out/目录。
5.2 构建系统选择的关键约束
README 特别强调:不建议也不支持在比各大发行版最老仍受支持版本更新的构建系统上构建 AppImageKit,原因正是本文第一节讨论的二进制兼容性问题——在旧系统上构建的二进制能在更新的目标系统上正常运行。当前官方以 CentOS 6.x 与 Ubuntu 14.04 作为构建系统。
5.3 构建配置与交叉编译
仓库的 CMake 构建体系(CMakeLists.txt)值得注意的细节:
- 版本信息:
VERSION 1-alpha,commit、build number、日期分别来自 git 与 Travis 环境变量(CMakeLists.txt); - 依赖管理:
mksquashfs默认由 cmake/dependencies.cmake 从 squashfs-tools 4.4 源码拉取构建(USE_SYSTEM_MKSQUASHFS可切换为系统版本),并静态链接 xz 支持; - 交叉编译:仓库预置了
cmake/toolchains/下的aarch64-linux-gnu.cmake、arm-linux-gnueabihf.cmake、i386-linux-gnu.cmake工具链文件,对应 runtime 源码中支持的 i386/x86_64/armhf/aarch64 四种架构(src/appimagetool.c); - 安装布局:
mksquashfs与runtime默认安装到lib/appimagekit(AUXILIARY_FILES_DESTINATION,见 src/CMakeLists.txt),appimagetool 会相对自身可执行文件位置查找 mksquashfs(src/appimagetool.c)。
六、CI 测试:如何验证 appimagetool 行为
仓库的 ci/ 目录提供了两个可直接运行的验证脚本,是理解 appimagetool 行为边界的极好素材:
- ci/test-appimage.sh:针对 appimagetool 自身 AppImage 的冒烟测试——无参数运行应失败、
-h/--version/--appimage-version/--appimage-updateinformation应成功;还支持PATCH_OUT_MAGIC_BYTES=1环境变量,把偏移 8 处的魔数抹掉后再跑测试,用于验证魔数的必要性。 - ci/test-appimagetool.sh:用 appimagetool 打包自身 AppDir 的端到端测试,覆盖:
- 无
.appimageignore时文件被打包,创建.appimageignore后对应文件被排除(L93-L96); --exclude-file显式排除文件与.appimageignore同时生效(L98-L109);- 可复现构建:同一 AppDir 两次构建产物 SHA-256 一致(L111-L119,对应 issue #625);
--mksquashfs-opt透传:-mem 100M不影响哈希,而-all-time 12345(改变 mtime)会改变哈希(L121-L135);- mksquashfs 失败时 appimagetool 应报错退出(L137-L145)。
- 无
七、结语
从 README.md 出发,结合 src/runtime.c、src/AppRun.c、src/appimagetool.c 等核心源码可以看到:AppImage 的"一文件跨发行版"体验,建立在 runtime 的 FUSE 挂载 + 环境变量隔离、appimagetool 的 squashfs 封装 + ELF 段元数据注入这套精巧设计之上。对应用开发者而言,掌握--appimage-extract、便携化目录、--comp/--exclude-file/--sign等关键选项,即可在 CI 中产出可复现、可签名、可增量更新的 AppImage 交付物;对想深入原理的读者,本文给出的源码定位可以帮你快速切入 runtime 与 appimagetool 的实现细节。
- 开发工具
【免费下载链接】AppImageKit
Package desktop applications as AppImages that run on common Linux-based operating systems, such as RHEL, CentOS, openSUSE, SLED, Ubuntu, Fedora, debian and derivatives. Join #AppImage on irc.libera.chat
相关推荐
3分钟定位AppImage运行故障:AppImageKit调试终极指南
3分钟定位AppImage运行故障:AppImageKit调试终极指南 你是否遇到过AppImage文件双击无反应?终端运行只显示"权限被拒绝"却找不到原因?本
开发工具Crossplane 包运行时配置(Package Runtime Config)设计解析:DeploymentRuntimeConfig 与 `--package-runtime` 机制详解
Crossplane 包运行时配置(Package Runtime Config)设计解析:DeploymentRuntimeConfig 与 package
云原生后端diy-layout-creator 的 Linux AppImage 构建环境搭建:WSL 与 AppImageTool 实战指南
diy layout creator 的 Linux AppImage 构建环境搭建:WSL 与 AppImageTool 实战指南 本篇指南基于 diy la
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考