Docker Image for Qt Development and Continuous Integration on x64 and arm64
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:
- install all the needed deps on Ubuntu focal;
- download Qt sources;
- configure;
- build;
- install;
- 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:
- carlonluca/qt-builder is an image with the proper tools to build Qt for x64 and cross-build Qt for arm64;
- carlonluca/qt-dev is an image with the proper tools to build and run Qt apps.
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!
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.
I think for Chromium 8GB are required on Linux. But if you want to parallelize, probably 16GB or more.
Thank you, appreciate your effort!
Thank you. Have fun!
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.
Glad to hear that 👍
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
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
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.
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.