Cross-compiling a static Qt5 app to Windows 10 from Linux

via Docker

qt | 2022-10-04

Statically cross-compiling Qt to Windows from Linux is a headache. You'll most likely spend a lot of time fighting Qt, CMake, Windows, etc.

Thankfully, we have solved that problem and share our Dockerfile that uses mingw to cross-compile in only 2 commands.

Steps

  1. Download our sample 'hello world' QtQuick (Qt 5.15) application
  2. preparing a docker base container for static Qt compilation
  3. cross-compiling our application, using the above container

Resulting in:

  • A single, self-contained .exe - 60MB.
  • Runs on a fresh Windows 10/11 installation without any additional requirements
  • ANGLE included (opengl->directx gfx translation layer)

Step 1: Download our sample application

The reason this article covers a QtQuick application is that these are harder to statically cross-compile than QtWidgets. So, this Dockerfile has both types covered (QtQuick, QtWidgets) which is nice.

We will be cloning qt5-qml-cmake-hello-world, a starter template.

git clone https://github.com/kroketio/qt5-qml-cmake-hello-world.git

Step 2: Build the base docker image

Next up, we'll prepare our docker container.

The Dockerfile, based on Ubuntu 20.04, compiles the following statically:

  • Qt5.15.2 + QtQuick support, without webengine
  • openssl 1.1.1k, libgpg, libgcrypt, zlib, libpng, libqrencode, libiconv, libevent

This takes around ~15 minutes depending on CPU speed and threads.

docker build -f Dockerfile.windows --tag app:win --build-arg THREADS=8 .

Note: You only need to build the base image once.

Also, you may wonder what Monero's depends build configuration (a derivative of Bitcoin's depends) is doing inside the Dockerfile: this toolchain supports reproducible builds. A topic for another article perhaps.

Step 3: Compile our sample application

The resulting binary will be over at: build/x86_64-w64-mingw32/release/qml_hello.exe

docker run --rm -it -v $PWD:/app -w /app app:win \
  sh -c 'make windows root=/depends target=x86_64-w64-mingw32 -j8'

That's it, your application will run on Windows 7, 8, 10, [...].

Gotchas

  • If you want to incorporate this Dockerfile in your own project, please note that both CMakeLists.txt and src/CMakeLists.txt have CMake code to facilitate static compiles. You would have to copy that code. Just search for the if(MINGW), if(WIN32), and if(STATIC) sections, and copy as you see fit.
  • This application will most likely not work inside Virtualbox and QEMU/KVM because of issues related to the vbox opengl graphics driver combined with Qt. I forgot the exact fix, but there is one.
  • The dockerfile includes the installation of random other libraries that are not relevant to our sample project, like: libevent, libgpg, etc. They are included for convenience. If you don't link against them, you won't use them.
  • The docker run command uses the Makefile to eventually call cmake. This is because the CMake command is long. If you want to pass CMake more flags, define them there.
  • This article covers Qt5 only. When we have a similar setup for Qt6, we will link it here.

Conclusion

For the past few years we have used this Dockerfile to create static Windows Qt builds in a CI/CD environment. We think it is a good replacement for both MSYS2 compiles on Windows or MXE-based toolchains.