Firstly, the cross-compiler I have is i686-w64-mingw32 (GCC 4.7.2). This is mingw-w64 as installed in Gentoo through portage's crossdev tool, and is for 32-bit Windows targets. I also have x86_64-w64-mingw32, which is mingw-w64 for 64-bit Windows targets, but my focus was on 32-bit.
Secondly, I had already installed the ffmpeg Windows builds before attempting this, as well as the OpenAL dev stuff from OpenAL Soft. For ffmpeg, I have:
ffmpeg-20121029-git-11d695d-win32-dev.7z (for the headers and import libs)
ffmpeg-20121029-git-11d695d-win32-shared.7z (for the actual DLLs)
The headers were copied to
/usr/i686-w64-mingw32/usr/include
(my cross-compiler's include directory), and the import libs (the lib*.dll.a files) were copied to
/usr/i686-w64-mingw32/usr/lib
I also copied the pkg-config files (the lib/pkgconfig/*.pc files) to
/usr/i686-w64-mingw32/usr/lib/pkgconfig
For cross-compiling with cmake, I have this toolchain file:
$HOME/cmake-toolchains/xcompile.txt
Code: Select all
# Cross-compiling requires CMake 2.6 or newer. Example:
# cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/xcompile.txt -DHOST=i686-w64-mingw32
# Where 'i686-w64-mingw32' is the host prefix for your cross-compiler. 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 "${HOST}-gcc")
SET(CMAKE_CXX_COMPILER "${HOST}-g++")
SET(CMAKE_RC_COMPILER "${HOST}-windres")
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH "/usr/${HOST}")
# here is where stuff gets installed to
SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}/usr" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
# 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)
# set env vars so that pkg-config will look in the appropriate directory for
# .pc files (as there seems to be no way to force using ${HOST}-pkg-config)
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "")
Now, to start with I initially followed the MSVC instructions from our wiki, but it needed some significant alterations.
- Step 1: Install the Ogre SDK.
Code: Select all
$ cp lib/release/lib*.dll.a lib/debug/lib*.dll.a /usr/i686-w64-mingw32/usr/lib/
$ cp -R include/* /usr/i686-w64-mingw32/usr/include/
This is about as easy and straight-forward a step is in this guide. You've been warned.
- Step 2: Install Boost. (alt title: Why Boost Needs to Die)
https://svn.boost.org/trac/boost/ticket/3393 (cross-compiling skips boost_thread)
https://svn.boost.org/trac/boost/ticket/4614 (unable to statically link to boost_thread on mingw)
They can be worked around, but be aware of them.
Grab the 1.51 source and extract it (don't start building yet). I grabbed 1.51 because 1.52 and 1.53 have their own issues with us, but if you want to try them, I don't imagine the instructions here would differ.
Go to the boost_1_51_0 directory where it extracted. Since boost is attempting to be the devil incarnate, you'll notice it doesn't use cmake, or even autotools, so there's no standard way to cross-compile. I've cobbled these instructions together from the various sources I could find. Edit tools/build/v2/user-config.jam using your favorite text editor. Under the commented lines for the GCC configuration, add:
Code: Select all
using gcc : 4.7 : i686-w64-mingw32-g++ ;
Run the bootstrap:
Code: Select all
$ ./bootstrap.sh --prefix=/usr/i686-w64-mingw32/usr
Code: Select all
$ ./b2 --prefix=/usr/i686-w64-mingw32/usr --build-dir=build --layout=versioned target-os=windows threadapi=win32
stage/lib/libboost_*-mt-1_51.a and stage/lib/libboost_*-mt-d-1_51.a
for at least 'system', 'filesystem', 'program_options', 'thread', 'date_time', and 'wave'. Note however how the thread ones are named:
libboost_thread_win32-*-mt-1_51.a
libboost_thread_win32-*-mt-d-1_51.a
You need to rename these to remove the '_win32' part or FindBoost.cmake will fail to find them. After you've renamed them, copy the libs and headers to the cross-compiler's directory (since, as with Ogre, setting BOOST_ROOT won't help)...
Code: Select all
cp stage/lib/libboost_* /usr/i686-w64-mingw32/usr/lib/
cp -R boost /usr/i686-w64-mingw32/usr/include/
- Step 3: Install Bullet
There may also be an issue building the demos because of Glut. Since we don't need the demos, we can just disable them.
Code: Select all
$ cmake . -DCMAKE_TOOLCHAIN_FILE=$HOME/cmake-toolchains/xcompile.txt -DHOST=i686-w64-mingw32 -DCMAKE_BUILD_TYPE=Release -DBUILD_CPU_DEMOS=OFF -DBUILD_DEMOS=OFF
$ make
Code: Select all
for i in lib/lib*_RelWithDebugInfo.a ; do mv "$i" "${i/_RelWithDebugInfo/}" ; done
Code: Select all
$ cp lib/lib*.a /usr/i686-w64-mingw32/usr/lib/
- Step 4: Install Freetype2
Code: Select all
$ cp ~/.wine/drive_c/Program\ Files/GnuWin32/lib/libfreetype.dll.a /usr/i686-w64-mingw32/usr/lib/
$ cp ~/.wine/drive_c/Program\ Files/GnuWin32/lib/pkgconfig/* /usr/i686-w64-mingw32/usr/lib/pkgconfig/
$ cp -R ~/.wine/drive_c/Program\ Files/GnuWin32/include/* /usr/i686-w64-mingw32/usr/include/
- Step 5: Install MyGUI
This one has a few minor issues, but they're easy to work out. The first problem is that, when building for Windows, MyGUI won't assume Ogre is in the default compiler paths, so we have to tell it. We can do this by setting the OGRE_HOME env var. It can also have issues linking its demos and tools because Ogre is using Boost, but MyGUI doesn't link Boost when it has to look for Ogre itself.
Code: Select all
$ OGRE_HOME=/usr/i686-w64-mingw32/usr cmake . -DCMAKE_TOOLCHAIN_FILE=$HOME/cmake-toolchains/xcompile.txt -DHOST=i686-w64-mingw32 -DMYGUI_BUILD_DEMOS=OFF -DMYGUI_BUILD_TOOLS=OFF -DMYGUI_BUILD_PLUGINS=OFF
$ make
Code: Select all
$ cp lib/libMyGUI*.a /usr/i686-w64-mingw32/usr/lib/
$ cp MyGUIEngine/include/* /usr/i686-w64-mingw32/usr/include/
$ cp Platforms/Ogre/OgrePlatform/include/* /usr/i686-w64-mingw32/usr/include/
- Step 6: Install Qt4
And that's it for the dependencies! But we're not yet done...
- Step 7a: CMake OpenMW
First, make sure you have the OpenMW sources downloaded. I strongly recommend creating a 'winbuild' subdir in the openmw root, to keep the build files away from the sources in case something goes wrong. If you ever want to stop, or want to restart from here, just clear everything in the winbuild directory.
Now, from the winbuild directory, copy FindBoost.cmake to the local cmake module path:
Code: Select all
$ cp /usr/share/cmake/Modules/FindBoost.cmake ../cmake/
Code: Select all
set(Boost_LIB_PREFIX "")
if ( WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN)
set(Boost_LIB_PREFIX "lib")
endif()
Code: Select all
if ( WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN AND NOT MINGW)
Code: Select all
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=$HOME/cmake-toolchains/xcompile.txt -DHOST=i686-w64-mingw32 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBULLET_INCLUDE_DIR=/full/path/to/bullet-2.81-rev2613/src -DMYGUI_LIBRARIES=/usr/i686-w64-mingw32/usr/lib/libMyGUIEngine.dll.a -DMYGUI_PLATFORM_LIBRARIES=/usr/i686-w64-mingw32/usr/lib/libMyGUI.OgrePlatform.a -DQT_INCLUDE_DIR=<qt_dir>/include -DQT_LIBRARY_DIR=<qt_dir>/lib -DQT_QTCORE_INCLUDE_DIR=<qt_dir>/include/QtCore -DQT_QTGUI_INCLUDE_DIR=<qt_dir>/include/QtGui -DQT_QTCORE_LIBRARY_RELEASE=<qt_dir>/lib/libQtCore4.a -DQT_QTCORE_LIBRARY_DEBUG=<qt_dir>/lib/libQtCored4.a
You can try building it now, but you'll eventually hit some snags. So let's continue...
- Step 7b: Building OpenMW
Code: Select all
diff --git a/libs/platform/string.h b/libs/platform/string.h
index 5368d75..53fdfc2 100644
--- a/libs/platform/string.h
+++ b/libs/platform/string.h
@@ -7,15 +7,8 @@
#endif
#include <string.h>
-#if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) || defined(__MINGW32__)
+#if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
// need our own implementation of strnlen
-#ifdef __MINGW32__
-static size_t strnlen(const char *s, size_t n)
-{
- const char *p = (const char *)memchr(s, 0, n);
- return(p ? p-s : n);
-}
-#elif (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
static size_t mw_strnlen(const char *s, size_t n)
{
if (strnlen != NULL) {
@@ -30,5 +23,3 @@ static size_t mw_strnlen(const char *s, size_t n)
#endif
#endif
-
-#endif
The next error is simple enough:
Code: Select all
components/files/windowspath.cpp:9:21: fatal error: Shlwapi.h: No such file or directory
Next up seems to be a problem with mingw's own unknwn.h header. In /usr/i686-w64-mingw32/usr/include/unknwn.h there is this declaration for the IUnknown interface:
Code: Select all
extern "C++" {
struct IUnknown {
public:
BEGIN_INTERFACE
virtual HRESULT WINAPI QueryInterface(REFIID riid,void **ppvObject) = 0;
virtual ULONG WINAPI AddRef(void) = 0;
virtual ULONG WINAPI Release(void) = 0;
template<class Q> HRESULT WINAPI QueryInterface(Q **pp) { return QueryInterface(__uuidof(*pp),(void **)pp); }
END_INTERFACE
};
}
Code: Select all
/usr/i686-w64-mingw32/mingw/include/unknwn.h: In member function ‘HRESULT IUnknown::QueryInterface(Q**)’:
/usr/i686-w64-mingw32/mingw/include/unknwn.h:70:82: error: ‘*’ cannot appear in a constant-expression
/usr/i686-w64-mingw32/mingw/include/unknwn.h:70:82: error: a function call cannot appear in a constant-expression
Next... Boost (have I mentioned I hate it, yet? because I do). It seems boost/tr1/ headers don't like cross-compiling with mingw:
Code: Select all
/usr/i686-w64-mingw32/mingw/include/boost/tr1/detail/config_all.hpp:151:26: error: no include path in which to search for utility
Code: Select all
diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp
index 765f1ce..4509904 100644
--- a/components/files/configurationmanager.hpp
+++ b/components/files/configurationmanager.hpp
@@ -1,7 +1,7 @@
#ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP
#define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(__MINGW32__)
#include <boost/tr1/tr1/unordered_map>
#elif defined HAVE_UNORDERED_MAP
#include <unordered_map>
Code: Select all
diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp
index c27dce6..d0c843b 100644
--- a/apps/openmw/mwrender/terrain.cpp
+++ b/apps/openmw/mwrender/terrain.cpp
@@ -1,3 +1,4 @@
+#define BOOST_THREAD_USE_LIB
#include <boost/lexical_cast.hpp>
#include <OgreTerrain.h>
diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp
index 87ae817..8b9066e 100644
--- a/apps/openmw/mwrender/videoplayer.cpp
+++ b/apps/openmw/mwrender/videoplayer.cpp
@@ -1,3 +1,4 @@
+#define BOOST_THREAD_USE_LIB
#include "videoplayer.hpp"
#define __STDC_CONSTANT_MACROS
@@ -19,8 +20,8 @@
#include "renderconst.hpp"
-#ifdef _WIN32
-#include <BaseTsd.h>
+#if defined(_WIN32) && !defined(__MINGW32__)
+#include <basetsd.h>
typedef SSIZE_T ssize_t;
#endif
@@ -368,7 +369,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder
}
void open(const std::string&)
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(__MINGW32__)
{ fail(std::string("Invalid call to ")+__FUNCSIG__); }
#else
{ fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); }
diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp
index 690b073..55de64e 100644
--- a/apps/openmw/mwsound/openal_output.cpp
+++ b/apps/openmw/mwsound/openal_output.cpp
@@ -3,6 +3,7 @@
#include <iostream>
#include <vector>
+#define BOOST_THREAD_USE_LIB
#include <boost/thread.hpp>
#include "openal_output.hpp"
Code: Select all
Linking CXX executable ../../openmw.exe
/usr/i686-w64-mingw32/usr/lib/libboost_thread-mgw47-mt-1_51.a(thread.o):thread.cpp:(.text+0x53db): undefined reference to `boost::chrono::system_clock::now()'
../../components/libcomponents.a(windowspath.cpp.obj): In function `ZNK5Files11WindowsPath11getUserPathEv':
/home/kitty/projects/openmw/components/files/windowspath.cpp:37: undefined reference to `_imp__PathAppendA@8'
collect2: error: ld returned 1 exit status
Code: Select all
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 43415c9..7f72a8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -188,7 +188,7 @@ if (HAVE_UNORDERED_MAP)
endif ()
-set(BOOST_COMPONENTS system filesystem program_options thread date_time wave)
+set(BOOST_COMPONENTS system filesystem program_options thread chrono date_time wave)
IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON)
MSDN specifies PathAppendA comes from shlwapi.dll. That is also how mingw has defined it, as it's attempting to import the function. However, mingw does not have a shlwapi import, and instead has it as a static lib, /usr/i686-w64-mingw32/lib/libshlwapi.a, thus the method can't be imported even though it should be. The easiest thing to do here is to not use the PathAppend method:
Code: Select all
diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp
index e8f1a2b..9dd763a 100644
--- a/components/files/windowspath.cpp
+++ b/components/files/windowspath.cpp
@@ -6,7 +6,7 @@
#include <windows.h>
#include <shlobj.h>
-#include <Shlwapi.h>
+#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
@@ -33,10 +33,7 @@ boost::filesystem::path WindowsPath::getUserPath() const
memset(path, 0, sizeof(path));
if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path)))
- {
- PathAppend(path, TEXT("My Games"));
- userPath = boost::filesystem::path(path);
- }
+ userPath = boost::filesystem::path(path) / "My Games";
return userPath / mName;
}
If you want to attempt to run openmw with wine, make sure to build and run the ini importer:
Code: Select all
$ make mwiniimport
...
$ wine mwiniimport.exe c:/Games/Morrowind/Morrowind.ini openmw.cfg
Code: Select all
$ cp /usr/lib/gcc/i686-w64-mingw32/4.7.2/libgcc_s_sjlj-1.dll /usr/lib/gcc/i686-w64-mingw32/4.7.2/libstdc++-6.dll ./
This is as far as I've gotten so far. The Ogre DLLs are looking for libgcc_s_dw2-1.dll, but unfortunately my cross-compiler uses libgcc_s_sjlj-1.dll instead.