Cross-compiling GTK+ apps for Windows

Having recently cross-compiled a small GTK+ application for Windows, I'll try and detail the process I used here. It consists of several sections:

  1. Installing MinGW
  2. Modifying your configure.in and Makefile.am
  3. Modifying your source
  4. Installing libraries on Windows

Installing MinGW

On Ubuntu 7.10, I simply installed the "mingw32", "mingw32-binutils" and "mingw32-runtime" packages, although if they're not available for your distribution, you should be able to get them from the MinGW website.

When installed, these packages create a minimal build tree in /usr/i586-mingw32msvc/, containing Windows versions of various standard header files, as well as other files required to compile against Windows libraries.

For you to be able to compile your GTK+ app in this build environment, the Windows GTK+ development files need to be placed there. You can get these from the GTK+ for Windows page, where you should download the developer packages and runtimes for:

  • glib
  • gtk
  • pango
  • atk
  • cairo
  • win_iconv
  • gettext-runtime
  • libpng

Uncompress these packages into /usr/i586-mingw32msvc/.

Obviously, if your application depends on other libraries, then their Windows development headers and libraries will also have to be installed in this directory. For example, my application depended on SQLite, so I had to download the precompiled SQLite Windows DLL and place it in /usr/i586-mingw32msvc/lib/ along with its .def file, and then copy my system sqlite.h header to the /usr/i586-mingw32msvc/include/ directory. You're basically aiming to have the correct headers and Windows libraries for the program you're trying to compile in that build tree, so that your application can be compiled against them.

Once they're in place, you need to execute the following commands to make sure the pkg-config files are all OK and have the correct prefixes:

cd /usr/i586-mingw32msvc
sed -i 's|^prefix=.*$|prefix=/usr/i586-mingw32msvc|g' lib/pkgconfig/*.pc
cd ./lib
for f in *.lib; do mv $f lib${f%%lib}a; done

Finally, you need to create a i586-mingw32msvc-pkg-config script in your $PATH containing the following:

 #!/bin/sh

PKG_CONFIG_LIBDIR=/usr/i586-mingw32msvc/lib/pkgconfig \
PKG_CONFIG_PATH=/usr/i586-mingw32msvc/lib/pkgconfig pkg-config $*

As the MinGW packages don't come with their own version of pkg-config, this version ensures that the compiler and linker are given the correct arguments for the build environment we've created.

Modifying your configure.in and Makefile.am

Few modifications have to be made to your configure.in if you're building a conventional GNOME application which uses (for example) GLib's gettext support, as GLib handles all the cross-platform difficulties that brings in. Add the following to your configure.in:

case "${host}" in
        i[[3456789]]86-mingw32*) WIN32="yes" ;;
        *cygwin*) WIN32="yes" ;;
        *) WIN32="no" ;;
esac
AM_CONDITIONAL([WIN32], test "$WIN32" = "yes")

This defines a WIN32 variable in your Makefiles, allowing you to change build rules for Windows. The modifications to your Makefile.am for your src directory will use this, so add the following to it:

if WIN32
AM_CFLAGS += \
        -mms-bitfields

AM_LDFLAGS += \
        -Wl,-subsystem,windows
endif

The -mms-bitfields option ensures that structs are packed similarly to how they're packed for Tor's GTK+ DLLs, so that your application can use the GTK+ API without crashing mysteriously. The -Wl,-subsystem,windows option stops Windows launching a console every time your application is executed.

Once these modifications are complete, you may be able to compile your application and run it successfully on Windows with the following commands:

./autogen.sh --host=i586-mingw32msvc
make clean
make

(--host here specifies that the application is to be run on an x86 Windows machine.)

Modifying your source

However, although you might find your application will run, it may not work completely. If, for example, you use GtkBuilder for your UI, you will probably find that none of the UI signal callbacks are being executed. This is because the callback functions are not being exported properly from the .exe, even though they might be exported fine when your application's built as an ELF.

The solution to this is to go through your code and make sure G_MODULE_EXPORT is put before the declaration of every callback function as follows:

-void
+G_MODULE_EXPORT void
 main_window_destroy_cb (GtkWindow *window, gpointer user_data)
 {

When compiling on Linux, this will have absolutely no effect, but when compiling on Windows, the macro expands to __declspec(dllexport), which marks the symbol as one to be exported, so that it can be called by gmodule. Although the nomenclature may suggest otherwise, this works for symbols in both .exes and DLLs. Once recompiled, you should find that your application works!

Installing libraries on Windows

Although this is covered in many other places, it's important that the libraries your application uses are actually installed on the Windows system onto which you want to deploy before you run your application. The simplest way to install GTK, GLib and all their dependencies is to use the installer from http://sourceforge.net/projects/gtk-win/.

Note that you'll still need to install any other libraries your application uses (such as, in my case, SQLite).

Projects/GTK+/Win32/Apps (last edited 2013-11-22 15:36:58 by WilliamJonMcCann)