Windows 32/64 cross GCC with C++11 threads support
5 Apr 2015
This document explains how to build, on a POSIX system, a cross-compiling GCC targeting both 32 and 64 bit Windows systems, with C++11 threads support. It uses MinGW-w64.
These instructions were tested on Debian 7.0 (Wheezy) on an amd64 system.
Prerequisites
You will need the following packages:
For the following instructions, we’ll assume that you have these packages collected somewhere in a directory of their own, which will also be the root build directory.
You need to choose an installation prefix, and make sure that that
prefix is in your path. We’ll use the environment variable
$MINGW to designate this installation prefix. Set it before
starting, or substitute it by hand in the commands that follow. For
example:
MINGW=/opt/mingw64
You’ll also need to ensure that:
You have write access to
$MINGWfor installation purposes. If you’re building as a regular user, it would be best to create the directory owned by that user, and then change ownership to root later.You have the subdirectory
$MINGW/binin your path. Otherwise, some of the steps after building binutils will fail.You have build dependencies that GCC normally requires.
Building the compiler
Building binutils
The first step is to build and install binutils:
tar xfj binutils-2.25.tar.bz2
mkdir binutils-build
cd binutils-build
../binutils-2.25/configure \
--target=x86_64-w64-mingw32 \
--enable-targets=x86_64-w64-mingw32,i686-w64-mingw32 \
--prefix=$MINGW \
--with-sysroot=$MINGW
make && make install
cd ..
Double-check before proceeding that binutils is in your
PATH:
which x86_64-w64-mingw32-ld
Creating necessary symlinks
Before the next step, create the following directories and symbolic links in your installation prefix:
mkdir -p $MINGW/x86_64-w64-mingw32/lib32
ln -s x86_64-w64-mingw32 $MINGW/mingw
ln -s lib $MINGW/mingw/lib64
Installing MinGW-w64 headers
Unpack, configure, and install MinGW-w64 headers for the Windows API and C runtime:
tar xfj mingw-w64-v4.0.1.tar.bz2
mkdir mingw-headers-build
cd mingw-headers-build
../mingw-w64-v4.0.1/mingw-w64-headers/configure \
--prefix=$MINGW/x86_64-w64-mingw32 \
--host=x86_64-w64-mingw32 \
--build=$(gcc -dumpmachine)
make install
cd ..
Building the compiler
Next we unpack, configure and build GCC – but only the core compiler. We can’t build libgcc and the C++ library yet:
tar xfj gcc-4.9.2.tar.bz2
mkdir gcc-build
cd gcc-build
../gcc-4.9.2/configure \
--target=x86_64-w64-mingw32 \
--enable-targets=all \
--prefix=$MINGW \
--with-sysroot=$MINGW \
--enable-threads=posix
make all-gcc && make install-gcc
cd ..
Building the C runtime
We’ve already unpacked the MinGW-w64 package. Now we use it to build and install the C runtime:
mkdir mingw-crt-build
cd mingw-crt-build
../mingw-w64-v4.0.1/mingw-w64-crt/configure \
--prefix=$MINGW/x86_64-w64-mingw32 \
--with-sysroot=$MINGW \
--enable-lib32 \
--enable-lib64 \
--host=x86_64-w64-mingw32 \
--build=$(gcc -dumpmachine)
make && make install
cd ..
Building the winpthreads library
First, build and install a 64-bit version. We need to move the DLL after installation so that it’s not clobbered by the 32-bit version later:
mkdir mingw-wpth-build64
cd mingw-wpth-build64
../mingw-w64-v4.0.1/mingw-w64-libraries/winpthreads/configure \
--prefix=$MINGW/x86_64-w64-mingw32 \
--host=x86_64-w64-mingw32 \
--build=$(gcc -dumpmachine)
make
# At this point, the build will fail, due to the fact that we
# enabled POSIX threads in the compiler, but libpthread.a doesn't
# exist yet. Work around it and try again:
cp fakelib/libgcc.a fakelib/libpthread.a
make && make install
# Move the created DLL
mv $MINGW/x86_64-w64-mingw32/bin/libwinpthread-1.dll \
$MINGW/x86_64-w64-mingw32/lib64/
cd ..
Now build the 32-bit version. This is almost the same, except for the configure arguments and the final destination for the DLL:
mkdir mingw-wpth-build32
cd mingw-wpth-build32
../mingw-w64-v4.0.1/mingw-w64-libraries/winpthreads/configure \
--prefix=$MINGW/x86_64-w64-mingw32 \
--host=x86_64-w64-mingw32 \
--build=$(gcc -dumpmachine) \
--libdir=$MINGW/x86_64-w64-mingw32/lib32 \
CC='x86_64-w64-mingw32-gcc -m32' \
CCAS='x86_64-w64-mingw32-gcc -m32' \
DLLTOOL='x86_64-w64-mingw32-dlltool -m i386' \
RC='x86_64-w64-mingw32-windres -F pe-i386'
make
cp fakelib/libgcc.a fakelib/libpthread.a
make && make install
mv $MINGW/x86_64-w64-mingw32/bin/libwinpthread-1.dll \
$MINGW/x86_64-w64-mingw32/lib32/
cd ..
Building the compiler runtime
Now, we can finish the GCC build:
cd gcc-build
make && make install
cd ..
This step takes quite a while, but after that, it’s all done – you should have a working Windows cross-compiler.
Building libraries with the cross-compiler (optional)
To avoid problems arising from a lack of package multilib support,
I’d suggest using $MINGW/local64 and
$MINGW/local32 as installation prefixes for 64 and 32 bit
libraries. In this section, I’ll show how to compile two libraries for
32-bit Windows, in a way that minimizes distribution dependencies.
There’s nothing special about the choice of these libraries in particular – they’re just two that I’ve happened to need so far.
Building libusb-1.0
Building libusb 1.0 is fairly straightforward, once configured correctly:
tar xfz libusb-1.0.19.tar.bz2
mkdir libusb-build32
cd libusb-build32
../libusb-1.0.19/configure \
--prefix=$MINGW/local32 \
--host=x86_64-w64-mingw32 \
CC='x86_64-w64-mingw32-gcc -m32' \
DLLTOOL=true \
RC='x86_64-w64-mingw32-windres -F pe-i386'
We provide a dummy DLLTOOL option because gcc already
builds a functional import library. If you let libusb try to build its
own, it doesn’t work (you’ll see warnings from gcc about skipping an
incompatible libusb-1.0.dll.a if you try to link against it).
One minor annoyance if you’re trying to build a minimize dependencies
is that libtool builds shared libraries with shared dependencies. To
enforce static dependencies for a library and avoid the need to
distribute libgcc with your binary, edit libusb/Makefile
prior to the make command, and edit the line that specifies
LDFLAGS:
LDFLAGS = -Wc,-static
You can’t specify these when giving the configure command, because
they cause autoconf tests to fail. Specifying -static on
its own wouldn’t cause the tests to fail, but libtool interprets this as
an instruction not to build a shared library at all. Specifying
-Wc tells libtool to pass the flag to the compiler without
trying to interpret it.
Build and install the library with:
make && make install
cd ..
Note that you can use pkg-config to obtain the necessary compiler and linker flags for this (and many other) libraries when cross-compiling:
PKG_CONFIG_PATH=$MINGW/local32/lib/pkgconfig
pkg-config --cflags libusb-1.0
pkg-config --libs libusb-1.0
Building wxWidgets
wxWidgets for 32-bit is similarly straight-forward, although one minor fix-up is required after installation, as the libraries get installed with incorrect names:
tar xfj wxWidgets-3.0.2.tar.bz2
mkdir wxwidgets-build32
cd wxwidgets-build32
../wxWidgets-3.0.2/configure \
--prefix=$MINGW/local32 \
--host=x86_64-w64-mingw32 \
--disable-shared \
CXX='x86_64-w64-mingw32-g++ -m32' \
CC='x86_64-w64-mingw32-gcc -m32' \
WINDRES='x86_64-w64-mingw32-windres -F pe-i386'
make && make install
# The library names don't match what wx-config reports. Fix this:
for x in /opt/mingw64/local32/lib/libwx_*.a; do
y=$(echo "$x" | sed 's/-x86_64-w64-mingw32.a$/.a/')
mv "$x" "$y"
done
Above, I’ve built a static library. Remove the
--disable-shared argument to configure if you want a shared
library instead.
Notes on linking
If you want to minimize distribution dependencies, link with
-static. This will avoid the need to distribute the libgcc
and winpthread DLLs with your binary.
If you’re trying to build a mostly-static binary and want to link
dynamically against one or two libraries (e.g. libusb, which has an LGPL
license), link your binary as follows, wrapping the exceptional library
flags with these -Wl directives:
x86_64-w64-mingw32-g++ -m32 -static -o binfile.exe <dependencies> \
-Wl,-Bdynamic <dynamic dependencies> -Wl,-Bstatic
You can do this for any subset of libraries you want to link against.