A proof-of-concept implementation of AppImage created in shell script
The ultimate goal is to have it be almost identical in normal use, but it isn't
quite there yet. It's currently missing the ability to extract without requiring
FUSE on the system, along with requiring fusermount3
, which only exists on
FUSE3 systems. Assuming you're on a modern system with FUSE3 though, it should
work just like normal AppImages (assuming your file manager allows launching
scripts as applications instead of just opening them in an editor)
The shImg runtime has a longer initialization time compared to standard the standard runtime. I've tried to optimize it a bit, but it still takes about 0.08s on my (fairly bad) hardware (mind you, Python 3 takes 0.1s on my system just to initalize and make a print statement, so it probably won't hurt performance that much for most things).
Another difference is the default compression being LZ4 (LZ4_HC) instead of LZIB, I decided this because LZ4 compression is practically free while still getting a decent (40%-60%) compression ratio. ZSTD is also supported as an option for both mid and high compression at the cost of longer launch time, but it should still be signifirantly faster than both ZLIB and XZ. Larger apps quickly reveal the benefit of using decompression optimized for modern hardware. Using LZ4, I generally get apps 30%-50% larger than ZLIB, but the return is a near-native launch speed.
Overall it's pretty simple, the script checks if the user has squashfuse/dwarfs
binaries on their system (prefers this), if not it will extract a portable
binary to $XDG_RUNTIME_DIR
. It then uses the binary to mount the attached
filesystem image at the specified offset, runs AppRun then unmounts and cleans
up once finished. See File structure for more info
Eventually I would like to make a proper tool for building shImgs, but for the time being, manually building is simple enough.
- Assemble an AppDir
- Compress the AppDir into a SquashFS image
mksquashfs AppDir AppDir.sqfs -b1M -comp lz4 -Xhc
- LZ4 should be used for applications where launch speed is preferred, ZSTD should be used in applications where maximum compression is preferred.
- Download or build the shImg runtime
- Concatenate the runtime and filesystem image
cat runtime-[COMPRESSION]-[ARCHITECTURES] AppDir.sqfs > app.shImg
At this point, the shImg should be a working application given that it's marked
executable or launched directly through the interpreter (eg: sh ./app.shImg
)
but the desktop integration zip should also be
applied as it'll make it easier to integrate into the target system (once a
final structure is decided on and software is made to supoort it)
An AppDir is composed of all the files your application requires to run. It should be arranged in FHS, although any file layout works so long as your application is configured to load from relative locations.
- Applications MUST not use absolute paths for loading built-in resources
- Applications should make as few assumptions about the base system as reasonably possible. Static linking is preferable for maximum compatibility with systems like Alpine and NixOS.
- AppDirs in shImg may be extended to support multiple system architectures in
one image. To do so, you may either detect and run the appropriate binary
using your
AppRun
, or provide anAppRun.[ARCH]
eg:AppRun.aarch64
, which the shImg runtime will prefer if the user is running said CPU.
The shell script (with the help of some attached fuse binaries) do the same job as the standard AppImage type 2 runtime, simply trying to find the image offset as fast as possible, mount it and run the contained application inside the SquashFS bundle. The (possibly multiarch) payload is appended, which contains the app itself.
Finally, a zip archive is slapped on the end to serve as desktop integration information. Zip was chosen over other formats for its ability to be placed at an arbitrary offset and still be accessed, this allows desktop integration software to simply open the AppImage as if it were a normal zip file, no need to worry about what's going on up front.
╔═══════════════════════════════╗ ─╮
║ shell script ║ │
╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ │ ╭─────────╮
║ squashfuse binaries for all ║ ├─┤ runtime │
║ supported architectures ║ │ ╰─────────╯
╟───────────────────────────────╢ ─┴╮
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ SquashFS/DwarFS payload ║ │ ╭───────────────────╮
║ ║ ├─┤ meat and potatoes │
║ (LZ4_HC, ZSTD or GZIP) ║ │ ╰───────────────────╯
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ ║ │
║ ║ │
╟───────────────────────────────╢ ─┬╯╭───────────────╮
║ desktop integration zip ║ ├─┤ cherry on top │
╚═══════════════════════════════╝ ─╯ ╰───────────────╯
This is my current draft for desktop integration information in shImg. It
is simply a zip file appended to the end of the shImg. Uncompressed entries
are intended to be extracted via the shell script runtime, but this requirement
may be dropped if I find an easy enough way to extract them without needing
infozip
on the host machine.
Inside the destop integration zip, the directory tree is as follows:
.APPIMAGE_RESOURCES/
├─ destop_entry
├─ metainfo [OPTIONAL]
├─ update_info [OPTIONAL] [MUST BE UNCOMPRESSED]
├─ signature [OPTIONAL] [MUST BE UNCOMPRESSED]
└─ icon
├─ default.{png,svg}
├─ 16.png [OPTIONAL]
├─ 24.png [OPTIONAL]
├─ 32.png [OPTIONAL]
├─ 48.png [OPTIONAL]
├─ 64.png [OPTIONAL]
├─ 96.png [OPTIONAL]
├─ 128.png [OPTIONAL]
├─ 256.png
└─ 512.png [OPTIONAL]
desktop_entry
contains the app's .desktop file. metainfo
contains AppStream
metainfo, typically located at usr/share/metainfo/*.appdata.xml
. update_info
contains AppImage update information, along with a special header and footer to
make it easy to find in shell script. signature
is not yet impletmented, but
will be a GPG sig used for signing the shImg.
The only supported icon (default.png
, default.svg
) image formats are PNG
and SVG, and there should only be one "default" file. Thumbnailing images MUST
be PNG. 256.png is the only required image for thumbnailing, but more sizes may
also be added if desired.
- DwarFS is licensed under GPL3, the DwarFS shImg version may only be used with GPL3 software.
- This section of the runtime has much less testing than the SquashFS version and may come with more issues. It should currently only be used for testing