Best practices

This section contains some best practices and recommendations how to generally design and write software so that it can be easily put into AppImages.

General Recommendations

It is crucial to understand that AppImage is merely a format for distributing applications. In this regard, AppImage is like a .zip file or an .iso file. It does not define how to compile applications. It it is also not a build system.

It is crucial to put binaries inside AppImages that are compatible with a variety of target systems. What goes into the AppImage is called the “payload”, or the “ingredients”. Producing the payload requires some thought, as you want your AppImage to run on as many targets systems as possible.

For an AppImage to run on most systems, the following conditions need to be met:

  1. Binaries must not use compiled-in absolute paths (and if they do, they need to be binary-patched)

  2. The AppImage needs to include all libraries and other dependencies that are not part of all of the base systems that the AppImage is intended to run on.

  3. The binaries contained in the AppImage need to be compiled on a system not newer than the oldest base system that the AppImage is intended to run on.

  4. The AppImage should actually be tested on the base systems that it is intended to run on.

Binaries must not use compiled-in absolute paths

Since an AppImage is mounted at a different location in the filesystem every time it is run, it is crucial not to use compiled in absolute paths. For example, if the application accesses a resource such as an image, it should do so from a location relative to the main executable. Unfortunately, many applications have absolute paths compiled in ($PREFIX, most commonly /usr) at compile time.

Open source applications

Wherever possible you should change the Source Code of the application in order not to use absolute paths. There are several ways to do this. The canonical way on Linux is to resolve proc/self/exe to get the path to the main executable and construct a relative path from there. As a result, it should work both in normal installations and in relocatable installations such as AppImages.

There are libraries which make this easier, for example BinReloc. Also see Resourceful, a project to study of cross-platform techniques for building applications and libraries that use resource files (e.g. icons, configuration, data).

Some application frameworks such as Qt have this functionality built-in, for example in QString QCoreApplication::applicationDirPath() (see documentation), and construct a relative path to ../share/kaidan/images/ from there.

Warning

QStandardPaths::standardLocations(QStandardPaths::AppDataLocation) does not work reliably.

According to the Qt documentation, this resolves to ~/.local/share/<APPNAME>, /usr/local/share/<APPNAME>, /usr/share/<APPNAME>, but clearly /usr is not where these things are located in an AppImage.

Closed source applications with compiled-in absolute paths

In case it is not possible to change the source code of the application, for example because it is a closed source application, you could binary patch the executable.

The trick is to search for /usr in the binary and replace it by the same length string ././ which means “here”. This can be done by using the following command:

find usr/ -type f -executable -exec sed -i -e "s|/usr|././|g" {} \;

This command is also available as part of the bash function collection at AppImage/pkg2appimage/functions.sh#L79. For the binary-patched application to work, you need to change to the usr/ directory inside the application directory before you launch the application.

Binaries compiled on old enough base system

The ingredients used in your AppImage should not be built on a more recent base system than the oldest base system your AppImage is intended to run on.

Some core libraries, such as glibc, tend to break compatibility with older base systems quite frequently, which means that binaries will run on newer, but not on older base systems than the one the binaries were compiled on.

If you run into errors like this:

failed to initialize: /lib/tls/i686/cmov/libc.so.6: version `GLIBC_2.11' not found

then the binary is compiled on a newer system than the one you are trying to run it on. You should use a binary that has been compiled on an older system. Unfortunately, the complication is that distributions usually compile the latest versions of applications only on the latest systems, which means that you will have a hard time finding binaries of bleeding-edge software that runs on older systems. A way around this is to compile dependencies yourself on a not too recent base system, and/or to use LibcWrapGenerator or glibc_version_header or bingcc.

When producing AppImages for the Subsurface project, we have had very good results by using CentOS 7, which is the oldest still-supported Linux distribution at the time of writing. This distribution is not too recent. However, there are still the most recent Qt and modern compilers available in the EPEL and devtools-2 repositories (the community equivalent of the Red Hat Developer Toolset 2). Binaries built on this distribution run on nearly any distribution, including Debian oldstable.

Be sure to check https://github.com/AppImage/pkg2appimage, this is how I build and host my AppImages and the build systems to produce them in the cloud using travis-ci, docker, docker-hub, and bintray. Especially check the recipes for Subsurface and Scribus.

See https://github.com/AppImage/AppImageKit/wiki/Docker-Hub-Travis-CI-Workflow for a description on how to set up a workflow involving your GitHub repository, Docker Hub, and Travis CI for a fully automated continuous build workflow.

You could also consider to link some exotic libraries statically. Yes, even Debian does that: https://lintian.debian.org/tags/embedded-library.html

See also

This concept is also described in Build on old systems, run on newer systems.

libstdc++.so.6

Note

As a general rule of thumb, please use no libstdc++.so.6 newer than the one that comes with the oldest distribution that you still want to support, i.e., the oldest still-supported LTS version of Ubuntu.