Freedom at last, Linux + MingW cross compiler’s endless possibilies

Having worked on an RTS (real time strategy) open source game at sourceforge.net, I came across many challenges but also learned many things related to the world of open source. This article will attempt to discuss some of those things in the hope that the information helps other developers in the open source realm. I will attempt to share this in 2 parts, part 1 will be the ultimate way to freedom, while part 2 will discuss things encountered along the way and will act almost as a kind of story.

Part 1: Cross compiling your C/C++ open source project in Linux, for Linux and Windows.

The basic recipe that I used to accomplish this involves the following:

– CodeBlocks IDE (you could likely use eclipse too)
– MingW cross compiler
– All the source code for your project (and its dependencies)
– time (the precious commodity that everyone wants from you)

For the first 2 items (CodeBlocks and MingW) got the following links:

CodeBlocks
MingW

Now, a few things to keep in mind as you travel through the compilation process:

– you MUST recompile all dll’s and libs within MingW (VC++ and other compilers are NOT compatible with MingW)

Take for example compiling OpenAL (Open Audio Library) from Creative that many open source projects use. Compiling this library includes a few challenges, including the need to fix dsound.h from the DirectX SDK to compile with MingW.

edit dsound.h and comment out line 1899:

//typedef struct IDirectSoundFullDuplex *LPDIRECTSOUNDFULLDUPLEX;

next edit the file included in the OpenAL SDK named XCompile.txt so its looks something like the following:

# Cross-compiling requires CMake 2.6 or newer. To cross-compile, first modify
# this file to set the proper settings and paths. Then use it from build/ like:
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile.txt \
#          -DCMAKE_INSTALL_PREFIX=/usr/mingw32/mingw
# If you already have a toolchain file setup, you may use that instead of this
# file.

# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
SET(CMAKE_C_COMPILER i586-mingw32msvc-gcc)
SET(CMAKE_CXX_COMPILER i586-mingw32msvc-g++)

# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc)

# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

Next edit CMakeLists.txt to tell it where to find your DirectX SDK files adding something like the following BEFORE the section:

# Check DSound/MMSystem backend
IF(DSOUND)

Add the following:

SET (DXSDK_DIR "/home/softcoder/Code/megaglest/trunk/source/win32_deps/Microsoft DirectX SDK (November 2007)")
SET(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} "${DXSDK_DIR}/Include")
INCLUDE_DIRECTORIES("${DXSDK_DIR}/Include")
LINK_DIRECTORIES("${DXSDK_DIR}/Lib")

Next run cmake to produce the makefiles for OpenAL which should now properly find direct sound from DirectX:

cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile.txt -DCMAKE_INSTALL_PREFIX=/usr/i586-mingw32msvc

make

after running this you should get OpenAL lib’s / DLL’s produced for MingW.

As a general rule you simply need to point the compiler etc.. the MingW’s and any dependencies need to find your MingW compiled libs.

For example Compiling lobogg and libvorbis:

./configure --host=i586-mingw32msvc --build=i686-pc-linux-gnu --enable-shared --disable-static --libdir=./lib

make

LDFLAGS="-L/home/softcoder/Code/megaglest/trunk/source/win32_deps/lib" ./configure --host=i586-mingw32msvc --enable-shared --disable-static --with-ogg-libraries=../lib --with-ogg-includes=../libogg-1.2.1/include

make

What should you do if you get lots of:

undefined reference to x

linker errors? This typically means you need to order the libs in your project in proper order! Here’s an example from my codeblocks project file (the order does matter):

<Linker>
          <Add option="-Wl,-subsystem,console" />
          <Add option="-mconsole" />
          <Add library="mingw32" />
          <Add library="ddraw" />
          <Add library="dsound" />
          <Add library="dxguid" />
          <Add library="ws2_32" />
          <Add library="iphlpapi" />
          <Add library="wsock32" />
          <Add library="liblibogg" />
          <Add library="liblibvorbis" />
          <Add library="liblibvorbisfile" />
          <Add library="zlib" />
          <Add library="jpeg" />
          <Add library="liblibpng" />
          <Add library="xerces-c" />
          <Add library="OpenAL32" />
          <Add library="liblibcurl" />
          <Add library="winmm" />
          <Add library="gdi32" />
          <Add library="opengl32" />
          <Add library="glu32" />
          <Add library="SDL" />
          <Add library="SDLmain" />
          <Add library="lua5.1" />
          <Add library="streflop" />
          <Add library="glest" />
          <Add directory="../../source/win32_deps/lib" />
          <Add directory="../../source/win32_deps/xerces-c-src_2_8_0/lib" />
</Linker>

Also what should you do if you program always produces stdout.txt and stderror.txt even though the application is a console application? It appears that this can happen due ti SDL taking convtrol of these streams. You can recompile SDL with:

./configure --disable-stdio-redirect

For more details check out: here

Another item that caused trouble was wxWidgets. I kept getting the following error after running configure:

./configure --target=i586-mingw32msvc --host=i586-mingw32msvc

/home/softcoder/Code/megaglest/trunk/source/win32_deps/wxWidgets-2.8.10/bk-deps i586-mingw32msvc-gcc -c -o wxexpat_xmlparse.o -I./src/expat   -I/home/softcoder/Code/megaglest/trunk/source/win32_deps/wxWidgets-2.8.10/lib/wx/include/i586-mingw32msvc-msw-ansi-release-2.8 -I./include -mthreads -Wall -Wundef -O2 -fno-strict-aliasing -mthreads ./src/expat/lib/xmlparse.c
./src/expat/lib/xmlparse.c:97:2: error: #error memmove does not exist on this platform, nor is a substitute available
./src/expat/lib/xmlparse.c: In function ‘XML_SetEncoding’:
./src/expat/lib/xmlparse.c:888: error: ‘isParamEntity’ undeclared (first use in this function)
./src/expat/lib/xmlparse.c:888: error: (Each undeclared identifier is reported only once
./src/expat/lib/xmlparse.c:888: error: for each function it appears in.)
./src/expat/lib/xmlparse.c:888: error: ‘externalParEntInitProcessor’ undeclared (first use in this function)
./src/expat/lib/xmlparse.c: In function ‘XML_SetReturnNSTriplet’:
./src/expat/lib/xmlparse.c:1110: error: ‘isParamEntity’ undeclared (first use in this function)
./src/expat/lib/xmlparse.c:1110: error: ‘externalParEntInitProcessor’ undeclared (first use in this function)
./src/expat/lib/xmlparse.c: In function ‘XML_SetParamEntityParsing’:
./src/expat/lib/xmlparse.c:1362: error: ‘isParamEntity’ undeclared (first use in this function)
./src/expat/lib/xmlparse.c:1362: error: ‘externalParEntInitProcessor’ undeclared (first use in this function)
make: *** [wxexpat_xmlparse.o] Error 1

To fix this issue I had to edit the configure script and add the following:

ORIGINAL:

OPTIMISE_CFLAGS=
if test "$wxUSE_OPTIMISE" = "no" ; then
    if test "$GCC" = yes ; then
                        OPTIMISE_CFLAGS="-O0"
    fi
else
    if test "$GCC" = yes ; then
        case "${host}" in
            *-pc-os2_emx | *-pc-os2-emx )
                                                OPTIMISE_CFLAGS="-O2"
            ;;
            *)
                                                                OPTIMISE_CFLAGS="-O2 -fno-strict-aliasing"
            ;;
        esac
    else
        OPTIMISE_CFLAGS="-O"
    fi
fi

FIXED:

OPTIMISE_CFLAGS=
if test "$wxUSE_OPTIMISE" = "no" ; then
    if test "$GCC" = yes ; then
                        OPTIMISE_CFLAGS="-O0"
    fi
else
    if test "$GCC" = yes ; then
        case "${host}" in
            *-pc-os2_emx | *-pc-os2-emx )
                                                OPTIMISE_CFLAGS="-O2"
            ;;
            *-*-cygwin* | *-*-mingw32* )
                OPTIMISE_CFLAGS="-O2 -fno-strict-aliasing -DHAVE_MEMMOVE=1 -DXML_DTD"

                echo "=====================> MingW Fix Setup"
            ;;

            *)
                                                                OPTIMISE_CFLAGS="-O2 -fno-strict-aliasing"
            ;;
        esac
    else
        OPTIMISE_CFLAGS="-O"
    fi
fi

I re-ran configure and then make compiled everything properly.

For my project I successfully compiled:

xerces-c
lua5.1
sdl
opengl
curl
openal
libpng
jpeg
zlib
libogg
libvorbis


Part 2: Why is it a good idea to cross compile?

If your software supports multiple platforms (like many “good” open source projects do), then you already know the answer. It can be a very tedious task to try to maintain code for multiple platforms especially if you cannot easily, quickly compile your code changes for the target platform in one development environment (and operating system). Some people use Virtual Machine Software (Like the highly recommended Virtual Box) but even this approach often leaves you feeling dirty, having to load up some version of Windows in your VM (assuming of course that you have a legit license, etc). All to offer a binary for those poor “entangled” windows users!

No more! Now we simply target the same code to the Windows platform and it builds the binaries using the MingW cross compiler! Now developers can focus on cross platform code with a purpose, and not worry so much about how to compile for those other platforms. This should open the doors for a broader audience for numerous open source projects (especially where build resources are limited). Add to this the installation of WINE (here) and you can also test the windows binary from your Linux environment. Lovely! This means you may support more users without compromising your coding morals.

Thanks, and I hope this helps others.


Comments are closed.