Crosscompiling Crystal Space on GNU/Linux
This is a HOWTO guide which explains how to cross-compile CrystalSpace for Windows under GNU/Linux. It is useful for Linux developers who wish to maintain a single development environment, and deploy binaries for both platforms.
The MinGW32 Toolchain
First, you need to build the MinGW32 toolchain for linux. This page describes how to get started:
An aside, roughly halfway down that page points to this really useful script which does virtually everything for you:
Note: This automated script does not work in Fedora Core 3. You must use the other script mentioned on the page, download the packages by hand, and modify the settings at the top of that script.
If you're on some platform other than Fedora, grab this script, put it in a local file, e.g. buildmingwcross.sh. Give it execute permission:
$ chmod +x buildmingwcross.sh
Modify the variables under the BEGIN USER SETTINGS heading if you want, but the defaults should be OK.
As of this writing, there are two bugs in this script which must be fixed before this will work. First, line #64 is supposed to be a comment, but it is missing the "#" symbol at the start of the line. Put it there. Second, on line #129, the variable $BUILD_DIR should read $BUILDDIR... there is no such $BUILD_DIR variable named anywhere else in the file, and if you use it as-is your build directory will be /source.
Remember that the script must have write permission to the destination directory, so if you put it in a location like /usr/local/mingw32, you will need to run the script with root privileges. This script will take quite a while to download and compile everything. Go take a coffee break.
When it's done and you have a mingw32 installation (mine's in the default location /usr/local/mingw32), add /usr/local/mingw32/bin to your system PATH. In Mandrake and Fedora, create the file /etc/profile.d/mingw32.sh:
# Add MinGW32 to the PATH PATH=$PATH:/usr/local/mingw32/bin export PATH
Make sure it has executable permission:
$ chmod +x /etc/profile.d/mingw32.sh
Quit and restart your terminal. The new path should show up the next time a shell is started. You can verify this by doing:
set | grep PATH
Check that the mingw32 portion of the path appears.
In all of the example paths in the remainder of this HOWTO, the path /usr/local/mingw32 is used. Replace this as necessary with your own install location path.
CrystalSpace requires several external libraries which are not a part of the default MinGW32 installation. At this point, you have two options.
Option #1 is to cross-compile all the prerequesite libraries by hand, installing them with, for example, a --prefix setting of /usr/local/mingw32/mingw32.
Option #2 is to obtain the standard Windows library package and install it under MinGW32 (and possibly Wine). This option is a binary package which contains the required ZLib, and many optional but useful libraries such as JPEG and Ogg/Vorbis support, and is a nice option to get you started quickly.
Option 1 is partially described below; only the steps involved for compiling and installing ZLib (the only library that is strictly required) are presented here.
Option 2 is described in detail below option 1.
Library Option 1 - ZLib under MinGW32
CrystalSpace requires ZLib, which is not a part of the default MinGW32 installation. If you chose the option to compile libraries by hand, download the latest source package from:
ZLib uses a nonstandard ./configure script, so some trickery will be necessary to get it working:
$ export CC=mingw32-gcc $ ./configure --prefix=/usr/local/mingw32/mingw32 $ make
`make` will fail because the naive configure script uses the local ranlib, and does not provide a way to set one short of modifying the configuration system itself. Do this:
$ mingw32-ranlib libz.a $ make $ make install $ mingw32-ranlib /usr/local/mingw32/mingw32/lib/libz.a $ unset CC
The second make will succeed and you can proceed to `make install`. The second `mingw32-ranlib` command may or may not be necessary, but I was having trouble until I did this. The final `unset CC` is important - CC needs to not be set for the remaining steps in this tutorial.
Although this document doesn't describe how to compile all necessary libraries, most should compile with a minimum of problems. The most serious problem you are likely to encounter is build systems which make assumptions about ranlib. Running mingw32-ranlib on generated libraries should fix this.
Compiling these libraries as shared objects instead of static libraries is certainly possible, but beyond the scope of this document.
Library Option 2 - The Standard Windows Libraries
The standard win32 library binary package is available at:
Unfortunately, this installer doesn't do anything we really need except self-extract the archive. This can be done under Wine or a normal Windows desktop. For example:
$ wine cs-win32libs-1.0_001.exe
After the install has completed, you have a new directory containing libs, dlls, include files, and some other stuff we don't really need. Change directories to the install location and copy the necessary items into the mingw32 root. For example:
$ cp -rf include/* /usr/local/mingw32/mingw32/include/ $ cp -rf lib/*.lib lib/*.a /usr/local/mingw32/mingw32/lib/
The installer probably installed the .dll files correctly in your system, but if Wine cannot find them, you can also manually copy the contents of the dll/ directory to your Wine system directory. For example:
$ cp -f dll/*.dll ~/.wine/drive_c/windows/system/
Note This process works with CrystalSpace 1.0, but YMMV with other versions.
In order for this to work, the CRYSTAL environment variable should point to a CS source directory in which you want to build. This should be separate from any existing Linux builds of CS. Note that when switching between building native and mingw32 applications, it is necessary to switch the CRYSTAL environment variable to point to the correct location.
The main problem with building CrystalSpace for another target platform is `jam`. The ./configure script happily builds jam0.exe for you but it does not seem to work under wine emulation.
The solution is to configure CS twice, once configured for the local machine, once for mingw32.
$ mkdir build-jam $ cd build-jam $ $CRYSTAL/configure $ cp jam $CRYSTAL $ cd .. $ rm -rf build-jam $ cd $CRYSTAL $ ./configure --target=mingw32 --host=mingw32 --build=i686-pc-linux-gnu \ CXXFLAGS=-I/usr/local/mingw32/mingw32/include \ "LDFLAGS=-L/usr/local/mingw32/mingw32/lib -s -O" \ CXX=/usr/local/mingw32/bin/mingw32-g++ --with-z=/usr/local/mingw32/mingw32 $ ./jam
Note: Fedora core 3 requires the value "i586-mingw32" in the --target and --host parameters. If your distribution requires something different here, please let me know.
Note: If jam reports missing symbols like _mspace_malloc, put '#define MSPACES 1' in the #ifdef-lines in libs/csutil/dlmalloc.c.
Note: If jam reports errors because of a missing 'windres' executable, make a new symbolic link named windres which links to i586-mingw32msvc-windres.
Notice the -s and -O options in the LDFLAGS section. This produces optimized DLLs with the symbols stripped. Without this, all the DLLs and EXEs that mingw32-g++ creates will be excessively large. However, if you get errors because i586-mingw32msvc-g++ cannot handle -fvisibility flags, leave out the -s switch.
This will take a long time, and a good chunk of it may fail to link because of a similar `ranlib` issue that libz has: it is trying to use the local ranlib instead of mingw32-ranlib. (NOTE: current CVS versions of CS seem to fix this; you can also try to 'export RANLIB=i586-mingw32msvc-ranlib' beforehand)
$ mingw32-ranlib `find . -name *.a`
This will fixup your libraries. Do this again:
Finally, you should have a complete Windows CS build in the $CRYSTAL directory. If you have `wine` installed and configured, you can test it:
$ wine walktest.exe -- -relight flarge
Projects which live under the CS source tree and use Jam for configuration and building do not need to do anything special. They will be built alongside walktest.exe and the other executables in the package.
Projects which embed CS and do not live in the source tree may also be cross-compiled. If you are not doing so already, you should include these in your build system (in configure.in or Makefile.am):
CXXFLAGS="$CXXFLAGS `$CRYSTAL/cs-config --cxxflags`" LDFLAGS="$LDFLAGS `$CRYSTAL/cs-config --ldflags`"
You will want to keep a CS binary installation for each build target. In my case, this means I have two CS system directories:
The second directory is a result of following the above steps and copying the resulting built CS directory to /usr/local/CS-i586-mingw32. The other directory is a plain-vanilla `./configure; jam` build of CS.
If you're using autoconf, put this in configure.in right after the AC_INIT call:
This macro knows how to switch compilers when you pass the mingw32 --target and --host values to ./configure. If your build system does't use autoconf, you'll have to figure out how to switch gcc/ld/ranlib/etc. by hand.
If you do include the AC_CANONICAL_SYSTEM macro in autoconf, you can cross-build your app like so (obviously modifying the CRYSTAL path for your setup):
$ export CRYSTAL=/usr/local/CS-i586-mingw32 $ ./configure --target=mingw32 --host=mingw32 --build=i686-pc-linux-gnu "LDFLAGS=-s -O"
Thanks to Res for first pointing out that the required libraries are available via the standard Windows library package.