- english
- français
Cross-compiling for a Win32 environment
You must know how frustrating it is when you can't use your favorite softwares under a new operating system. Always have to reboot isn't an acceptable workaround for day to day use (read your mails, open a document, etc.). Now, you don't want your programs suffer the same limitation: you need them to run under many OS, without the need to restart your computer or use another one to compile.
If you used to work under Unix/Linux, set up an environment to build natively for your host system and for other targets should ease your job. The procedure is always the same, and here, we'll discuss a cross-compiler to build for Microsoft Windows, under Linux.
If your OS has a package management system, you should use it
first. You can then continue with the tests (Testing the cross-compiler
).
If the package manager doesn't provide something, or you
prefer to do it be yourself, you'll need the following
components:
-
MinGW.
This is a complete set of tools to work under Microsoft Windows,
but only 2 are usefull in our context:
- MinGW runtime (binary version, the name should look like mingw-runtime-x.x.tar.gz)
- Win32 API (binary version, the name should look like w32api-x.x.tar.gz)
- binutils.
- GCC. Here, we'll test the C (gcc-core) and C++ (gcc-g++) compilers only.
Target name
The same way your system use an id to qualify the platform, we need one to designate the target platform. Take the example below:
-
on an AMD Athlon XP based PC, under Linux:
$ gcc -dumpmachine i686-pc-linux-gnu
-
on an Intel Pentium MMX based PC, under FreeBSD:
$ gcc -dumpmachine i386-unknown-freebsd
As you can see, the name is divided into 2 parts: the architecture and the OS. Thus we prepare an id on these observations:
- the computer running Windows must be an ix86-pc (except for Windows NT for Alpha and Windows CE, but i never tested this). you must replace ix86 with your processor (i386 for a 386, i586 for a Pentium, i686 for a Pentium II and above).
- for the system, we don't use something derived from Windows, but a name which will recall the library we'll use. Two famous libs are Cygwin and MinGW. Here, we talk about MinGW, so mingw32.
At the end, here is what we have for the previous Athlon XP:
i686-pc-mingw32
MinGW
The prompt
During the process, most of the commands can run under an unprivileged user account. Despite this, installations may require a root account (for system-wide installs). By convention, commands which doesn't need an administrative account will use the following prompt:
$ command
Commands which require a root access will look like:
# commande
The first thing to do is to install the libraries and headers needed by binutils, GCC and all cross-compilations. It is now that you decide where you install the build tools. We'll take /usr/local/cross in this example. We begin with the creation (as root) of this directory, where we can untar both the packages:
# mkdir /usr/local/cross; cd /usr/local/cross cross# mkdir i686-pc-mingw32; cd i686-pc-mingw32
We made a directory named with the platform id. The packages go in it:
i686-pc-mingw32# tar zxf /path/to/mingw-runtime-3.3.tar.gz i686-pc-mingw32# tar zxf /path/to/w32api-2.5.tar.gz
As tar keeps the owner of every files, they may be owned by someone else. It is safe to change it back to root:
i686-pc-mingw32# chown -R root:root . i686-pc-mingw32# chown -R 664 * i686-pc-mingw32# chown -R +X *
The first archive, mingw-runtime, contains files required by the compiler and the linker to produce Windows binaries. The second brings the Win32 API, ie. standard headers and libraries.
Now is a good time to compile the binutils, than GCC. For these steps, you don't need to be root anymore.
Binutils
As usual, the building process can be divided into 3 parts:
- run the configure script
- build
- install
Once you untar'd the binutils' archive, you must create a directory at the same level, where the build will take place. It's recommanded to not work in the just uncompressed directory:
build$ tar jxf /path/to/binutils-2.14.tar.bz2 build$ mkdir binutils-build build$ cd binutils-build
We're ready to run the configure script. It'll take 2 arguments: one for the installation prefix (the directory we've created earlier, here /usr/local/cross), one for the target (or the binutils would build a version for the current host):
build/binutils-build$ ../binutils-2.14/configure \ --prefix=/usr/local/cross \ --target=i686-pc-mingw32
Compile time (not very long, compared to GCC):
build/binutils-build$ make
Once successfully done, the following command will install everything (requires a root access):
build/binutils-build# make install
Ok, finished for the binutils. You have to update your PATH, if the install path isn't already in it (this command will work for Bash; consult the document of your shell if necessary):
build$ export PATH="/usr/local/cross/bin:$PATH"
To test the binutils, you can issue the command:
build$ i686-pc-mingw32-ld -V GNU ld version 2.14 20030612 Supported emulations: i386pe
Binutils have been built to produce a i386pe
format and
PE is the one suported
by Microsoft Windows.
GCC
For GCC, we are going to do the same thing. Let's begin with the package uncompression, and the working directory:
build$ tar jxf /path/to/gcc-core-3.4.0.tar.bz2 build$ tar jxf /path/to/gcc-g++-3.4.0.tar.bz2 build$ mkdir gcc-build build$ cd gcc-build
Here, we're interested in the C and C++ compilers.
Next, the configure command line is the same as for the binutils:
build/gcc-build$ ../gcc-3.4.0/configure \ --prefix=/usr/local/cross \ --target=i686-pc-mingw32
This time, you must check the configure output: it should find the binutils' tools. Here's an example:
checking for i686-pc-mingw32-ar... i686-pc-mingw32-ar checking for i686-pc-mingw32-as... i686-pc-mingw32-as checking for i686-pc-mingw32-dlltool... i686-pc-mingw32-dlltool checking for i686-pc-mingw32-ld... i686-pc-mingw32-ld checking for i686-pc-mingw32-nm... i686-pc-mingw32-nm checking for i686-pc-mingw32-ranlib... i686-pc-mingw32-ranlib checking for i686-pc-mingw32-windres... i686-pc-mingw32-windres
If you don't obtain this, but the following output, the build won't be ok. Check your PATH:
checking for i686-pc-mingw32-ar... no
When everything is fine, we start the longer build process:
build/gcc-build$ make
At the end, you can test the compiler, but every run tests will fail obviously. So if you have uncompressed gcc-testsuite too, you can run make check.
We'll conclude this part with the installation of GCC, as root:
build/gcc-build# make install
Now that you're root, the PATH may be wrong and the make install may fail. To fix this, update your PATH. After this, you can go back to your normal user account (unprivileged).
A basic test consists of asking GCC the platform it was built for:
build$ i686-pc-mingw32-gcc -dumpmachine i686-pc-mingw32
Testing the cross-compiler
Now, i guess you can't wait to test all these, so let's start with the famous Hello World:
#include <stdio.h>
int main()
{
printf("Hello World !\n");
return 0;
}
You already known this source code, we can focus on the compilation:
build/hello$ i686-pc-mingw32-gcc -o hello.exe hello.c build/hello$ ls hello.c hello.exe
As you can see, GCC produced hello.exe. To run it, you may choose between Wine (or WineX) or a computer running Microsoft Windows. Here is the brilliant result:
build/hello$ wine hello.exe Hello World !
Next, we want to try a GUI example. The goal is to display a simple dialog box, to force us to use the windows.h header, and the Win32 specific entry point, WinMain:
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL,
"Cette fenêtre prouve que le cross-compilateur est fonctionnel !",
"Hello World", MB_OK);
return 0;
}
Time to compile it, as above:
build/hello$ i686-pc-mingw32-gcc -o hello_ui.exe hello_ui.c build/hello$ ls hello_ui.c hello_ui.exe
And run it:
build/hello$ wine hello_ui.exe
Isn't this screenshot amazing ?
Cross-compiling with a configure script
Now, the cross-compiler is fully operationnal. We've already seen how to do for a single file, but for a whole software, it may be more complicated. Hopefully, when a package uses autoconf/automake, things area still easy. As for the binutils or GCC, you must change the target:
build/monlogiciel$ ./configure --target=i686-pc-mingw32 build/monlogiciel$ make
Next, you must install the result on the target and test it.
For your own projects
To apply this to your own projects, there's nothing more to do. But the same way, Linux binaries must be linked to libraries sometimes, Windows binaries must linked to DLL. The syntax is the same (we are still using GCC and the binutils), and the variables to set this are the usual CFLAGS, CXXFLAGS, CPPFLAGS, LDFLAGS, etc. Therefore to link your program with libogg, just add -logg to LDFLAGS. Obviously, you need a Win32 binary of libogg.
Unlike Linux' .so
, Windows uses DLL (.dll
). But
when you are about to link your program, a second file, .lib
is required.