Insert Title

Mai 27, 2017

Deploying apps with QtWebEngine for Ubuntu

Introduction

QtWebEngine is a very handy C++/Qt module for creating applications with embedded web content. It carries a complete Chromium browser and allows you to embed HTML/JS content in your application or to modify such content as you wish.

QtWebEngine

If you however want to deploy this application to Ubuntu, you will be faced with two problems:

  • QtWebEngine is not available in the official repository (yet). This means that you can't just create a .deb package, since the necessary dependencies won't be available. There are PPA's, but at least at the time of this writing, none of them worked.

  • You can't compile Qt statically with QtWebEngine included, since the codebase doesn't support it. Also you would have to make the source code of your application available under the terms of the LGPL, which could be unwanted if you chose a different license or if your application is proprietary.

Using a snap package

Note: As for now, this only works under 16.04. Under Ubuntu 16.10 you need to add -Wl,--as-needed to the linker flags, but I couldn't figure out how.

Luckily, we can overcome this problem by utilizing the new snap package format which has been introduced into ubuntu 16.04. A snap package will allow you to distribute your application shipped with a Qt version of your choice, allowing to include QtWebEngine and to comply with the terms of the LGPL (Note: I'm not a lawyer. Check with your legal experts if your application is proprietary and if you don't want to make your source code publicly available).

Creating the snap package

If you are new to snap packages, I'd advise you to read the official tutorial, called "Build your first snap". It takes about 5 minutes to complete and will tell you everything you will need to know to distribute your application.

The remainder of this article will assume that you have basic knowledge of snap packaging.

Adding your source code

We start first by adding your source code to the snapcraft.yml file. For the sake of this tutorial, I will use the QtWebEngine minimal example, which is included in the Qt sourcecode:

name: qtwebengine-snap-tutorial
version: "2017-18-02"
summary: A short demo on howto package Qt using snap

grade: devel
confinement: devmode

apps:
  qtwebengine-example:
    command: deploy/qtwebengine.sh

parts:
  example:
    # Replace this with the path to your sourcecode
    source: https://download.qt.io/archive/qt/5.8/5.8.0/submodules/qtwebengine-opensource-src-5.8.0.tar.xz
    source-subdir: qtwebengine-opensource-src-5.8.0/examples/webengine/minimal/

Including Qt

For adding Qt to our build, we will need to complete several steps:

  • Download the Qt sourcecode from download.qt.io.
  • Extract it
  • Install all necessary dependencies inside the snap environment
  • run the configure script will the right parameters. Important: Depending on what parts of Qt your application uses, you will maybe need to adjust this step.
  • Compile & install it.
  • Copy everything needed for the application into a special deploy folder.

The last step is needed since the QtWebEngineProcess looks for it's resource files at some hard-coded paths and in the path where the executable lies. Of course it would be nicer if we could omit this step and put everything into /usr in the snap environment, but as far as I know, this is not possible.

Anyway, here is the qt part for the snapcraft.yml file:

parts:
  qt:
    source: http://download.qt.io/archive/qt/5.8/5.8.0/single/qt-everywhere-opensource-src-5.8.0.tar.gz
    stage-packages:
        - libdbus-1-dev
        - libpci-dev
        - libxss-dev
        - gperf
        - bison
        - libegl1-mesa-dev
        - libfontconfig1-dev
        - libudev-dev
        - libudev1
        - re2c
        - libvpx-dev
        - libsnappy-dev
        - libsrtp0-dev
        - zlib1g-dev
        - libwebp-dev
        - libxslt1-dev
        - libevent-dev
        - libjsoncpp-dev
        - libopus-dev
        - libprotobuf-dev
        - libfreetype6-dev
        - libx11-dev
        - libxext-dev
        - libxfixes-dev
        - libxi-dev
        - libxrender-dev
        - libxcb1-dev
        - libx11-xcb-dev
        - libxcb-glx0-dev
        - libxcb-keysyms1-dev
        - libxcb-image0-dev
        - libxcb-shm0-dev
        - libxcb-icccm4-dev
        - libxcb-sync-dev
        - libxcb-xfixes0-dev
        - libxcb-shape0-dev
        - libxcb-randr0-dev
        - libxcb-render-util0-dev
        - libxcb-xinerama0-dev
        - libxcomposite-dev
        - libxcursor-dev
        - libxrandr-dev
        - libxtst-dev
        - libpng12-dev
        - yasm
        - bc
        - python
        - libbluetooth-dev
        - libopenal-dev
        - libgstreamer1.0-dev
        - libgles2-mesa-dev
        - libgl1-mesa-dev
        - mesa-common-dev
        - libglu1-mesa-dev
        - libgstreamer-plugins-base1.0-dev
    plugin: make
    prepare: |
        bash -c '$SNAPCRAFT_STAGE/../patch.sh'
    build: |
        ./configure -opensource -release -confirm-license -nomake tests -no-compile-examples -system-zlib -skip virtualkeyboard -skip wayland -skip charts -prefix $SNAPCRAFT_PART_INSTALL -I $SNAPCRAFT_PART_INSTALL/usr/include/freetype2 -I $SNAPCRAFT_PART_INSTALL/usr/include -L $SNAPCRAFT_PART_INSTALL/usr/lib/x86_64-linux-gnu/ -nomake examples -nomake tools -opengl desktop -qt-libpng -qt-xcb -v 2>&1 | tee qt-configure.log
        make -s -j $(echo $(nproc) +1 | bc)
        rm -v $SNAPCRAFT_PART_INSTALL/usr/include/x86_64-linux-gnu/bits/string3.h
        make -s install
        mkdir $SNAPCRAFT_PART_INSTALL/deploy
        cp -r $SNAPCRAFT_PART_INSTALL/plugins $SNAPCRAFT_PART_INSTALL/deploy/
        cp -r $SNAPCRAFT_PART_INSTALL/libexec/* $SNAPCRAFT_PART_INSTALL/deploy/
        cp -r $SNAPCRAFT_PART_INSTALL/resources/icudtl.dat $SNAPCRAFT_PART_INSTALL/deploy/
        cp -r $SNAPCRAFT_PART_INSTALL/resources/qtwebengine_resources_100p.pak $SNAPCRAFT_PART_INSTALL/deploy/
        cp -r $SNAPCRAFT_PART_INSTALL/resources/qtwebengine_resources_200p.pak $SNAPCRAFT_PART_INSTALL/deploy/
        cp -r $SNAPCRAFT_PART_INSTALL/resources/qtwebengine_resources.pak $SNAPCRAFT_PART_INSTALL/deploy/
        cp -r $SNAPCRAFT_PART_INSTALL/translations/qtwebengine_locales $SNAPCRAFT_PART_INSTALL/deploy/
        cp $SNAPCRAFT_STAGE/../example.sh $SNAPCRAFT_PART_INSTALL/deploy/
    stage:
        - bin/*
        - usr/*
        - deploy/*
        - lib/*
        - translations/*
        - resources/*

OK, this is a lot. Here is what's going on:

  1. We are installing several dependencies. These are needed for compiling Qt and QtWebEngine and my be needed to be adjusted by you, regarding which modules your application needs.

  2. The prepare step calls a custom bash script, which works around a bug in the C header files supplied by Ubuntu 16.04. The script & patch is listed below.

  3. Since we need to do several custom steps during the build process, we can't use snapcraft's autotools plugin. Instead, we utilize the more general make plugin and provide a custom build script.

  4. During the stage phase, we make sure that all files needed by QtWebEngine are carried over to the final package.

Patching the ubuntu C header files

One of the ubuntu header files uses the notation // for commenting at one point instead of /*. Since QtWebEngine uses the flags -pedantic -Werror internally, this causes the build process to fail. Therefore we need to patch this file:

--- ./usr/include/x86_64-linux-gnu/bits/string3.h   2016-11-16 21:53:50.000000000 +0100
+++ ./usr/include/x86_64-linux-gnu/bits/string3.h   2017-02-10 08:12:21.786385589 +0100
@@ -126,7 +126,7 @@
   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
    }

    -// XXX We have no corresponding builtin yet.
    +/* XXX We have no corresponding builtin yet. */
     extern char *__stpncpy_chk (char *__dest, const char *__src, size_t __n,
                            size_t __destlen) __THROW;
 extern char *__REDIRECT_NTH (__stpncpy_alias, (char *__dest, const char *__src,

This patch file called c-comment.patch is applied during the build stage by a helper script called patch.sh:

1
2
3
4
#!/bin/bash
pushd $SNAPCRAFT_PART_INSTALL
patch -p1 < ../../../c-comment.patch
popd

That's it; our snapcraft package is complete.

Final remarks

To be honest, this is a lot of stuff to deploy an application and the resulting package is rather big; furthermore, this doesn't seem to work with Ubuntu 16.10/17.04 due to changes in some libraries (if you know a solution, write me a e-mail).

Nevertheless, it shows that all kinds of programs can be packaged and deployed using snap.

Troubleshooting

  • If Qt doesn't build for you, change the -qt-xcb parameter of call to ./configure to -system-xcb (or the other way around).
  • The message Note: No relevant classes found. No output generated. is normal during to the compilation of Qt and does not indicate an error.