A Docker image for building portable Qt applications

I've uploaded to the Docker hub an image that I will be using for building my Qt applications and packaging them into an AppImage file. To which you might be asking: why bother?

The point is that this is a slightly special image: as its Docker tag (trusty-qt512) says, this image ships Qt 5.12 on top of Ubuntu Trusty (yes, that's 14.04!). The reason why I'm using such an obsolete distribution is that I'm striving to get my AppImage package to be as portable as possible: in fact, my early builds of PhotoTeleport were run on a more recent Ubuntu 16.04, but some users complained that they couldn't run it in their own distributions (IIRC, these were Arch Linux and, indeed, Ubuntu 14.04). The problem is that while packages in the AppImage format bundle their own dependencies in the package, some libraries are generally excluded, as bundling them can cause a variety of issues. But that means that if you are building your application in a system which ships newer versions of these libraries than those installed in your users' machines, there's a high risk that your application will be using some newer symbols not present in the older versions — and therefore your application won't run in those machines.

To solve this problem, the recommended approach is to build on the oldest system that you wish to support; and given that I'm mostly familiar with Ubuntu, I chose Trusty as my target. Luckily, the Qt binary installer shipped at qt.io also happens to support Trusty, so I'm all settled. Indeed, Trusty does not ship recent versions of the C++ compiler, but that can be easily solved thanks to a wonderful PPA which ships up to g++-8.
I've also added the latest version of cmake, as it's unlikely that one can live with the very old version that's shipped in Trusty.

The Docker image can be pulled and used in your own machine or set as a baseline in your .gitlab-ci.yml recipe, if you happen to use Gitlab and are interested to generate the AppImage file from there. That's what I'm trying to do for another project of mine, and while I'm not there yet, the goal is getting closer and closer. Those interested in seeing exactly what is going into my Docker image can have a look at its Dockerfile. There are a few issues with this approach, though, and I feel that I should highlight them in order not to raise your expectations too much:

  • You must B.Y.O.D. (Bring Your Own Dependencies)! It's likely that at least some of the libraries used by your project won't exist in Trusty, or will be an older version unsuitable for your needs. That means that your .gitlab-ci.yml will have to be modified to retrieve a newer version of the library and build it prior to building your application. There are good and bad sides to this: the good one is that this encourages you to use the very latest version of the dependency, which otherwise you might not have used by preferring your distro's version; the bad side is that this is extra work for you (and for the Gitlab CI), and that you risk missing some critical security updates if you don't closely follow the upstream development of your dependencies — something that your distribution's maintainers are likely to do for you. Anyway, here's an example of how I've changed Imaginario's CI recipe to build and fetch exiv2 from its upstream site, with caching of the build artifacts.
  • Gitlab CI becomes slower! Compared to my previous builds, each job has become one or two minutes slower. This is mostly due to not using ubuntu:16.04 as a baseline: the standard Ubuntu images are widely popular, so Gitlab is playing smart and caching them. On the other hand, it seems that my custom image is being pulled every time from the Docker hub. :-(

Of course, if you know of a better way to build a portable binary while not renouncing to the benefits of using a newer Linux distribution, you are very welcome to let me know in the comments. :-)

Comments