Skip to content

A Stackoverflow Question

To the question...

How to build a unified C++ cross-platform Windows and Linux (x86_64/aarch64) build system using Python, CMake, and Docker

The Problem

Developing C++ applications (specifically Qt-based ones) for multiple platforms usually involves a fragmented workflow:

  • Maintaining separate OS environments (Windows for MSVC, Linux for GCC).
  • Installing and managing massive toolchains (Qt, MinGW, MSVC, aarch64-linux-gnu) on every machine.
  • Managing multiple repository clones or constantly switching IDE contexts.
  • Dealing with CMakePresets.json configuration complexity when supporting multiple compilers and platforms.
  • Lacking a unified interface to switch between native builds, cross-compilation, and containerized builds.
  • Manual environment variable management for toolchain paths, Wine prefixes, and Docker volume mounts.
  • No standardized way to share toolchain configurations across projects or team members.

I want a way to build for Windows (native/MinGW/MSVC) and Linux (x86_64/aarch64) from a single Linux host without manually installing all these toolchains or switching operating systems. Ideally, this solution would:

  • Support Wine for cross-compiling Windows targets using MinGW or MSVC
  • Use Docker containers for isolated Linux builds (GCC x86_64/aarch64) including Wine for cross-compiling
  • Parse and use CMakePresets.json for preset-based configuration
  • Provide an interactive menu system to select build configurations
  • Automatically configure toolchain environments per platform
  • Allow optional portable toolchain installation to avoid massive system-wide installs

How can one orchestrate a Python-based build script that uses either native compilers or Docker containers to handle these different environments seamlessly?

My Solution

Python Build Script for C++ CMake Projects

I created this recursive called Python script for the Piper1GPL repository to build their C++ part (used for creating a python module) for different compilers so I could build my TTS Qt6 plugin with it.
The script is leaning heavily on CMake with the accompanied CMakePresets.json file (v6+) and allows compiling for multiple platforms with a single command:

  • Native Linux GCC -> ./build.py -mb gnu-debug
  • Native Linux GCC aarch64 cross-compiler -> ./build.py -mb ga-debug
  • Native Linux MinGW cross-compiler -> ./build.py -mb gw-debug
  • Wine + ...
    • Windows MSVC 2022 -> ./build.py -mb msvc-debug
    • Windows MinGW -> ./build.py -mb mingw-debug
  • Native Windows MSVC 2022 -> build.py -mb msvc-debug
  • Native Windows MinGW -> build.py -mb mingw-debug
  • Linux Docker + ...
    • Linux GCC -> ./build.py docker -- -mb gnu-debug
    • Linux GCC aarch64 cross-compiler -> ./build.py docker -- -mb ga-debug
    • Linux MinGW cross-compiler -> ./build.py docker -- -mb gw-debug
    • Wine +
      • Windows MSVC 2022 -> ./build.py docker -- wine -- -mb msvc-debug
      • Windows MinGW -> ./build.py docker -- wine -- -mb mingw-debug

The pattern here is clear and easy to understand since it is recursively called.
Unittests can be performed and applications run using the environment variables set from a the accompanied build.ini file using commands like these:

# Run all unit tests.
./build.py docker -- wine -- -t msvc-debug
# Run a specific application.
./build.py docker -- wine -- run -p msvc-debug -- piper-cli.exe

For a working boilerplate project use command: ./build.py install --project
It uses the script from sf-cmake GitHub repository to create a 'hello-world' project for the CLI and the Qt6 GUI.
Watch it in action on YouTube for Linux and Windows.
DoxyGen is also avaiable as build target named doccument which automatically seeks out the source files from the build targets and allows PlantUML files to be included.

Links: Docker image, Dockerfile

UML Diagram

uml diagram