User:Iceiceice/Cross-compiling

From The Battle for Wesnoth Wiki
Revision as of 16:06, 24 October 2014 by Iceiceice (talk | contribs) (Get mingw)

Here are some instructions I created the first time I managed to cross compile wesnoth. It's as much a "post-mortem" as a guide, I leave it here in case I ever want to do it again later, or if it might help someone.

These instructions refer to using mingw to cross-compile wesnoth 1.13.0-dev on Linux Mint Qiana 17 , targeted at 32-bit windows.

Last tested at commit: 1385ab3791088d7c9bfa3bc439fcf49b23563535

Get mingw32

It might seem as simple as "sudo apt-get install mingw32", but after you do this, you need to make sure that you have a 32 bit compiler which is good enough to compile wesnoth. This required some trial and error, the version I got automatically from the distribution was only gcc 4.2, as you can see:

  $ i586-mingw32msvc-g++ -v
 Using built-in specs.
 Target: i586-mingw32msvc
 Configured with: /build/buildd/mingw32-4.2.1.dfsg/build_dir/src/gcc-4.2.1-2-dfsg/configure -v --prefix=/usr --target=i586-mingw32msvc --enable-languages=c,c++ --enable-threads --enable-sjlj-exceptions --disable-multilib --enable-version-specific-runtime-libs
 Thread model: win32
 gcc version 4.2.1-sjlj (mingw32-2)


Furthermore it was broken, and had some bugs about ::swprintf not defined, which caused boost iostreams to break... google this for some stories of pain. [1] [2] [3]


Therefore to get a more recent version of mingw I added a package from utopic universe :

 $ sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ utopic main universe"

And I install

 $ sudo apt-get install g++-mingw-w64-i686

(Launchpad page: http://packages.ubuntu.com/utopic/g++-mingw-w64-x86-64)

Now I get this instead

 $ i686-w64-mingw32-g++ -v
 Using built-in specs.
 COLLECT_GCC=i686-w64-mingw32-g++
 COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-w64-mingw32/4.9-win32/lto-wrapper
 Target: i686-w64-mingw32
 Configured with: ../../src/configure --build=x86_64-linux-gnu --prefix=/usr --includedir='/usr/include' --mandir='/usr/share/man' --infodir='/usr/share/info' --sysconfdir=/etc --localstatedir=/var --libexecdir='/usr/lib/gcc-mingw-w64' --disable-maintainer-mode --disable-dependency-tracking --prefix=/usr --enable-shared --enable-static --disable-multilib --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --libdir=/usr/lib --enable-libstdcxx-time=yes --with-tune=generic --enable-version-specific-runtime-libs --enable-fully-dynamic-string --enable-libgomp --enable-languages=c,c++,fortran,objc,obj-c++ --enable-lto --with-plugin-ld --enable-threads=win32 --program-suffix=-win32 --program-prefix=i686-w64-mingw32- --target=i686-w64-mingw32 --with-as=/usr/bin/i686-w64-mingw32-as --with-ld=/usr/bin/i686-w64-mingw32-ld
 Thread model: win32
 gcc version 4.9.1 (GCC)

With gcc 4.9, now we are in business.

Download loonycyborg's wesnoth deps collection

http://sourceforge.net/projects/wesnoth/files/SDK/wesnoth-deps-1.11.zip/download

I'm going to assume henceforth that you extract this to the folder "~/w-deps", and that inside is "include", "bin" etc. directories.

Download boost

I wanted to test our stated boost dependencies, so I actually make a folder "~/old-boost/" where I hold several versions of boost. You can find any old version of boost at source forge, e.g.

[4] [5] [6]

etc.

Download one of these and extract it to ~/old-boost/, or take from w-deps folder.

I'm going to assume that you picked boost 1.52.0, so your boost directory is ~/old_boost/boost_1_52_0/.

Make a boost config file (tell it what compiler you want to use)

In your home directory, make a text file called user_config.jam.

Here's some sample contents:

 $ cat user-config.jam 
 using gcc ;
 using gcc : 4.6 : g++-4.6 ;
 using gcc : w64mingw32 : i586-mingw32msvc-g++ ;
 using gcc : w64mingw64 : x86_64-w64-mingw32-g++ ;


When boost has a file like this, it means that e.g. "toolset=gcc-4.6" will cause it to use the compiler g++-4.6, and "toolset=gcc-w64mingw32" will cause it to use the compiler i586-mingw32msvc-g++.

Make a line like this:

 using gcc : w64mingw32 : i686-w64-mingw32-g++ ;

To register our new compiler.

(And make sure its the only w64mingw32 line of course.)

Compile boost

Navigate to your boost directory, ~/old_boost/boost_1_52_0/

First, run the "bootstrap" program,

 ./bootstrap.sh

You don't need to give it any arguments, it doesn't really matter what it builds with. It is just building the boost jam program which will actually orchestrate the boost build.

Now, run boost jam (called "b2"). I recommend to put the actual command in shell script, for ease of use in case you have to modify it (and so you don't forget it...)

I used the following recipe:

 $ cat recipe
 #!/bin/bash
./b2 toolset=gcc-w64mingw32 -d+2 -a --reconfigure --debug-configuration --with-iostreams --with-locale --with-regex --with-filesystem --with-system --with-program_options target-os=windows variant=release threading=multi threadapi=win32 address-model=32 architecture=x86 instruction-set=i686
  link=static runtime-link=static -j 2 -sBZIP2_BINARY=libbz2 -sBZIP2_INCLUDE=/home/chris/w-deps/include -sBZIP2_LIBPATH=/home/chris/w-deps/bin -sZLIB_BINARY=zlib -sZLIB_INCLUDE=/home/chris/w-deps/include -sZLIB_LIBPATH=/home/chris/w-deps/bin


Discussion of flags

toolset=gcc-w64mingw32
Tells to use the compiler I choose for 32 bit windows. Could also have tried e.g. gcc-w64mingw64, for 64-bit windows, based on my user-config.jam, or gcc-4.6 for g++-4.6 compiler (see my user-config.jam above)
-d+2
--debug-configuration
These are debugging output options that make boost tell you about the configuration, and show you what compiler commands it is running.
-a
--reconfigure
-a makes sure that all targets are rebuilt, and reconfigure makes sure that it reconfigures based on new options. If you are changing things around these options prevent you from getting screwed over by stale options.
--with-iostreams
--with-locale
--with-regex
--with-filesystem
--with-system
--with-program_options
Select what libraries you want. It's better to use --with than --without because otherwise you get a bunch of random crap. You can't use both kinds.
target-os=windows
threading=multi
threadapi=win32
You need these to make it build dll's, and that will work for you.
address-model=32
architecture=x86
instruction-set=i686
I added these out of paranoia at some point but they might not really be necessary
link=static
runtime-link=static
This is the only reasonable option here
-j 2
Parallelize the build over 2 cores
-sBZIP2_BINARY=libbz2
-sBZIP2_INCLUDE=/home/chris/w-deps/include
-sBZIP2_LIBPATH=/home/chris/w-deps/bin
Tell boost exactly where to find bzip2 binary and header. I earlier tried to do this just with cxxflags and linker flags, but it got confused with my system-installed bzip2 and caused me a lot of pain. This fixed it.
-sZLIB_BINARY=zlib
-sZLIB_INCLUDE=/home/chris/w-deps/include
-sZLIB_LIBPATH=/home/chris/w-deps/bin
Tell boost exactly where to find zlib binary and header.

Now run the recipe (don't forget to "chmod +x" it):

 $ ./recipe

If you would like to suppress the warnings, you can use cxxflags="-w" as an additional argument.

You should get this at the end:

 The Boost C++ Libraries were successfully built!
 
 The following directory should be added to compiler include paths:
 
     /home/chris/old_boost/boost_1_52_0
 
 The following directory should be added to linker library paths:
 
     /home/chris/old_boost/boost_1_52_0/stage/lib


If you did not, check that all of your paths are matching correctly, that you actually have a libbz2.a and zlib.a in your ~/w-deps/bin, (and that they *match* the associated headers... if in doubt, try downloading again, or even compile the c libraries yourself, it doesn't matter what compiler you use for c libraries). Check that the compiler name is correct. You could use cxxflags to try to get additional diagnostics. The -d+2 command causes boost to output much additional diagnostics as well.

Compile wesnoth

Now, you need to craft an scons command that will properly tell it to cross compile, to use the cross compiling compiler, and point it to your fresh boost compile, and all the deps. I prefer to in this case as well store the recipe in a shell script:

 $ cat build_mingw_boost_1_52.sh 
 #!/bin/bash
 rm .scons-option-cache
 scons default_targets=wesnoth build=release extra_flags_config="-DBOOST_THREAD_USE_LIB -Wno-unused-local-typedefs" prefix=~/w-deps boostdir=~/old_boost/boost_1_52_0 boostlibdir=~/old_boost/boost_1_52_0/stage/lib gettextdir=~/w-deps gtkdir=~/w-deps  host=i686-w64-mingw32

Some discussion

rm .scons-option-cache
So that stale settings can't screw up my build and confuse me
default_targets=wesnoth
This could also be wesnoth, wesnothd, campaignd. I think it can't include "test" atm because we didn't compile the boost unit testing system.
build=release
Unless you want to make a debug build
extra_flags_config
Add -Wno-unused-local-typedefs or your log will be unreadable with pointless warnings.
Add -DBOOST_THREAD_USE_LIB because loonycyborg said so
prefix=~/w-deps
This is pretty handy, this will cause scons to add ~/w-deps/include to be included as a cxx flag, and also to add ~/w-deps/bin to be linked. So it saves you some typing.
boostdir=~/old_boost/boost_1_52_0
First directory that boost reported to you above (or for whatever boost version you want to use)
boostlibdir=~/old_boost/boost_1_52_0/stage/lib
Second directory that boost reported to you above (Wherever the boost libraries ended up for you.)
gettextdir=~/w-deps
gtkdir=~/w-deps
These point scons to find compiled versions of these, and not your system version (which will not be binary compatible in case of C++ libs).
Note that I don't add sdldir=~/w-deps, because it shouldn't be necessary as SDL is a c lib, so my system version is fine. But if you get a problem finding or linking SDL, then add that in also.
host=i686-w64-mingw32
This is where we select the compiler, actually. scons will prepend the "host" followed by -, followed by g++, if no cxxtool argument is given. (I'm not sure how "host" and "cxxtool" arguments interact.) So this selection results in the compiler, i686-w64-mingw32-g++, as desired.

That's all there is to it. Good luck.

WINE

To get wine to run I did the following. You must make sure you are using a 32 bit environment.

 env WINEPREFIX=~/prefix32 WINEARCH=win32 wine cmd

All the online instructions say that you need to update wine's path, using regedit, to point to your dll's. I couldn't actually get this to work, so I ended up putting symlinks in my prefix 32 C:\windows\system32 directory:

 ~/prefix32/drive_c/windows/system32 $ ln -s ~/w-deps/bin
 ~/prefix32/drive_c/windows/system32 $ ln -s /usr/lib/gcc/i686-w64-mingw32/4.9-win32/

If I got errors like this:

 $ env WINEPREFIX=~/prefix32 WINEARCH=win32 wine wesnoth
 err:module:import_dll Library libpangoft2-1.0-0.dll (which is needed by L"C:\\windows\\system32\\libpangocairo-1.0-0.dll") not found
 err:module:import_dll Library libpangocairo-1.0-0.dll (which is needed by L"Z:\\home\\chris\\wesnoth-src\\clone\\wesnoth\\wesnoth.exe") not found
 err:module:LdrInitializeThunk Main exe initialization for L"Z:\\home\\chris\\wesnoth-src\\clone\\wesnoth\\wesnoth.exe" failed, status c0000135

I fixed it by adding a symlink to ~/w-deps/bin/libpangoft2-1.0-0.dll in the folder prefix32/drive_c/windows/system32.

And so on and so forth... Eventually it worked.