How to create Snap packages of Qt applications

Introduction

One of the advantages of snap packages is that they are self-contained. When you install a snap, you know that you don’t need to install additional dependencies (besides the automatically-installed core snap that provides the basic operating system layer), and it will simply work on every Linux distribution that supports snaps.

Here, we show how to create self-contained snap packages for Qt-based applications, and we show an additional approach where some of the app dependencies are provided by a separate snap: the Ubuntu app platform snap. The platform snap provides an (optional) approach for the software provider, and can save disk space in some cases. Below we will explain the two approaches to building a snap for Qt-based software: a snap that is self-contained and includes Qt, and one that uses the platform snap, and we show the advantages of each approach. However, before showing these two approaches that you can apply to your own QML code, we demonstrate how to create a snap from deb packages in the Ubuntu archive so that you can get started right away even before you write any code.

We assume that before reading this blog post, you have acquired knowledge about how to use Snapcraft. So if you haven’t, we recommend reading the documentation on snapcraft.io and the snap-codelabs tutorials.

All the commands that are listed below are executed on an Ubuntu 16.04 LTS machine with the stable-phone-overlay PPA enabled. Some of the snapcraft commands may run on other configurations, but for the “Ubuntu App Platform Snap” section it is a hard requirement because the version of Qt - upstream 5.6 long term support version - and other libraries used to build the snap need to match the versions in the ubuntu-app-platform snap. Installing the snap packages works on different versions of Ubuntu and even different Linux distributions. The examples were tested on amd64 architecture with Intel graphics. If you are running this on a different CPU architecture, naturally the architecture in the directory and snap file names listed below must be modified. If you have an Nvidia GPU and use the Nvidia proprietary drivers there can be problems when running some snapped applications, so in that case we currently recommend to use the open source Nouveau drivers.

The examples are also available in a repository linked to from the Evaluation section.

Qt cloud parts - a simple use case

We will demonstrate how to build a simple app snap that includes the Qt release and Ubuntu UI Toolkit (UITK) from the Ubuntu archives. For this example, we use the UITK gallery which is part of the ubuntu-ui-toolkit-examples deb package on classic Ubuntu systems, so no additional code is needed. Because of that, we can simply use the nil plugin and pull in the examples as stage-packages. We create a directory called uitk-gallery which contains only a snapcraft.yaml file with the following contents:

name: uitk-gallery
version: '0.1'
summary: Showcase gallery that demonstrates the components in the Ubuntu UI Toolkit.
description: |
  See https://developer.ubuntu.com/api/qml/ for the API documentation of the components.

grade: devel
confinement: strict

parts:
  ubuntu-ui-toolkit-examples:
    plugin: nil
    stage-packages:
      - qmlscene
      - qml-module-qtqml-models2
      - ubuntu-ui-toolkit-examples
    after: [desktop-qt5]

apps:
  uitk-gallery:
    command: desktop-launch "qmlscene $SNAP/usr/lib/*/qt5/examples/ubuntu-ui-toolkit/examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml"
    plugs: [unity7, opengl, network]

(notes: the command line assumes you are on and targeting amd64 system. the plugs line is needed so that you have access to graphical subsystem from your confined app)

Under stage-packages we listed all the packages that need to be pulled from the Ubuntu archive, including their dependencies. ubuntu-ui-toolkit-examples contains all the QML code for the UITK gallery that we want to run using qmlscene. We also included qml-module-qtqml-models2 because some pages of the UITK gallery import QtQml.Models. The line after: [desktop-qt5] fetches the desktop-qt5 part from the remote parts repository. It will automatically pull in Qt 5 from the Ubuntu archive, set-up environment variables, and provide the desktop-launch script that is called to start the app. The snap file can be created simply by going to the uitk-gallery directory which contains the snapcraft.yaml file, and running:

snapcraft

Note that Snapcraft will ask for the sudo password to install the Qt5 dev packages that are required to compile Qt apps, but can be left out if all the dependencies are already present. Running snapcraft will create (on an amd64 machine) the file uitk-gallery_0.1_amd64.snap which can then be installed by:

snap install --dangerous uitk-gallery_0.1_amd64.snap

where the dangerous parameter is required because we are installing an unsigned snap that does not come from the Ubuntu store. Note that you do not need to use sudo if you have logged in with snap login. The UITK gallery can now be launched using:

uitk-gallery

The desktop-qt5 cloud part pulls in the current stable version of Qt of the Ubuntu 16.04 LTS release - 5.5.1 normally or 5.6.1 in the case of stable overlay PPA. To uninstall the UITK gallery snap before going to the next section, run:

snap remove uitk-gallery

QML project using parts from the cloud

If your existing QML code is not available as a deb package, then obviously you cannot pull it in from the archive when creating the snap using stage-packages. To show how to include your own QML code, we copy the UITK gallery code to the ubuntu-ui-toolkit-gallery directory inside the snapcraft (uitk-gallery) directory. Go to the parent directory of the uitk-gallery of the previous section, and run:

bzr branch lp:ubuntu-ui-toolkit
cp -R ubuntu-ui-toolkit/examples/ubuntu-ui-toolkit-gallery uitk-gallery/

Alternatively, if you have the ubuntu-ui-toolkit-examples package installed, you can run:

cp -R /usr/lib/*/qt5/examples/ubuntu-ui-toolkit/examples/ubuntu-ui-toolkit-gallery/ uitk-gallery/

You should now have both the snapcraft.yaml and the copied ubuntu-ui-toolkit-gallery directory that contains the source code of the UITK gallery under the uitk-gallery. We can now remove the ubuntu-ui-toolkit-examples from the stage-packages in the snapcraft.yaml file. Because that line is removed, the dependencies of the UITK gallery are no longer pulled in automatically, and we must add them to the YAML file, which then becomes:

name: uitk-gallery
version: '0.2'
summary: Showcase gallery that demonstrates the components in the Ubuntu UI Toolkit.
description: |
  See https://developer.ubuntu.com/api/qml/ for the API documentation of the components.

grade: devel
confinement: strict

parts:
  ubuntu-ui-toolkit-gallery:
    plugin: dump
    source: .
    stage-packages:
      - qmlscene
      - qml-module-qtqml-models2
      - qml-module-qt-labs-folderlistmodel
      - qml-module-qtquick-xmllistmodel
      - qml-module-ubuntu-components
      - ubuntu-ui-toolkit-theme
      - ubuntu-ui-toolkit-tools
    after: [desktop-qt5]

apps:
  uitk-gallery:
    command: desktop-launch "qmlscene $SNAP/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml"
    plugs: [unity7, opengl, network]

Note that besides the changes in stage-packages, also the location of ubuntu-ui-toolkit-gallery.qml was updated in the uitk-gallery command because the QML files are no longer installed in usr/lib inside the snap, but copied in the root of the snap filesystem. As before, the snap package can be created by executing:

snapcraft

inside the uitk-gallery directory. The UITK gallery can then be installed and started using:

snap install --dangerous uitk-gallery_0.2_amd64.snap
uitk-gallery

and uninstalled by:

snap remove uitk-gallery

Now that you have seen how to package the UITK gallery from source into a snap, you can do the same for your own QML application by using the dump plugin with the dependencies as stage-packages. If your application includes C++ code as well, you need to use another plugin, for example the qmake plugin. For that we refer to the Snapcraft tutorials mentioned in the introduction.

For those who like to experiment with newer versions of upstream Qt, we provide qt57 and qt58 cloud parts in the parts repository for Qt 5.7.1 and 5.8 (in development). However, the qt57 and qt58 cloud parts do not yet include a wrapper script similar to desktop-launch, so one must be included with with snap configuration, see for example timostestapp2. When using these cloud parts, you should usually omit any Qt/QML package from stage-packages, as the ones compiled from newer Qt are used directly, and you should also omit the after: [desktop-qt5].

Ubuntu app platform snap

Update 02/2017: Please note that no-system-libraries build-attribute is recommended now in order to avoid bundling of Qt and other libraries in the snap when the intention is to use ubuntu-app-platform instead. The parts section updated accordingly.

The snap files we created in the previous sections contain everything that is needed in order to run the UITK gallery application, resulting in a snap file of 86MB. Here we will explain how to use the Ubuntu app platform snap when you have multiple app snaps that all use the same Qt version.

Benefits of this approach include disk space saving, download time and bandwidth usage if metered.

When your snap uses the ubuntu-app-platform snap for Qt and other platform libraries, we can remove the stage-packages from the snapcraft.yaml file because (in this case), all the needed libraries are included in ubuntu-app-platform. We must also replace after: [desktop-qt5] by after: [desktop-ubuntu-app-platform]. This will set-up your snap to use the global desktop theme, icon theme, gsettings integration, etc. A more elaborate description of the desktop-ubuntu-app-platform is given in the parts list on the Ubuntu wiki. In the uitk-gallery directory we must currently create a directory where the files from the platform snap can be mounted using the content interface:

mkdir ubuntu-app-platform

and this empty directory (mount point) must be added in the YAML file as well. At this point the directory structure is as follows:

uitk-gallery/
 .. snapcraft.yaml
 .. ubuntu-ui-toolkit-gallery/
    .. [gallery contents]
 .. ubuntu-app-platform/

The whole YAML is:

name: uitk-gallery
version: '0.3'
summary: Showcase gallery that demonstrates the components in the Ubuntu UI Toolkit.
description: |
  See https://developer.ubuntu.com/api/qml/ for the API documentation of the components. ubuntu-app-platform snap must be installed for this snap to work.

grade: devel
confinement: strict

plugs:
    platform: # plug name, to be used later
        interface: content
        content: ubuntu-app-platform1 # content being mounted and the version, currently 1
        target: ubuntu-app-platform # the mount directory created
        default-provider: ubuntu-app-platform # default content source snap, currently the only provider too

parts:
  ubuntu-ui-toolkit-gallery:
    plugin: dump
    source: .
    after: [desktop-ubuntu-app-platform]
    prime: [ubuntu-ui-toolkit-gallery, ubuntu-app-platform]
    build-attributes: [no-system-libraries]

apps:
  uitk-gallery:
    command: desktop-launch "qmlscene $SNAP/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml"
    plugs: [platform, unity7, opengl, network]

Again, the new snap file can be created using:

snapcraft

This time, before we can use the snap, the ubuntu-app-platform snap must be installed and connected to the new uitk-gallery snap. So, execute the following commands:

snap install ubuntu-app-platform
snap install --dangerous uitk-gallery_0.3_amd64.snap
snap connect uitk-gallery:platform ubuntu-app-platform:platform
uitk-gallery

Note that the snaps must be connected before running uitk-gallery for the first time. If uitk-gallery has been executed before the snap connect you will see an error message. To fix the problem, uninstall the uitk-gallery snap, then re-install it and run the snap connect command before executing uitk-gallery. This is a known limitation in snapd which will be resolved soon.

Another note: once support for the default-provider, already defined above, will correctly be implemented in snap, there will no longer be a need to install the platform snap separately - it will be pulled from the store automatically and the interface connects automatically.

Evaluation

We demonstrated three different approaches to creating a UITK gallery snap, which we gave the version numbers 0.1, 0.2 and 0.3. For each of the approaches, the table below lists the time needed for the different stages of a snapcraft run, but the pull and build stages have been combined because when doing pull, some of the prerequisites need to be built already. The all stages row shows the total time when running the snapcraft command in a clean directory, so that all stages are executed sequentially, so the value is less than the sum of the previous rows in the table because in each stage it is not necessary to check completion of the previous stages.

Version (bzr revision)

0.1 (r1)

0.2 (r2)

0.3 (r3)

build (includes pull)

1m49s

1m48s

3.6s

stage

7s

7s

1.5s

prime

33s

34s

1.8s

snap

1m11s

1m13s

1.7s

all stages

3m32s

3m20s

4.0s

install

2.2s

2.4s

1.2s

snap file size

86 MB

86 MB

1.3 MB

The measurements were done on a laptop with an Intel Core i5-6200U CPU with 8 GB RAM and an solid-state drive by running each command three times and listing the average execution time. All build-dependencies were pre-installed so their installation time is not included in the measurements. Note that this table only serves as an illustration, and execution times will vary greatly depending on your system configuration and internet connection, but it can easily be tested on your own hardware by bzr branching revisions r1, r2 and r3 of lp:~tpeeters/+junk/blog-snapping-qt-apps.

The times and file size listed in the last column (version 0.3) do not include downloading and installing the ubuntu-app-platform snap, which is 135 MB (it includes more than just the minimal Qt and UITK dependencies of the UITK gallery). It can be seen that (depending on the internet connection speed, and which files were downloaded already), using the ubuntu-app-platform can speed up the pull and build time when creating a new snap file. However, the most important advantage is the reduction of the sum of the file sizes when installing multiple app snaps that all depend on Qt or other libraries that are part of the platform snap, because the libraries need to be installed only once. The disadvantage of this approach is that the app snap must be built using the exact same Qt (and other libraries) version as the ubuntu-app-platform snap, so the choice whether the snap should be fully self-contained or depend on the platform snap must be made individually for each use case.

Future work

The UITK gallery snap is not yet available in the Ubuntu store, so our next step will be to publish a UITK examples snap that includes the UITK gallery, and to set-up automatic publishing of that snap to different channels when we update the UITK or the examples. We will also evaluate what is the best way to make newer versions of Qt available and determine whether we need to provide prebuilt binaries to replace the qt57 and qt58 cloud parts.

Finally, we will determine which libraries need to be included in the ubuntu-app-platform snap. The plan is to include all APIs that are listed on https://developer.ubuntu.com/api/qml/ and if APIs are missing we will add them to that webpage as well as to the platform snap. Of course, if you think we are forgetting a library that is useful and used in many different applications, please leave a comment below.

Comments

No comments yet.

Add your comment