Using Zig to cross-compile from Linux to Windows

I had the problem that I wanted to cross compile from Linux to Windows using mingw32, which worked, but as soon as some features of std::condition_variable were used, linking failed due to the mingw32 version being used on Ubuntu 20.04 VM (my build VM ...) being too old. Newer binaries were not available, and I did not want to compile it from source.

So, I used Zig to achieve the same (i.e. cross compile from Linux to Windows). Mind you: this is a C++ project with no parts in Zig.

First, make sure you install the Zig binary distribution and add the location containing the zig binary to your PATH.

As a starting point, I used the CMake integration from https://github.com/mrexodia/zig-cross, derived a .cmake toolchain file zig-toolchain-x86_64-windows-gnu.cmake from the existing one (zig-toolchain-aarch64.cmake) with the following contents:

if(CMAKE_GENERATOR MATCHES "Visual Studio")                                                                                                                                                                                                                                                                                    
    message(FATAL_ERROR "Visual Studio generator not supported, use: cmake -G Ninja")
endif()
#set(CMAKE_SYSTEM_NAME "Linux")
set(CMAKE_SYSTEM_NAME "Windows")
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
#set(CMAKE_C_COMPILER "zig cc -target x86_64-windows-gnu")
#set(CMAKE_CXX_COMPILER "zig c++ -target x86_64-windows-gnu")
set(CMAKE_C_COMPILER "zig_c_wrapper.sh")
set(CMAKE_CXX_COMPILER "zig_cxx_wrapper.sh")

if(WIN32)
    set(SCRIPT_SUFFIX ".cmd")
else()
    set(SCRIPT_SUFFIX ".sh")
endif()

# This is working (thanks to Simon for finding this trick)
set(CMAKE_AR "${CMAKE_CURRENT_LIST_DIR}/zig-ar${SCRIPT_SUFFIX}")
set(CMAKE_RANLIB "${CMAKE_CURRENT_LIST_DIR}/zig-ranlib${SCRIPT_SUFFIX}")

The wrapper scripts zig_c_wrapper.sh and zig_cxx_wrapper.sh were necessary because setting the CMAKE_C_COMPILER or CMAKE_C_COMPILER to something that is a combination of binary plus arguments does not work.

The contents are simply:
zig_c_wrapper.sh:

#!/bin/sh
zig cc -target x86_64-windows-gnu $@

zig_cxx_wrapper.sh:

#!/bin/sh
zig c++ -target x86_64-windows-gnu $@

After this, you can cross compile to Windows using cmake as follows (assuming you have the project set up with CMake already ... my project was an ImGui application with an external GLFW dependency that I could already compile Linux -> Linux and Windows -> Windows and can now also compile Linux -> Windows with the same CMake file):

mkdir build-zig-win
cd build-zig-win
cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=/path/to/cmake/zig-toolchain-x86_64-windows-gnu.cmake ..                                                                                                                                                                                             
make

The result is a statically compiled ImGui application that can run under Windows. It runs noticeably slower than an application compiled Windows -> Windows under mingw32, though, but I have not looked into that too much (since it's an internal utility mostly used by myself ...). Adding -O2 does not help, but turning verbose logging output out does.

Caveat: the minimum Windows version that Zig supports is 8.1 (the latest mingw version I used supported Windows 7); error manifests as failure to resolve function GetSystemTimePreciseAsFileTime() (see https://github.com/ziglang/zig/issues/5766).


You'll only receive email when they publish something new.

More from pmf
All posts