AppImage vs Snapcraft vs Flatpak2019-11-19
This is a rant-sized report of honestly tring both Flatpak and Snapcraft. And only name-dropping AppImage. On Linux, I'm a GNOME user and use Ubuntu (best "standard" Linux distro/Debian repos) and Fedora (best upstream GNOME and freedesktop.org support) on my machines. I'm obviously biased.
Warning: Walls of text! What follows is a dump of a few hours spent with this last weekend. YOTLD.
I recently cleaned up and updated an old GTK+ tool I still
use every now and then
for breaking up audio files. It's
written in C and apart from GTK uses
as its dependencies, so it's quite a small, nifty utility.
Since I want that tool available on macOS and Windows too, I set up Travis CI to build packages (Travis CI has macOS build machines, and Windows builds are done using Fedora in a Docker image, since Fedora has nice packaging for most of mingw-w64).
As a baseline, the Windows release (that ships with all required libs thanks to Martin Preisler's wonderful mingw-bundledlls and a custom "copy icons from the Adwaita icon theme but only the ones I think the app uses" script) is ~ 9.35 MiB.
The Linux/BSD app packaging situation is always a little
tricky. You can rely on distributions packaging the update
eventually, or you could add the
.spec file (for RPM,
although different RPM-based distros have different rules
and naming conventions) and
debian/ folder (for DEB),
but even then, you might need to build against really old
versions of libraries (to be maximally compatible) or have
builds for different OS versions. Static linking isn't
really an option here.
There's basically 3 competing "standards":
- AppImage: Developed by a single developer (Simon Peter); basically a single file that you make executable and run, ships with all dependendent libraries; no real sandboxing/security features, as it's basically just a binary that you run as user, for a discussion on the sandboxing state, just look at: AppImageKit#839
- Snapcraft: Supported by Canonical; bundles all dependencies (no "platforms" to depend on, but there's something called "core18" that I haven't been able to figure out what it is) has sandboxing built-in, targets "everything" (server, GUI, IoT) so less focused on just Desktop apps (the case for this exercise)
- Flatpak: Supported by RedHat and GNOME/freedesktop.org; tailored towards packaging GUI applications; can depend on "platforms", so that one doesn't need to ship GTK and all the libraries, but just depend on the platform (smaller packages); has sandboxing built-in; libraries not in the platform must be shipped in the Flatpak; repository-based, but one can "export" a package into an installable .flatpak file
AppImage has been around since 2004 as "klik", around 2011 seems to be when the current tech was created (according to Wikipedia).
I skip AppImage here, since the community seems to be promoting the solution a bit too heavily and apart from tooling (which is important!) it's not much better (in terms of sandboxing) than shipping binaries with dependencies in a relocatable tarball.
Seriously, it's good stuff, but would need more tooling in the sandboxing and "avoid shipping everything and the kitchen sink" area to be a serious contender.
According to Wikipedia, snapd/Snappy has been around since 2014.
I tried snapcraft; it has not one but two different tutorials:
The second one is asking for "your current level of expertise",
which tries to be friendly to newbies, but in reality it's making it
hard to navigate. Why not have a Single Tutorial that covers all
bases? At least the docs are better,
and I like the fact that it uses YAML (Flatpak uses JSON). The
is fine, but if I want to find out what
base: core18 means, I
click through to core18, and it
doesn't tell me anything (yes it's some base thing -- what does
it contain and what is it good for?!). If there was a kind of
"gui" base that includes GTK and related libraries, a snap
built with that base could be quite compact (similar to what
"platforms" in Flatpak are?).
The tutorial doesn't really mention possible values for
(in my case, Meson), and
source-type (in my case, local) but
hey, I figured it out somehow.
Also, the tutorial uses DOSBox as an example (this is probably
fine), but then it has custom environment variables like
DISABLE_WAYLAND: '1' in there that tells you more about the
Year Of The Linux Desktop than anything else. If the Canonical
engineers would have just picked another package or spent the
time to fix up DOSBox to work with Wayland, then the YOTLD
might come sooner than "always next year".
One upside of snapcraft is that it can work on macOS and Windows too, so you could build snaps on non-Linux machines. Canonical uses its own Multipass utility for this (yay NIH!), which is basically the same as Docker, but with a Ubuntu/Canonical flavor (I do think that multipass has a bit nicer CLI interface than Docker for spinning up local dev VMs, though -- when it works). snapcraft uses Multipass by default to get a clean build environment, but I had trouble running it in Travis CI (which only has Ubuntu 18.04, and it doesn't properly work if using e.g. Ubuntu 19.10 via Docker).
And right now, my local multipass (on Ubuntu) seems to be broken,
it works until the
Launched: snapcraft-<packagename> line
and then does nothing. Tool-specific things. Hard to debug.
But when it did work, the package was ~ 34 MiB compressed, with
the biggest file being
libicudata.so.60.2 at ~ 25.7 MiB, followed
libgtk-3.so.0.220.30 coming in at ~ 7 MiB. Yes, every snap has
to ship its own ICU. Apparently
nobody at Canonical thought it would be a good idea to put ICU
into the "base" image? Then again, maybe I just did something
wrong and wouldn't need to ship those libs in the snap? I would
imagine that even most server apps would (indirectly) need ICU?
ICU seems to be fine for base. Put it in base!
After the first successful build of the snap, installing and running the snap failed, but the instructions in this thread helped here, and theming (on Ubuntu) seems to work fine. But why can't snapping GTK GUI apps be part of the official docs?
I don't get why I need to specify all
tool even helpfully lists libs that I missed -- why do I need
to add it explicitly and it can't figure it out similar to
dpkg-shlibdeps on Debian? And why do I need to list
X11/Wayland libs when I don't really care (in this app) about
which backend GTK happens to be using?
My app uses the Meson build system, and support for Meson
is built into snapcraft, but I needed to specify
meson-parameters: ["--prefix=/usr"] to make it work
(can't Snap do that automatically? Flatpak seems to be
able to figure that detail out for me automatically).
Some positive points: The file format is just a squashfs, and
one can use
unsquashfs -l <filename>.snap to list the
file contents. Also, the filename is as nice as what Debian
tools build, meaning
It also seems to be super picky about "owning" the
folder in the project, up to the point of complaining
if any other files are in that folder than what it expects
(it tells you to use
snap/local/ for any "custom" files).
So no moving packaging-related files off to e.g.
packaging/snap/ by default. However, you can create a
snap symlink to whatever subfolder you have and it will
be fine. I do wish
snapcraft would optionally allow
snapcraft.yaml file on the command line
instead of enforcing its file structure, but it's more or
less a minor issue that can be worked around with symlinks
snapcraft.yaml file I came up with is 94 lines and
mostly lists run-time shared library dependencies
stage-packages). For whatever reason, I also needed to
pull step and
Icon= line in the
.desktop file to contain the full path to the icon file,
I don't get why snap can't figure out the full icon path
from the icon name, after all, the
has been around for some time now.
Bad: Snapcraft "owns"
$HOME/snap, which appears in the
file manager and
ls by default, which is bad. There is
for this, but it's basically the snap engineers arguing
why it could be a good idea and everybody else telling
them what year it is ("It's 2017", "It's 2019", ...).
I started a thread on the snapcraft Forum about how to shrink the package size, it's still not perfect, but first tries resulted in a package size of ~ 344 KiB, which is quite acceptable (app + libao + libmpg123), but snapcraft complains that some libraries I link against (they should come from the GNOME snap) are not shipped with the snap, hm...
Flatpak started out in 2007 as "glick", with work on xdg-app beginning in 2014, which was renamed to Flatpak in 2016.
It uses JSON, and while I prefer YAML for something I type manually, JSON is Just Fine. The Flatpak JSON file I came up with is 35 lines, and there's basically no boilerplate code, all meat. Sweet!
One weird thing is that the sandboxing options (allowing
access to the Wayland, X11 and PulseAudio sockets) are
"finish-args", which doesn't sound intuitive.
About the sandboxing permissions: I also don't want to
specify both Wayland and X11, I want to say "this uses
GTK, so whatever the current display manager, I want to
have access to that".
The defaults are good, there is really good documentation
and the package is super small (120 KiB, including libao).
GTK is not shipped in the package, because it's in the
Freedesktop Platform Flatpak, which is installed as a
dependency and shared between all installed apps (this is
A Good Thing!). Interestingly,
mpg123 is also shipped
with the Freedesktop Platform, so I didn't even need to
ship it, which was a bit of a suprise (I wouldn't have
expected it, but I don't mind having to ship less, but
that brings up the point of where do I see a list of
files and libraries shipped with the freedesktop platform?).
As of this writing,
prevents me from installing the Flatpak, but forcing it
--no-deps makes it work without issues.
Flatpak is based on
ostree and has the concept of repos,
which makes it kind of hard to "inspect" the contents of
the Flatpak. Snap is much nicer with
unsquashfs -l in
this regard. I have found some hints that one can unpack
the ostree bundle, but it's quite tedious, there should
be an easy way to inspect the contents of a Flatpak
bundle before installing (and also to verify the contents
and paths look good after building the package).
Here's a super annoying big issue: How and where do I specify the version of the package? I haven't found a way do to this yet, all it says is "master".
In a similar vein, in order to build a "package" file, I
first have to build the Flatpak into a local repo and
then export the bundle from there? I'm sure there was
some thinking behind that, but the Snap way of outputting
.snap file by default (that can then be put into a
repo in a second step) seems more natural to me.
How do I figure out which freedesktop.org platform versions are on which distro? Is it OK to go with 19.08 or should I stick to 18.08 for now? My suspicion is that it does not matter, and it will just install the runtime in parallel if needed, so I'll just pick the most recent / most convenient.
Good: User data is stored in
it's hidden by default, but easy to access if needed (and
easy to calculate per-app disk usage, as it's separated).
I still hope that there's a place for distribution packages.
The leaf packages ("apps") do need some kind of sandboxing,
as sandboxing is good and important to have (not only to
protect the "root" user, but protect the user's data, as
usually there's more privacy-related juicy info in
than in all of the system folders).
Even with Flatpak, there's still some duplication (e.g. the GTK version from the distro package and the Freedesktop Platform Flatpak), but it's better than Snap's "let every snap ship with its own version of ICU" or AppImage's "just run this executable, it contains Everything" approach.