Słomkowski's technical musings

Playing with software, hardware and touching the sky with paraglider.

Compiling C++ application for Windows under Linux


Despite using Arch Linux for day-to-day computing I sometimes need to write some simple code in C++ for Windows. Switching operating systems back and forth is cumbersome so is compiling under virtual machine.

Windows port of GCC is named MinGW (Minimalistic GNU for Windows) and, quite surprisingly, it is available in repositories of major Linux distributions with wealth of open-source libraries cross-compiled for Windows platform.

Using this tool, writing multiplatform software can be frictionless. It works well in IDE Clion too. For the demonstration I provide example repository which contains sample CMake project of DLL and Windows executables built by MinGW.

Installing MinGW toolchain

Under Arch Linux I recommend ownstuff unofficial repository. The compiler package is named mingw-w64. You might try compiling it from AUR but it is a real pain. There are also many packages for various libraries; all package names start with mingw-w64- prefix. More detail on Arch Wiki.

MinGW is also available in official repository under Debian - meta package gcc-mingw-w64. There are also tons of precompiled libraries; all having mingw in package’s name. There is also Debian Wiki entry concerning this topic.

General configuration

The most important thing is to define the compiler executables and set CMAKE_SYSTEM_NAME:

cmake_minimum_required(VERSION 3.0)

project(mingw-test)

set(CMAKE_SYSTEM_NAME Windows)

SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
set(CMAKE_RANLIB i686-w64-mingw32-ranlib)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)

Then you simply define executable. It doesn’t require further comments.

add_executable(example example.cpp)

To cross compile a dynamic loadable library you use add_library, but for convenience it’s better to set PREFIX and SUFFIX to empty strings. This way you can use the whole filename (with .dll extension) as target name:

add_library(shared_lib.dll SHARED shared_lib.cpp shared_lib.h)
set_target_properties(shared_lib.dll PROPERTIES
        PREFIX ""
        SUFFIX ""
        LINK_FLAGS "-Wl,--add-stdcall-alias"
        POSITION_INDEPENDENT_CODE 0 # this is to avoid MinGW warning; 
        # MinGW generates position-independent-code for DLL by default
)

To link executable with aforementioned DLL, you add:

target_link_libraries(example shared_lib.dll)

Using pkg-config

The common way of defining library compilation flags and directories under Unix is to use pkg-config tool. This way your codebase is not dependent on the exact layout of directories of the distribution. Each library provides .pc file with the build parameters like header locations etc. Many packages compiled for MinGW also provide .pc files, at least under Debian and Arch Linux.

For example, lets add zlib as dependency:

Load PkgConfig package and find zlib:

include(FindPkgConfig)
find_package(PkgConfig REQUIRED)

pkg_check_modules(ZLIB "zlib")

Add dependencies to example executable:

include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(example ${ZLIB_LIBRARIES})

Adding runtime libraries to Wine

When you try to run your freshly cross-compiled program under Wine, it probably will fail with the message like this:

0009:err:module:import_dll Library libstdc++-6.dll (which is needed by L"your-program.exe") not found

It means that Wine cannot find runtime libraries which your program depends on. They are usually placed under /usr/i686-w64-mingw32/bin. You’ll need to add this path to PATH variable within Wine subsystem.

Edit file ~/.wine/system.reg. This is the representation of Windows Registry under Wine. Find PATH variable definition under [System\\CurrentControlSet\\Control\\Session Manager\\Environment] section. Append the library path as translated to Windows-like path: Z:\usr\i686-w64-mingw32\bin so it looks like this:

"PATH"=str(2):"C:\\windows\\system32;C:\\windows;C:\\windows\\system32\\wbem;Z:\\usr\\i686-w64-mingw32\\bin"

Running with Wine under Clion

Your CMake project should load in Clion without issues. Windows-specific headers like windows.h are available. Build will configure itself automatically, but running the target will not. Fortunately you can create Run/Debug configuration to run it under Wine - as shown in the picture:

Run/Debug configuration window.
You can easily configure the target to run under Wine within Clion.