Packaging native binaries

The most easy packaging methods are available for native binaries, like e.g., produced when compiling C++ or C code. Native binaries have a well defined and reliable behavior to find their runtime dependencies, the so-called shared libraries. These are the primary dependencies you will have to ship with your application. Of course, some applications might require additional resources, e.g., icon files. Also, some applications try to load libraries dynamically during the runtime. But for now, let’s assume we have a basic binary application (this is the most common type).

The AppImage team provides tools that simplify the packaging process significantly. These tools are semi-automatic, and ship with various features needed to bundle said shared library dependencies correctly. The one we are going to use in this guide is linuxdeploy.

linuxdeploy is an AppDir maintenance tool. Its primary focus is on AppDirs, AppImage is just one possible output format. It features a plugin system for greater flexibility in use. Plugins can either bundle additional resources for e.g., frameworks such as Qt, but are also used to provide output generators, e.g., for AppImages.

Packaging from source

Building applications from source and packaging them as AppImages is the most common scenario. In this section, it is described how apps that were built from source can be packaged into AppDirs, from which AppImages are being generated.

Using the build system to build the basic AppDir

If you use a modern build system (such as for instance CMake or qmake), you can use the provided make install commands to create an AppDir-like directory that can be used with linuxdeploy.

As install configurations usually install all binaries, libraries, resources etc. in a way defined by the application author, this method provides a very easy and fast way to set up the basic AppDir.

Note

Of course, the application authors need to set up install configurations in their buildsystem, otherwise this method is not usable. Many applications have working install configurations, though. If not, you should ask the authors to add the relevant code to their build system.

CMake

CMake provides an additional parameter to configure where the files are installed when running make install called DESTDIR. If DESTDIR is specified, CMake will “install” the files into the given directory instead of the filesystem root (/).

Note

By default, CMake sets an internal variable called CMAKE_INSTALL_PREFIX to a path other than /usr to prevent users calling e.g., sudo make install from damaging their system. The variable must explicitly be set to /usr therefore.

Here’s an example how to use this method:

# fetch sources (you could as well use a tarball etc.)
> git clone https://github.com/linuxdeploy/QtQuickApp.git
> cd app

# build out of source
> mkdir build
> cd build

# configure build system
# the flags below are the bare minimum that is needed, the app might define additional variables that might have to be set
> cmake .. -DCMAKE_INSTALL_PREFIX=/usr

# build the application on all CPU cores
> make -j$(nproc)

# now "install" resources into future AppDir
> make install DESTDIR=AppDir

Now, ideally all the binaries and libraries the app needs are installed into a new directory called AppDir in your build directory.

Note

The quality of the install configurations will vary from app to app. Please don’t be surprised if the application is installed partially only. If the command doesn’t exist at all, please fall back to bundling manually, which is described below.

qmake

Qt’s qmake also provides a variable to change the “target” of make install calls called INSTALL_ROOT. The qmake-based method is very similar to the CMake one. There’s just one major difference: qmake does install into /usr by default already.

Preparing a basic application is very simple, as the following example illustrates:

# get the source code
> git clone https://github.com/linuxdeploy/QtQuickApp.git
> cd QtQuickApp

# create out-of-source build dir and run qmake to prepare the Makefile
> mkdir build
> cd build
> qmake ..

# build the application on all CPU cores
> make -j$(nproc)

# use make install to prepare the AppDir
> make install INSTALL_ROOT=AppDir

Now, you have a new directory AppDir which ideally contains all the binaries, shared libraries etc., just like after finishing the CMake method.

Using linuxdeploy for building AppImages

Now that we have the basic AppDir, we need to bundle dependencies into it to make the AppDir self-contained in preparation to make an AppImage from it. The following guide shows how linuxdeploy is used for this purpose.

linuxdeploy describes itself as an “AppDir maintenance tool”. Its primary focus is on AppDirs, and it uses plugins to create output formats such as AppImages.

The following section describes how it can be used to deploy dependencies of applications into an AppDir that was created using the methods described in the previous section, and shows how this AppDir can eventually be packaged as an AppImage.

See also

Please see linuxdeploy user guide for more information on how to use linuxdeploy.

Bundling resources into the AppDir

Start by downloading linuxdeploy. The recommended way to get it is to use the AppImages provided on the GitHub release page.

Note

At the moment, AppImages are provided for x86/i386 and x86_64/amd64 architectures, as other platforms cannot be targeted properly on the build service. The tool itself should support all major platforms, including ARM. You can compile linuxdeploy yourself to test it. Contributions adding new platforms welcome!

After downloading the AppImage, you have to make it executable, as usual. Then, you can first run linuxdeploy on your AppDir:

> ./linuxdeploy-x86_64.AppImage --appdir AppDir

This creates AppDir if it doesn’t exist yet. Inside AppDir some basic directory structure is created that isn’t necessarily required, but might be handy when adding resources manually to the AppImage.

Note

linuxdeploy supports an iterative workflow, i.e., you run it, and it will start to bundle resources. If there is a problem, it will show a detailed error message, and exit with an error code. You can then fix the issue, and call it again to try again. See ref-linuxdeploy-iterative-workflow for more information.

If your application has installed itself properly, it should have installed a desktop file and an icon as well. The desktop file is used for AppImage desktop integration, and since desktop files require icons, an icon is always required, too.

Example:

# get linuxdeploy's AppImage
> wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
> chmod +x linuxdeploy-x86_64.AppImage

# run linuxdeploy and generate an AppDir
> ./linuxdeploy-x86_64.AppImage --appdir AppDir

You can bundle additional resources such as icon files, executable and desktop files using the respective flags described in the --help text or on linuxdeploy’s homepage.

Note

Desktop file and icon are used for so-called desktop integration. If your build system didn’t install such files into the right location, you can have linuxdeploy put your own files into the right places. Please see linuxdeploy-bundle-desktop-files-icons for more information.

Packaging existing binaries (or: manually packaging everything)

Packaging existing binaries is very simple as well. As the existing binaries don’t provide facilities to create a basic AppDir with the build system, you have to package everything into the right place manually.

Luckily, linuxdeploy supports such a workflow as well. It provides functionalities to automatically put the most common resources an application might use (such as binaries, libraries, desktop files and icons) into the right places without having the user to create any sort of structure or know where to put files. This is described in Packaging binaries and other resources manually.

Note

Many applications require more resources during runtime than just the binaries and libraries. Often, they require graphics for drawing a UI, or other files that are normally in a “known good location” on the system. These resources should be bundled into the AppImage as well to make sure the AppImage is as standalone as possible. However, linuxdeploy cannot know which files to bundle.

Please consult the applications’ documentation (e.g., homepage or man pages) to see what kinds of resources must be put into the AppImage. This can involve some trial-and-error, as you need to test your AppImages on different systems to find possible errors.

Warning

In order to be packaged as AppImages, applications must load the resources relative to their main binary, and not from a hardcoded path (usually /usr/...). This is called relocatability.

If your app doesn’t load resources from the AppImage, but e.g., shows errors it couldn’t find resources, it is most likely not relocatable. In this case, you must ask the author of the application to make it relocatable. Many modern frameworks such as Qt even provide functionality to implement this easily. In some cases, there’s also flags you can specify when building from source to make applications relocatable.

Bundling additional resources using linuxdeploy plugins

As mentioned previously, linuxdeploy provides a plugin system. So-called “input” plugins can be used to bundle additional resources, such as Qt plugins, translations, etc.

Please see Using input plugins for more information.

Build AppImages from AppDir using linuxdeploy

As mentioned previously, linuxdeploy uses plugins to create actual output files from AppDirs. For AppImages, there’s linuxdeploy-plugin-appimage.

To create AppImages, just add --output appimage to your linuxdeploy call to enable the plugin. An AppImage will be created using appimagetool.

Minimal example:

> ./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage

As most plugins, linuxdeploy-plugin-appimage provides some environment variables to enable additional functionality, such as:

SIGN=1
Sign AppImage. See ref-signing-appimages for more information.
UPDATE_INFORMATION=zsync|...
Add update information to the AppImage, and generate a .zsync file.

See also

More information on the environment variables can be found in the README, including a complete (and up to date) list of supported environment variables.

Examples

In this section, some examples how linuxdeploy can be used are shown.

QtQuickApp

This section contains a few example scripts that showcase how AppImages can be built for QtQuickApp, a basic demonstration app based on QtQuick, using some QML internally. It can be built using both CMake and qmake. We use it to show some example scripts how AppImages can be built for it, using the methods introduced in this guide.

Using qmake and make install

The following script might be used to create AppImages for QtQuickApp, using qmake and make install strategy.

travis/build-with-qmake.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#! /bin/bash

set -x
set -e

# building in temporary directory to keep system clean
# use RAM disk if possible (as in: not building on CI system like Travis, and RAM disk is available)
if [ "$CI" == "" ] && [ -d /dev/shm ]; then
    TEMP_BASE=/dev/shm
else
    TEMP_BASE=/tmp
fi

BUILD_DIR=$(mktemp -d -p "$TEMP_BASE" AppImageLauncher-build-XXXXXX)

# make sure to clean up build dir, even if errors occur
cleanup () {
    if [ -d "$BUILD_DIR" ]; then
        rm -rf "$BUILD_DIR"
    fi
}
trap cleanup EXIT

# store repo root as variable
REPO_ROOT=$(readlink -f $(dirname $(dirname $0)))
OLD_CWD=$(readlink -f .)

# switch to build dir
pushd "$BUILD_DIR"

# configure build files with qmake
# we need to explicitly set the install prefix, as CMake's default is /usr/local for some reason...
qmake "$REPO_ROOT"

# build project and install files into AppDir
make -j$(nproc)
make install INSTALL_ROOT=AppDir

# now, build AppImage using linuxdeploy and linuxdeploy-plugin-qt
# download linuxdeploy and its Qt plugin
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage

# make them executable
chmod +x linuxdeploy*.AppImage

# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS="$REPO_ROOT"/src

# QtQuickApp does support "make install", but we don't use it because we want to show the manual packaging approach in this example
# initialize AppDir, bundle shared libraries, add desktop file and icon, use Qt plugin to bundle additional resources, and build AppImage, all in one command
./linuxdeploy-x86_64.AppImage --appdir AppDir -e QtQuickApp -i "$REPO_ROOT"/resources/qtquickapp.png -d "$REPO_ROOT"/resources/qtquickapp.desktop --plugin qt --output appimage

# move built AppImage back into original CWD
mv QtQuickApp*.AppImage "$OLD_CWD"

Note

We’re using a separate bash script that runs in an isolated, temporary directory to prevent modifications to the existing source code or the system.

Many examples “hack” those instructions directly into their CI configuration, e.g., .travis.yml. This approach has many problems, most notably that it’s impossible to test those scripts locally. By extracting the whole process into a script, it becomes quite simple to test the build script locally as well as run it in the CI system.

An example .travis.yml is included in a later section, showing how the script can be run on Travis CI. It’s quite generic, you should be able to copy it without having to make too many modifications.

Using CMake and make install

The following script might be used to create AppImages for QtQuickApp, using qmake and make install strategy. It is effectively the same script as the qmake one, but uses CMake instead of qmake to build the binaries and install the data into the AppDir.

travis/build-with-qmake.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#! /bin/bash

set -x
set -e

# building in temporary directory to keep system clean
# use RAM disk if possible (as in: not building on CI system like Travis, and RAM disk is available)
if [ "$CI" == "" ] && [ -d /dev/shm ]; then
    TEMP_BASE=/dev/shm
else
    TEMP_BASE=/tmp
fi

BUILD_DIR=$(mktemp -d -p "$TEMP_BASE" AppImageLauncher-build-XXXXXX)

# make sure to clean up build dir, even if errors occur
cleanup () {
    if [ -d "$BUILD_DIR" ]; then
        rm -rf "$BUILD_DIR"
    fi
}
trap cleanup EXIT

# store repo root as variable
REPO_ROOT=$(readlink -f $(dirname $(dirname $0)))
OLD_CWD=$(readlink -f .)

# switch to build dir
pushd "$BUILD_DIR"

# configure build files with CMake
# we need to explicitly set the install prefix, as CMake's default is /usr/local for some reason...
cmake "$REPO_ROOT" -DCMAKE_INSTALL_PREFIX=/usr

# build project and install files into AppDir
make -j$(nproc)
make install DESTDIR=AppDir

# now, build AppImage using linuxdeploy and linuxdeploy-plugin-qt
# download linuxdeploy and its Qt plugin
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage

# make them executable
chmod +x linuxdeploy*.AppImage

# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS="$REPO_ROOT"/src

# initialize AppDir, bundle shared libraries for QtQuickApp, use Qt plugin to bundle additional resources, and build AppImage, all in one single command
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage

# move built AppImage back into original CWD
mv QtQuickApp*.AppImage "$OLD_CWD"

Integrate build scripts into CI systems

Travis CI

The scripts introduced in the previous subsections will move the files back into the directory where they’re called. Therefore, the .travis.yml and especially the script file can be kept delightfully short:

.travis.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
language: cpp
# needed for installing packages with apt
sudo: required

# we use the Qt PPAs to have some more recent Qt versions that are compatible with Ubuntu trusty
before_install:
  - sudo add-apt-repository ppa:beineri/opt-qt-5.10.1-trusty -y
  - sudo apt-get update

# install Qt from the PPA, and set up environment so that calls to Qt tools end up in the new Qt installation
install:
  - sudo apt-get -y install qt510base qt510declarative
  - . /opt/qt*/bin/qt*-env.sh

# use CMake based build script shown in previous section
script:
  - bash travis/build-with-cmake.sh

# OPTIONAL: uploading binaries using uploadtool
# see https://github.com/probonopd/uploadtool for more information

branches:
  except:
    # Do not build tags that we create when we upload to GitHub Releases
    # required for uploadtool
    - /^(?i:continuous)/

See also

Please see the Bundling your Travis CI builds as AppImages section in the Hosted services section for more information on Travis CI. It also contains a guide on uploadtool.