Win32 Cross Compiling with MinGW32


(jaybird) #1

I spent quite a while trying to get an installation of MinGW32, so I thought I’d write a small guide for those that would like to cross compile win32 binaries on Linux.

For those that don’t know, MinGW is a free program that can compile Windows programs on the Linux platform (and I’m sure others). You can read more about it http://www.mingw.org. However, there are a lot of files to download and a lot of choices to make about your installation, and it is no light undertaking.

I found a wonderful resource on the internet for a very easy automated MinGW installation. The code for it is as follows:

build-cross.sh


#!/bin/sh

# This is my script for building a complete cross-compiler toolchain.
# It is based partly on Ray Kelm's script, which in turn was built on
# Mo Dejong's script for doing the same, but with some added fixes.
# The intent with this script is to build a cross-compiled version
# of the current MinGW environment.
#
# Updated by Sam Lantinga <slouken@libsdl.org>

# what flavor are we building?

TARGET=i586-mingw32msvc

# uncomment or add entries here

# where does it go? (your cross tools install directory)
#PREFIX=/usr/local/cross-tools

# if the prefix you are using needs root to access it please uncomment this
# (if you are using the above default prefix most likely you need this
#DO_SU=1

# your favorite downloader (pick one)

#DOWNLOADER="wget -c"
#DOWNLOADER="curl -#OLC -"
#DOWNLOADER="proz -rf"


# your platform specific optimisation flags. This is so that the compiler is optimised. This does not affect the win32 binaries in any way.
# CFLAGS="-O3 -march=pentium"

# your favorite mirror (pick one)

# austria
#MINGW_URL=ftp://gd.tuwien.ac.at/gnu/mingw
#MINGW_URL=http://gd.tuwien.ac.at/gnu/mingw

# Dublin, Ireland	Europe
#MINGW_URL=http://heanet.dl.sourceforge.net/sourceforge/mingw

#Chapel Hill, NC	North America
#MINGW_URL=http://unc.dl.sourceforge.net/sourceforge/mingw

# Minneapolis, MN	North America
#MINGW_URL=http://umn.dl.sourceforge.net/sourceforge/mingw

# Reston, VA	North America
#MINGW_URL=http://aleron.dl.sourceforge.net/sourceforge/mingw


# you probably don't need to change anything from here down

TOPDIR=`pwd`
SRCDIR="$TOPDIR/source"

# These are the files from the MinGW 1.1 release
GCC_VERSION=3.3.1-20030804-1
GCC=gcc-$GCC_VERSION
GCC_CORE=gcc-core-$GCC_VERSION
GCC_CORE_ARCHIVE=$GCC_CORE-src.tar.gz
GCC_GPP=gcc-g++-$GCC_VERSION
GCC_GPP_ARCHIVE=$GCC_GPP-src.tar.gz
GCC_PATCH=""
BINUTILS=binutils-2.13.90-20030111-1-src
BINUTILS_ARCHIVE=$BINUTILS.tar.gz
MINGW=mingw-runtime-3.2
MINGW_ARCHIVE=$MINGW.tar.gz
W32API=w32api-2.4
W32API_ARCHIVE=$W32API.tar.gz

# These are the files from the SDL website

SDL_URL=http://www.libsdl.org/extras/win32/common

# don't use the directx and opengl headers from SDL website, they are outdated and don't work with clanlib
# the opengl headers included in w32api-2.4 is better
# as for directx you need directx 8 for clanlib to work
#OPENGL_ARCHIVE=opengl-devel.tar.gz
#DIRECTX_ARCHIVE=directx-devel.tar.gz

# need install directory first on the path so gcc can find binutils

PATH="$PREFIX/bin:$PATH"

check_envars()
{
	echo "checking envars..."
	echo "If we bail out here, please edit this script to fix problems."
	if test -z $PREFIX; then
		echo "PREFIX not set, please set PREFIX to /usr/local/cross-tools or where-ever you want your cross tools"
		echo "Note that you will also need to set DO_SU=1 if your install path needs root access"
		exit 1
	fi
	if test -z $DOWNLOADER; then
		echo "DOWNLOADER not set, please set DOWNLOADER to your favorite download program like wget, curl..."
		exit 1
	fi
	if test -z $MINGW_URL; then
		echo "MINGW_URL not set, please set MINGW_URL to your favorite mirror"
	fi
	echo "done checking envars"
}

#
# download a file from a given url, only if it is not present
#

download_file()
{
	cd "$SRCDIR"
#	if test ! -f $1 ; then
		echo "Downloading $1"
		$DOWNLOADER "$2/$1"
		if test ! $? || test ! -f $1 ; then
			echo "Could not download $1"
			exit 1
		fi
#	else
#		echo "Found $1 in the srcdir $SRCDIR"
#	fi
  	cd "$TOPDIR"
}

download_files()
{
	mkdir -p "$SRCDIR"
	
	# Make sure wget is installed
	if test "x`which wget`" = "x" ; then
		echo "You need to install wget."
		exit 1
	fi
	download_file "$GCC_CORE_ARCHIVE" "$MINGW_URL"
	download_file "$GCC_GPP_ARCHIVE" "$MINGW_URL"
	download_file "$BINUTILS_ARCHIVE" "$MINGW_URL"
	download_file "$MINGW_ARCHIVE" "$MINGW_URL"
	download_file "$W32API_ARCHIVE" "$MINGW_URL"
#	download_file "$OPENGL_ARCHIVE" "$SDL_URL"
#	download_file "$DIRECTX_ARCHIVE" "$SDL_URL"
}

install_libs()
{
	echo "Installing cross libs and includes"
	cat >install_libs.sh<<INSTALL_LIBS
	mkdir -p "$PREFIX/$TARGET"
	cd "$PREFIX/$TARGET"
	gzip -dc "$SRCDIR/$MINGW_ARCHIVE" | tar xf -
	gzip -dc "$SRCDIR/$W32API_ARCHIVE" | tar xf -
#	gzip -dc "$SRCDIR/$OPENGL_ARCHIVE" | tar xf -
#	gzip -dc "$SRCDIR/$DIRECTX_ARCHIVE" | tar xf -
INSTALL_LIBS
	if test $DO_SU; then
		su -c 'sh install_libs.sh'
	else
		sh install_libs.sh
	fi
	rm -f install_libs.sh
	cd "$TOPDIR"
}

extract_binutils()
{
	cd "$SRCDIR"
	rm -rf "$BINUTILS"
	echo "Extracting binutils"
	gzip -dc "$SRCDIR/$BINUTILS_ARCHIVE" | tar xf -
	cd "$TOPDIR"
}

configure_binutils()
{
	cd "$TOPDIR"
	rm -rf "binutils-$TARGET"
	mkdir "binutils-$TARGET"
	cd "binutils-$TARGET"
	echo "Configuring binutils"
	"$SRCDIR/$BINUTILS/configure" --prefix="$PREFIX" --target=$TARGET &> configure.log
	cd "$TOPDIR"
}

build_binutils()
{
	cd "$TOPDIR/binutils-$TARGET"
	echo "Building binutils"
	make &> make.log
	if test $? -ne 0; then
		echo "make failed - log available: binutils-$TARGET/make.log"
		exit 1
	fi
	cd "$TOPDIR"
}

install_binutils()
{
	cd "$TOPDIR/binutils-$TARGET"
	echo "Installing binutils"
	if test $DO_SU; then
		su -c 'make install &> make-install.log'
	else
		make install &> make-install.log
	fi
	if test $? -ne 0; then
		echo "install failed - log available: binutils-$TARGET/make-install.log"
		exit 1
	fi
	cd "$TOPDIR"
}

extract_gcc()
{
	cd "$SRCDIR"
	rm -rf "$GCC"
	echo "Extracting gcc"
	gzip -dc "$SRCDIR/$GCC_CORE_ARCHIVE" | tar xf -
	gzip -dc "$SRCDIR/$GCC_GPP_ARCHIVE" | tar xf -
	cd "$TOPDIR"
}

patch_gcc()
{
	if [ "$GCC_PATCH" != "" ]; then
		echo "Patching gcc"
		cd "$SRCDIR/$GCC"
		patch -p1 < "$SRCDIR/$GCC_PATCH"
		cd "$TOPDIR"
	fi
}

configure_gcc()
{
	cd "$TOPDIR"
	rm -rf "gcc-$TARGET"
	mkdir "gcc-$TARGET"
	cd "gcc-$TARGET"
	echo "Configuring gcc"
	"$SRCDIR/$GCC/configure" -v \
		--prefix="$PREFIX" --target=$TARGET \
		--with-headers="$PREFIX/$TARGET/include" \
		--with-gnu-as --with-gnu-ld \
		--without-newlib --disable-multilib &> configure.log
	cd "$TOPDIR"
}

build_gcc()
{
	cd "$TOPDIR/gcc-$TARGET"
	echo "Building gcc"
	make LANGUAGES="c c++" &> make.log
	if test $? -ne 0; then
		echo "make failed - log available: gcc-$TARGET/make.log"
		exit 1
	fi
	cd "$TOPDIR"
}

install_gcc()
{
	cd "$TOPDIR/gcc-$TARGET"
	echo "Installing gcc"
	if test $DO_SU; then
		su -c 'make LANGUAGES="c c++" install &> make-install.log'
	else
		make LANGUAGES="c c++" install &> make-install.log
	fi
	if test $? -ne 0; then
		echo "install failed - log available: gcc-$TARGET/make-install.log"
		exit 1
	fi
	cd "$TOPDIR"
}

final_tweaks()
{
	echo "Finalizing installation"
cat >final_tweaks.sh<<FINAL_TWEAKS
	# remove gcc build headers
	rm -rf "$PREFIX/$TARGET/sys-include"

  # Add extra binary links
	if [ ! -f "$PREFIX/$TARGET/bin/objdump" ]; then
		ln "$PREFIX/bin/$TARGET-objdump" "$PREFIX/$TARGET/bin/objdump"
	fi
	if [ ! -f "$PREFIX/$TARGET/bin/dllwrap" ]; then
		ln "$PREFIX/bin/$TARGET-dllwrap" "$PREFIX/$TARGET/bin/dllwrap"
	fi
	if [ ! -f "$PREFIX/$TARGET/bin/windres" ]; then
		ln "$PREFIX/bin/$TARGET-windres" "$PREFIX/$TARGET/bin/windres"
	fi

	# make cc and c++ symlinks to gcc and g++
	if [ ! -f "$PREFIX/$TARGET/bin/g++" ]; then
		ln "$PREFIX/bin/$TARGET-g++" "$PREFIX/$TARGET/bin/g++"
	fi
	if [ ! -f "$PREFIX/$TARGET/bin/cc" ]; then
		ln -s "gcc" "$PREFIX/$TARGET/bin/cc"
	fi
	if [ ! -f "$PREFIX/$TARGET/bin/c++" ]; then
		ln -s "g++" "$PREFIX/$TARGET/bin/c++"
	fi

	# strip all the binaries
	ls "$PREFIX"/bin/* "$PREFIX/$TARGET"/bin/* | egrep -v '.dll$' |
	while read file; do
		strip "$file"
	done
FINAL_TWEAKS
	if test $DO_SU; then
		su -c 'sh final_tweaks.sh'
	else
		sh final_tweaks.sh
	fi
	rm -f final_tweaks.sh
	echo "Installation complete!"
}

check_envars

download_files

install_libs

extract_binutils
configure_binutils
build_binutils
install_binutils

extract_gcc
patch_gcc
configure_gcc
build_gcc
install_gcc

final_tweaks


Copy and paste that into a file called build-cross.sh. You will need root access on the machine you are trying to install this on. Make a temporary directory and drop this file into then. With your favorite editor, edit the top portion of the script, following the existing instructions. You will semi-customize your installation with this. Once you are done, save it, and run the script:


sh build-cross.sh

Assuming you followed the instructions in the file, the program will start to download/compile.

A problem I ran into is that on my Linux distro, Bison and Flex were not installed by default. If you get an error during the binutils compilation, you might trying to check that those two programs are in fact installed, and if not, install them.

Once MinGW is installed, you will need to edit one more file to get your cross compiling going. Find the cross-scons.sh file in your ETSDK/src directory. You will need to edit two lines.


PREFIX=/usr/local/cross-tools
TARGET=i386-mingw32msvc

Assuming you left the install directory as the default one in the script, you can leave the PREFIX line alone. You will need to change the TARGET directory though, to:


TARGET=i586-mingw32msvc

You’re done! Go ahead and give it a try with


sh cross-scons.sh BUILD=release


(jaybird) #2

Just a note, the program will generate the files as .dll’s, but the scons script will still rename your files to .so’s. You will need to manually rename them to their proper win32 names, or write a small script to do it for you.

MinGW also generates much larger .dll’s than MSVC does. An easy way to cut down file size is to strip out the compiler’s debug information (this is not the same as ET’s debug code, existing or stuff you added). You can do this with the (appropriately named) command “strip”. Assuming you have renamed your files to .dll files, type:


strip *.dll

And you should have working Windows binaries.

I hope someone found the post helpful, and that it will help cut down some time in getting your mod running.


(bani) #3

if you pass COPYBINS=1 to scons, it should strip them as part of the build process.
you will need to make a ‘bin’ dir at the same level as the ‘src’ dir in the sdk tree ahead of time, since the scons script will error out if it doesnt exist beforehand.


(=>ETK<=Elite) #4

I have looked all over for information to my question, but still get the same results. So, hopefully someone knows of a new way to do this by now.

Anyhow, to the question (that has been asked about…10 million times, lol). Is there any way to compile the *_mac files on a linux box?

I am pretty new to linux, but figured it would be easier to compile my mod on it since I can get atleast Windows and Linux binaries compiled at the same time without rebooting to another os.

I followed all jaybird’s and bani’s posts, along with the readme included in the sdk, to get the *.so and *.dll’s compiled correctly, thanks for the help with that area. One thing, with Debian systems (debian, ubuntu, etc, although I am not sure about other linux distro’s, I would imagine it is the same or similar with Synaptic or something) you can just use apt-get install mingw32 to install the mingw automatically.

I do see included in the linux sdk a makebundle.sh file for mac files. How can I get this to work properly, or does it? If I just use scons, it will make the *.so files. Now, if I rename them accordingly, the make_bundle.sh does work, but I have no idea if it is working properly, and if these files will actually work on a mac.

Can someone please point me in the right direction here. Thanks in advance.

(Note: Tired of staring at code all day, if I missed any info, just let me know and I will post it)


(kamikazee) #5

Bani uses a Mac to compile those binaries afaik.


(=>ETK<=Elite) #6

I got that from one of his earlier posts. However, what is the point of that makebundle.sh file if I can’t compile the *_mac files?

There has got to be a way for this to be done, otherwise I don’t see why they would include that file to make bundles for the mac.

I do see included in the linux sdk a makebundle.sh file for mac files. How can I get this to work properly, or does it? If I just use scons, it will make the *.so files. Now, if I rename them accordingly, the makebundle.sh does work, but I have no idea if it is working properly, and if these files will actually work on a mac.

Is this way correct at all (to just rename the *.so’s to what the script expects)? Or will it not work? Or is there another way to use the makebundle.sh file? Or is it just a hopeless case?


(kamikazee) #7

The binaries can’t be the same, if you take byte-order into account. Apart from that, Mac uses different includes.

The code and SCons file check if the platform is Darwin or Linux so the right code gets used.
Makebundle.sh is included, but that’s just because all installs include the same files but are packaged differently for easier installation.
For example, the Windows install has got the SCons files and makebundle.sh as well. But that doesn’t mean you can make Linux or Mac binaries using Windows.

Making Windows binaries using Linux is only possible because GCC has been made compatible with Windows trough the MINGW project.


(=>ETK<=Elite) #8

Ok, I was worried about them just including it in the package for an easier distribution between platforms. Thanks for the help.


(jaybird) #9

The files are included just so you would have them. You need a Mac to compile Mac bins. I have heard of people cross compiling on Mac, though it is extremely difficult to pull off and also somewhat illegal (afaik). You’re better off getting a Mac, or at least access to one.


(=>ETK<=Elite) #10

Ok, I will look around for someone with an old to compile it when it’s finshed…or atleast 1 release is finished, so I got awhile before this needs to be done anyhow.

I can run it and test it both serverside and clientside as it is without the mac binaries, so it’s not really a delay or holding back progess or anything.

Thanks for your time guys.


(bani) #11

you need a mac to compile the mac binaries. you can thank apple for that.
i bought a mac mini in order to make mac builds. makebundle.sh is only intended to run on a mac.


(Hobo) #12

i’m cross-compiling mac binaries just fine using http://www.opendarwin.org/projects/odcctools/
but yeah, you do need includes etc. from an osx install.