Docker Image for Qt Development and Continuous Integration on x64 and arm64

GitLab CI

NOTE: this project now also includes Android builds of Qt. Refer to updated articles to know more.

Intro

It happens from time to time that I would benefit from having a simple Docker image with all the deps needed to build and run Qt apps on specific Qt versions. For instance, I may need to build some binary on Mac OS for Linux or I may need to have an image for some continuous integration system.

Problem

I had a look around and there are quite a few projects: some do not provide an arm build, some rely on the Qt official installer, that is a bit tricky to automate and tends to change often, some rely on distro-provided packages, that do not provide every version etc… I’d prefer to have a solid build procedure to build images for any Qt version I want. Also, my CI system runs on arm64, so I’d need a multiarch image, and therefore two different Qt builds. I thought it could be simpler to just build Qt on-the-fly in a Dockerfile but… turned out it is not that simple.

Solution

At first I created a Dockerfile to:

  1. install all the needed deps on Ubuntu focal;
  2. download Qt sources;
  3. configure;
  4. build;
  5. install;
  6. cleanup.

This proved to be a simple solution, but building Qt for arm on qemu or building on arm took way too much. So I though of a different solution. I created two images:

With the first image Qt can be built and cross-built in a few hours for both x64 and arm64, directly on x64. The builds are then injected into a second multiarch image. This is way faster than building on qemu.

Only one problem left: in Qt 5, crossbuilding Qt results in some Qt tools like qmake to be built for the host arch, which is unacceptable in this case. So, in the second image, a build of the only qtbase module is also needed to build arm64 binaries. Qt 6 works differently instead.

Result

The code to build Qt and all the images are available at: https://github.com/carlonluca/docker-qt.
The image to build Qt is available at: https://hub.docker.com/repository/docker/carlonluca/qt-builder.
The image to use for app development is available at: https://hub.docker.com/repository/docker/carlonluca/qt-dev.

Continuous Integration on GitLab with Qt

An example of usage of the dev image is running continuous integration and unit tests on your code on Jenkins or GitLab. For example, I can now run my unit tests in my GitLab instance running on Raspberry Pi 4 with the following code (taken from my project https://github.com/carlonluca/lqobjectserializer):

variables:
  GIT_SUBMODULE_STRATEGY: recursive

stages:
  - test_qt5
  - test_qt6

Test_qt5:
  stage: test_qt5
  image:
    name: "carlonluca/qt-dev:5.15.2"
    entrypoint: [""]
  script:
    - cd LQObjectSerializerTest
    - mkdir build
    - cd build
    - cmake ../qt5
    - make
    - ./LQObjectSerializerTest
    - ./LGithubTestCase

Test_qt6:
  stage: test_qt6
  image:
    name: "carlonluca/qt-dev:6.1.2"
    entrypoint: [""]
  script:
    - cd LQObjectSerializerTest
    - mkdir build
    - cd build
    - cmake ../qt6
    - make
    - ./LQObjectSerializerTest
    - ./LGithubTestCase

These images are still experimental though.
Have fun 😉

Bye!

10 thoughts on “Docker Image for Qt Development and Continuous Integration on x64 and arm64”

  1. What are the minimum physical resources required to run a successful build?

    ./../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/objects/js-temporal-objects.cc -o obj/v8/v8_base_without_compiler/js-temporal-objects.o
    c++: fatal error: Killed signal terminated program cc1plus
    compilation terminated.
    [9676/30073] CXX obj/v8/v8_base_without_compiler/objects.o
    In file included from ../../../../../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/handles/handles-inl.h:10,
    from ../../../../../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/api/api-inl.h:12,
    from ../../../../../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/api/api-arguments-inl.h:9,
    from ../../../../../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/objects/objects.cc:13:
    ../../../../../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/execution/local-isolate.h: In member function ‘void v8::internal::LocalIsolate::FatalProcessOutOfHeapMemory(const char*)’:
    ../../../../../../src/qt-everywhere-src-6.5.2/qtwebengine/src/3rdparty/chromium/v8/src/execution/local-isolate.h:102:3: warning: ‘noreturn’ function does return
    102 | }
    | ^
    [9677/30073] CXX obj/v8/v8_base_without_compiler/elements.o
    ninja: build stopped: subcommand failed.
    [5547/7197] Automatic MOC for target SerialBus
    AutoMoc: /src/qt-everywhere-src-6.5.2/qtserialbus/src/serialbus/qcanframeprocessor.h:0:1: note: No relevant classes found. No output generated.
    [6066/7197] Building CXX object qtvirtualkeyboard/src/components/CMakeFiles/qtvkbcomponentsplugin.dir/.rcc/qmlcache/qtvkbcomponentsplugin_KeyboardLayout_qml.cpp.o
    FAILED: qtwebengine/src/core/Release/x86_64/QtWebEngineCore.stamp qtwebengine/src/core/Release/x86_64/QtWebEngineCore /build/qtwebengine/src/core/Release/x86_64/QtWebEngineCore.stamp /build/qtwebengine/src/core/Release/x86_64/QtWebEngineCore
    cd /build/qtwebengine/src/core && /usr/bin/ninja -C /build/qtwebengine/src/core/Release/x86_64 QtWebEngineCore
    [6072/7197] Building CXX object qtvirtualkeyboard/src/virtualkeyboard/CMakeFiles/VirtualKeyboard.dir/cmake_pch.hxx.gch
    ninja: build stopped: subcommand failed.

  2. Dude, thank you for your work and the images.

    After several hours trying to run qt build in CI I decided to search for a ready to use solution and found this post.

    It works like a charm and definitely saved several hours (or maybe days) for me.

  3. Hi Luca,
    i am interested in your project, but i can’t see the difference between qt-builder and qt-dev.
    On the other hand, I tried to launch the docker from qt-dev, but I’m not doing it right as it says it requires an argument.

    > docker run carlonluca/qt-dev
    /bin/bash: -c: option requires an argument

    1. Hello,
      qt-builder is used to build Qt itself. It includes all the libraries and the headers needed to build Qt from its sources. qt-dev is instead an image which includes the Qt build (resulting from the builder) and only includes what is needed to build a Qt app and run it (so some dev packages are not needed). A single qt-builder can be used to build many versions of Qt. There are instead qt-dev images for each Qt version.

      qt-dev images expect a command (https://github.com/carlonluca/docker-qt/blob/master/Dockerfile_6.x_jammy#L2). For instance:

      $ docker run -it –rm carlonluca/qt-dev:6.3.2 bash

      At this point you’ll have your Qt 6.3.2 in /opt:

      $ docker run –rm carlonluca/qt-dev:6.3.2 “ls /opt/”
      Qt-amd64-6.3.2
      Qt-arm64-6.3.2

      1. Thanks for the answer Luca.
        I am interested in creating a docker based on ubuntu (or any other) with Qt5.9.4 and I found your post interesting.
        Do you think it is possible to have a single docker with Qt to be compatible with an ARM64 and x86 based environment?
        Thanks so much.

        1. The images in qt-dev are all multiarch images, so are usable on both amd64 and arm64.
          You should be able to create a qt-dev image for 5.9 with the same technique.

Leave a Reply

Your email address will not be published. Required fields are marked *