-
-
Notifications
You must be signed in to change notification settings - Fork 563
Creating AppImages
The general workflow for creating an AppImage involves the following steps:
- Gather suitable binaries. If the application has already been compiled, you can use existing binaries (for example, contained in .tar.gz, deb, or rpm archives). Note that the binaries must not be compiled on newer distributions than the ones you are targeting. In other words, if you are targeting Ubuntu 9.10, you should not use binaries compiled on Ubuntu 10.04. For upstream projects, it might be advantageous to compile special builds for use in AppImages, although this is not required.
- Gather suitable binaries of all dependencies that are not part of the base operating systems you are targeting. For example, if you are targeting Ubuntu, Fedora, and openSUSE, then you need to gather all libraries and other dependencies that your app requires to run that are not part of Ubuntu, Fedora, and openSUSE.
- Create a working AppDir from your binaries. A working AppImage runs your app when you execute its AppRun file.
- Turn your AppDir into an AppImage. This compresses the contents of your AppDir into a single, self-mounting and self-executable file.
- Test your AppImage on all base operating systems you are targeting. This is an important step which you should not skip. Subtle differences in distributions make this a must. While it is possible in most cases to create AppImages that run on various distributions, this does not come automatically, but requires careful hand-tuning.
While it would theoretically be possible to do all these steps by hand, AppImageKit contains tools that greatly simplify the tasks.
Now, on a more practical note, there are different ways to generate an AppImage of your application:
- Use Open Build Service (OBS)
- Convert existing binary packages (.deb, .rpm, ...)
- Use Travis CI
- Run
linuxdeployqt
on your Qt application - Use
electron-builder
for Electron-based apps - Create an AppDir manually
This option is recommended for open source projects because it allows you to leverage the existing Open Build Service infrastructure, security and license compliance processes. See https://github.com/probonopd/AppImageKit/wiki/Using-Open-Build-Service for how to use this.
This option might be the easiest if you already have up-to-date packages in place, ideally a ppa for trusty or earlier or a debian repository for oldstable. In this case, you can write a small .yml
recipe and in many cases are done with the package to AppImage conversion. The pkg2appimage script can run .yml recipes. See examples.
This option might be the easiest if you already have continuous builds on Travis CI in place. In this case, you can write a small scriptfile and in many cases are done with the AppImage generation. See examples.
This option might be the easiest if you build your application from source code using using cmake
, qmake
, or make
, and/or if you have a Qt-based application. In the latter case, you are probably already using windeployqt
and macdeployqt
and now can use linuxdeployqt
in the same fashion with the -appimage
argument and in many cases are done with the AppImage generation See example.
This option might be the easiest if you have an Electron-based application. In this case, you define AppImage as a target for Linux (default in the latest version of electron-builder) and in many cases are done with the AppImage generation. See examples.
Create an AppDir manually, then turn it into an AppImage. Start out with the example below, then check the examples on bundling certain applications or type of applications as AppImages from the right-hand side "Pages" menu.
In practice, you will probably never do this by hand. So this is mainly to illustrate the concept.
Create an AppDir structure that looks (as a minimum) like this:
MyApp.AppDir/
MyApp.AppDir/AppRun
MyApp.AppDir/myapp.desktop
MyApp.AppDir/myapp.png
MyApp.AppDir/usr/bin/myapp
MyApp.AppDir/usr/lib/libfoo.so.0
The AppRun
file can be a script or executable. It sets up required environment variables such as $PATH
and launches the payload application. You can write your own, but in most cases it is easiest (and most error-proof) to use a precompiled one from this repository.
Of course you can leave out the library if your app does not need one, or if all libraries your app needs are already contained in every base operating system you are targeting.
Your binary, myapp, must not contain any hardcoded paths that would prevent it from being relocateable. You can check this by running strings MyApp.AppDir/usr/bin/myapp | grep /usr
. Should this return something, then you need to modify your app programmatically (e.g., by using relative paths, using binreloc, or using QString QCoreApplication::applicationDirPath()
).
If you prefer not to change the source code of your app and/or would not like to recompile your app, you can also patch the binary, for example using the command
sed -i -e 's#/usr#././#g' MyApp.AppDir/usr/bin/myapp
This usually works as long as the application is not doing a chdir()
which would break this workaround, because then ././
would not be pointing to $APPDIR/usr
any more. You can run
strace -echdir -f ./AppRun
to see whether the application is doing a chdir()
(99% of GUI applications don't).
Also see https://www.gnu.org/software/gnulib/manual/html_node/Supporting-Relocation.html
It has been a pain for many users of GNU packages for a long time that packages are not relocatable. The relocatable-prog module aims to ease the process of making a GNU program relocatable.
Note: The same is true for any helper binaries and/or libraries that your app depends on. You check this and patch it with
cd MyApp.AppDir/usr/
find . -type f -exec sed -i -e 's#/usr#././#g' {} \;
cd -
which replaces all occurrences of /usr
with ././
, which simply means "here".
myapp.desktop should contain (as a minimum):
[Desktop Entry]
Name=MyApp
Exec=myapp
Icon=myapp
Type=Application
Categories=Utilities;
Be sure to pick one of the Registered Categories, and be sure that your desktop file passes validation by using desktop-file-validate your.desktop
. If you are not deploying an application with a graphical user interface (GUI) but a command line tool (for the terminal), make sure to add Terminal=true
.
Then, run appimagetool
on the AppDir in order to turn it into an AppImage. You can get it from this repository's Releases page (it comes as an AppImage itself; yes, we eat our own dogfood).
For an AppImage to run on most systems, the following conditions need to be met:
- 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.
- 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.
- The AppImage should actually be tested on the base systems that it is intended to run on.
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 libaries, 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 softwares that run on older systems. A way around this is to compile dependencies yourself on a not too recent base system, and/or to use LibcWrapGenerator.
When producing AppImages for the Subsurface project, I have had very good results by using CentOS 6. This distribution is not too recent (current major CentOS version minus 1) while there are still the most recent Qt and modern compilers for it in the EPEL and devtools-2 (the community equvalent of the Red Hat Developer Toolset 2) repositories. When using it for compilation, I found the resulting binaries to run on a wide variety of systems, including debian oldstable (wheezy).
Be sure to check https://github.com/probonopd/AppImages, 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/probonopd/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
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 (at the time of this writing, Ubuntu 14.04).
Some projects require newer C++ standards to build them. To keep the glibc dependency low you can
build a newer GCC version on an older distro and use it to compile the project. If you do this, however, then your compiled application will require a newer version of the libstdc++.so.6
library than available on that distro. Bundling libstdc++.so.6
however will in most cases break compatibility with distros that have a newer library version installed into their system than the bundled one. So blindly bundling the library is not reliable. While this is primarily an issue with libstdc++.so.6
in some rare cases this might also occur with libgcc_s.so.1
. That's because both libraries are part of GCC. You would have to know the library version of the host system and decide whether to use a bundled library or not before the application is started. This is exactly what the patched AppRun binary from https://github.com/darealshinji/AppImageKit-checkrt/ does. It will search for usr/optional/libstdc++/libstdc++.so.6
and usr/optional/libgcc_s/libgcc_s.so.1
inside the AppImage or AppDir. If found it will compare their internal versions with the ones found on the system and prepend their paths to LD_LIBRARY_PATH
if necessary.
To ensure that the AppImage runs on the intended base systems, it should be thoroughly tested on each of them. The following testing procedure is both efficient and effective: Get the previous version of Ubuntu, Fedora, and openSUSE Live CDs and test your AppImage there. Using the three largest distributions increases the chances that your AppImage will run on other distributions as well. Using the previous (current minus one) version ensures that your end users who might not have upgraded to the latest version yet can still run your AppImage. Using Live CDs has the advantage that unlike installed systems, you always have a system that is in a factory-fresh condition that can be easily reproduced. Most developers just test their software on their main working systems, which tend to be heavily customized through the installation of additional packages. By testing on Live CDs, you can be sure that end users will get the best experience possible.
I use ISOs of Live CDs, loop-mount them, chroot into them, and run the AppImage there. This way, I need approximately 700 MB per supported base system (distribution) and can easily upgrade to newer versions by just exchanging one ISO file. The following script automates this for Ubuntu-like (Casper-based) and Fedora-like (Dract-based) Live ISOs:
sudo ./AppImageAssistant.AppDir/testappimage /path/to/elementary-0.2-20110926.iso ./AppImageAssistant.AppImage
Please DO NOT put an AppImage into another archive like a .zip
or .tar.gz
. While it may be tempting to avoid users having to set permission, this breaks desktop integration with the optional appimaged
daemon, among other things. Besides, the beauty of the AppImage format is that you never need to unpack anything. Furthermore, packing an AppImage into some form of archive prevents the AppImage from being added to the central catalog of available AppImages at https://github.com/AppImage/AppImageHub.
By default, AppRun sets some variables such as LD_LIBRARY_PATH
before executing the payload application. While this is sufficient in most cases, it may lead to issues if the payload application launches other applications that reside in the base system, that is, outside of the AppImage. KDevelop is an example of such an application. In these cases, the appimage-exec-wrapper library can be used together with the AppImage distribution mechanism. Place the library somewhere in your AppImage and point LD_PRELOAD
to it before launching your application. Whenever your application invokes a child process through execv()
or execve()
, this wrapper will intercept the call and see if the child process lies outside of the bundled appdir. If it does, the wrapper will attempt to undo any changes done to environment variables before launching the process, since you probably did not intend to launch it with e.g. the LD_LIBRARY_PATH
you previously set for your application. linuxdeployqt, on the other hand, does entirely without setting LD_LIBRARY_PATH
by setting the RPATH in libraries and executables relative to $ORIGIN.
Yes, you can compile the AppImageKit tools for those architectures. The Open Build Service has support for it.
No, you need one AppImage for each architecture. FatELF could solve this, but is not merged into the mainline kernel, so it is currently not an option.
We recommend that you put the AppImage for Linux on your project's download page alongside the dmg for macOS and the exe for Windows, like so:
For open source projects, we recommend that you publish your AppImage in addition on GitHub Releases.
You also may want to add it to AppImageHub, the crowd-sourced directory of available AppImages.
Even under open source licenses, distributing and/or using code in source or binary form may create certain legal obligations, such as the distribution of the corresponding source code and build instructions for GPL licensed binaries, and displaying copyright statements and disclaimers. As the author of an application which you are distributing as an AppImage, you are responsible to obey all licenses for any third-party dependencies that you include in your AppImage, and ensure that their licenses and source code are made available, where required, together with the release binaries. AppImageKit itself is released under the permissive MIT license.