Wednesday, August 11, 2010

Building a Cross Compiler on Linux for MinGW32

I was installing OpenVPN the other day and after getting everything up and running wanted to build an installer for Windows, to make it easier for the average person to be able to connect to the company VPN.

The basic idea was to prompt for a P12 cert and it's private key password during the installation and then install this along with the OpenVPN client. OpenVPN has an option "askpass", which allows you to store the private key password for a CRT or P12 cert. The build provided on openvpn.net, however, has this feature disabled. Due to this I had to recompile OpenVPN.

Not having Windows installed I knew I was going to have to figure out another way to get it to compile. I could either try and get MinGW32 running in Wine or setup a cross compiler environment. I thought the latter would be the easier option (as I was quite familiar with the existing Linux building environment). Boy was I wrong.

Either way. During the process of building the cross compiler, sorting out problem after problem I came across a script written by Paul Millar, which will extract, patch and compile MinGW32 for you as a cross compiler. This is a life saver.

To honor his hard work, even though still a bit tricky to use, I'm documenting it here.

Firstly, download and extract this script into ~/mingw-src. You can get it here. Inside this directory also create 2 other directories called Archive and Patches.

Then, get hold of the following source packages from http://www.sourceforge.net/projects/mingw/files/:
  • gcc
  • binutils
  • w32api
  • mingw-runtime
At the time of writing this, they had the following names:
  • gcc-3.4.4-3-msys-1.0.13-src.tar.lzma
  • binutils-2.19.51-3-msys-1.0.13-src.tar.lzma
  • w32api-3.14-3-msys-1.0.12-src.tar.gz
  • mingw-runtime-3.14-src.tar.gz
Then extract these archives into ~/mingw-src/Archive. The lzma archives would first have to be extacted with lzma into a temporary location, and the resulting tar archive extracted into ~/mingw-src/Archive.

Extract them one at a time, because some of them also contain patches. As you extract each of these, copy the patches into the ~/mingw-src/Patches directory, naming them {prefix}_name.patch, where {prefix} would be gcc, binutils, w32api or mingw-runtime_, depending on which archive the patch originated from.

For example, the binutils package has 2 patches, nl.
  1. 01-scriptdir.patch
  2. binutils-2.19.51-1-msys.patch
These would respectively be places in ~/mingw-src/Patches with these names:
  1. binutils_01-scriptdir.patch
  2. binutils_binutils-2.19.51-1-msys.patch.
The important part is the prefix and underscore. What comes after it doesn't actually matter. So you could name them binutils_1 and binutils_2 if you prefer.

When you're done with this, edit the parameters file, applying the following changes:
  1. Comment the LANGUAGES variable.
  2. There are 2 definitions of the DEFAULT_CC variable. The first assigns it the value gcc-4.0 and the second prefixes it with a distcc invocation. Change the value of the first to only gcc and comment the second.
  3. Comment the DISTCC_LOG and DISTCC_HOSTS variables.
  4. Change MAKE_PROCESS_COUNT to equal the number of CPU cores you have available, multiplied by 2, plus 1. So if you have 4 cores, this would be (4*2) + 1, which is 9.
  5. Comment the ROOT_TARGET variable.
  6. Comment the CLUSTER_DEPLOY variable.
  7. Check what the extensions are for your binutils, w32api and mingw-runtime archives in the ~/mingw-src/Archive directory. If it's not tar.gz for any of these, update the BINUTILS, W32API and RUNTIME variables to reflect the correct extension.
After the script has finished building the packages, change to ~/mingw32 where the packages were deployed and run the following commands:
cd ~/mingw32
cp -Rp i686-mingw32msvc/include/ i686-mingw32msvc/lib/ ./
rm -rf i686-mingw32msvc/include/ i686-mingw32msvc/lib/
ln -s ../include i686-mingw32msvc/include
ln -s ../lib i686-mingw32msvc/lib
Then you should have a working cross compiler environment in ~/mingw.

To use it, load the following environment:
PREFIX=$HOME/openvpn
TARGET=i686-mingw32msvc
TOOLCHAIN=$HOME/mingw32
BIN="$TOOLCHAIN/bin"
export PATH="$BIN:$TOOLCHAIN/$TARGET/bin:$PATH"
export CC="$BIN/$TARGET-gcc"
export LDFLAGS="-L$TOOLCHAIN/lib"
export CFLAGS="-I$TOOLCHAIN/include"
export CROSS_COMPILE="$TARGET"
Remember to update the value of PREFIX to where you want to install the resulting package, which in the example is ~/openvpn.

Once loaded you can compile a package (like OpenVPN) with the following command. This has to be run from the directory where it's source code is extracted.
./configure --prefix=$PREFIX --build=$TARGET && make && sudo make install
And that's all there is to it. The resulting package will be located in the directory where PREFIX points.

1 comment:

Unknown said...

Hi there,

I am trying to reproduce your procedure (using same versions of gcc, binutils, etc.) but I get stuck during the 'Building binutils package' section:

Any ideas or pointers will be gr8!

Errors:


&& CONFIG_FILES=po/Makefile.in:po/Make-in \
CONFIG_HEADERS= /bin/bash ./config.status
config.status: creating po/Makefile.in
config.status: executing depfiles commands
config.status: executing libtool commands
config.status: executing default-1 commands
config.status: executing bfd_stdint.h commands
config.status: executing default commands
make[3]: Leaving directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD/bfd/po'
make[3]: Entering directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD/bfd/po'
make[3]: Nothing to be done for `info'.
make[3]: Leaving directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD/bfd/po'
make[3]: Entering directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD/bfd'
make[3]: Nothing to be done for `info-am'.
make[3]: Leaving directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD/bfd'
make[2]: *** [info-recursive] Error 1
make[2]: Leaving directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD/bfd'
make[1]: *** [all-bfd] Error 2
make[1]: Leaving directory `/root/mingw-build-dir/binutils-2.19.51-1/BUILD'
make: *** [all] Error 2