Building GTK apps for MS Windows on Linux

Contents

Cross-compiler and binutils
Win32 API
GTK API for win32
GTK pkgconfig files
Configuring and building sources for win32
DLL-building magic
Simple makefile example
GTK runtime files
Message catalogs
Creating a self-installing exe

Introduction

The intended readership for this page is those who are already quite comfortable with building GTK-based software on Linux (who know their way around the various tools), and who wish to prepare win32 versions of their programs without having to mess with that Other OS themselves.

I don't have time at present to write a full, coherent HOWTO. What you'll find here are various ideas, tips and examples in a fairly raw state. They are based on my experience in cross-building for Windows my program gretl. If you want to take a look at a fairly large-scale example of a cross build, grab the gretl source package from sourceforge and poke around in the win32 subdirectory of the source.

Since some elements below may date fairly quickly, I should say that I'm writing this in early October, 2004. I'm sure to have overlooked various details in these notes: if anyone spots missing stuff, please let me know and I'll update this page. If you find this page of interest, you might also want to take a look at my page on using some native win32 stuff in a GTK context.

Cross-compiler and binutils

Your first requirement is a cross-compiler and an appropriate set of binutils (as, ld and friends).

You can assemble these yourself or there is the option of grabbing a cross-compiler package (in binary or source form) from libsdl.org.

If you take the put-it-together-yourself route (as I have done), I recommend starting your shopping at mingw.org. Take a look at the mingw download area and decide how adventurous you want to be ("Candidate" releases of various vintages or the "Current" release). Download the source for selected releases of binutils and gcc. I am currently using gcc version 3.4.0 and binutils version 2.15.90, and they work fine.

Now you need to decide on a location for your cross system. I put it in /opt/cross-tools. Having chosen a place to install the tools, configure and make them. I use these scripts:

#!/bin/sh
cd binutils-whatever
mkdir build
cd build
../configure --disable-nls --target=mingw32 \
--prefix=/opt/cross-tools --disable-shared
make  "CFLAGS=-O2 -fno-exceptions" "LDFLAGS=-s"
make install
#!/bin/sh
cd gcc-whatever
mkdir build
cd build
../configure --target=mingw32 --prefix=/opt/cross-tools --disable-nls \
 --disable-shared --enable-languages=c,c++,f77 
make CFLAGS="-O2 -fomit-frame-pointer" LDFLAGS=-s
make install

You can probably throw away the documentation for these tools, since you'll already have it for the Linux versions:

rm -rf /opt/cross-tools/info /opt/cross-tools/man

or something like that.

Win32 API

To get any further you need to install the appropriate headers and import libraries to support the win32 API. As of this writing, the package you want is w32api-3.1.tar.gz (again, from mingw.org). Untar the package in the appropriate place. For example if you've chosen /opt/cross-tools for your cross tools root, and you've configured gcc with a target of mingw32, your cross-gcc will be found in /opt/cross-tools/mingw32 and you'd do, e.g.

cd /opt/cross-tools/mingw32
tar xvfz w32api-3.1.tar.gz

GTK API for win32

It's a GTK app you're building, so you need all the appropriate headers and import libs. You could (perhaps) build all that stuff yourself, but why bother when Tor Lillqvist has done the job for us? [If you do feel like trying this, check out the documentation for Cross-compiling the GLib package.]

Pick up all the current "dev" zipfiles (atk, glib, gtk, pango) from the GTK download page. Tor also offers links to win32 versions of various GTK dependencies, such as libiconv, libpng, zlib and libxml2. You'll want to grab these too. In all cases you need the "dev" files, containing headers and the "import libraries" (*.a or *.lib) that are needed at compile time. If you want to package and distribute a GTK runtime along with your app (making it self-contained), you'll also need to grab the corresponding "bin" or "runtime" packages. These contain the dlls corresponding to the import libraries, along with various other runtime files (see below for details on this).

I recommend unzipping the GTK and associated "dev" files using the same root as mentioned above for the win32 API. That is, import libs go into /opt/cross-tools/mingw32/lib on my system, and headers are based at /opt/cross-tools/mingw32/include.

Note on import libraries: You're likely to run across more than one variety of import library. There are *.a libs, which work nicely with a cross gcc. Tor's packages include these. But some packages don't include *.a libraries, only Microsoft-style *.lib files. These can be used OK with the utility mingw32-dllwrap, but not (so far as I can tell) directly with cross gcc. On the other hand, you may find that if the .lib files don't work (linker errors) you can substitute the corresponding dlls and get linking to work.

GTK pkgconfig files

The pkgconfig files supplied with Tor's GTK "dev" packages are designed for use on Windows and have to be modified slightly for a cross build (so that they contain the correct prefix). Here is a script that does the job (to be run in the relevant cross pkgconfig directory) :

#!/bin/sh

TARGET=/opt/cross-tools/mingw32

for f in *.pc ; do
   if grep 'prefix=/target' $f >/dev/null 2>&1 ; then
     cat $f | sed s+^prefix=/target+prefix=$TARGET+ > $f.tmp
     mv $f.tmp $f
   fi
done  

Configuring and building sources for win32

For cross-building your own app, or for cross-building special dependencies (extra dlls), there are broadly two approaches:

  1. Use the regular mechanism of configure scripts in conjunction with pkg-config, but in cross mode.

  2. Use hand-crafted Makefiles.

The first option is preferable if it works OK. Obviously you're going to need some environment variable magic to get anything working. Having experimented a bit, I now start by sourcing the following (source cross.env).

# cross.env
PREFIX=/opt/cross-tools
TARGET=mingw32
export CC="mingw32-gcc -mms-bitfields"
export CXX="mingw32-g++ -mms-bitfields"
export CFLAGS="-O2 -march=i586 -mms-bitfields"
export CXXFLAGS="-O2 -march=i586 -mms-bitfields"
export PKG_CONFIG_PATH=$PREFIX/$TARGET/lib/pkgconfig
export PATH=$PREFIX/bin:$PREFIX/$TARGET/bin:/bin:/usr/bin
export LD_LIBRARY_PATH=$PREFIX/$TARGET/lib
export LDFLAGS=-L$PREFIX/$TARGET/lib
export OBJDUMP=$PREFIX/bin/mingw32-objdump
export HOST_CC=/usr/bin/gcc

Note: The -mms-bitfields flag is essential if you want your app to actually run on win32 (when using Tor Lillqvist's pre-built GTK runtime at any rate).

In place of plain configure I use this script called cross-configure:

#!/bin/sh

TARGET=mingw32
cache=win32.cache
sh configure --cache-file="$cache" \
        --target=$TARGET --host=$TARGET --build=i686-linux \
        --prefix=/opt/cross-tools/mingw32 $*
status=$?
rm -f "$cache"
exit $status

With the environment set up correctly, make can be used as is (no fancy stuff required).

Note: An alternative to the above approach is to load all the required environment settings into your cross-configure script, and write a corresponding cross-make script that invokes make with the appropriate environment. Advantage of this alternative: you don't need to "pollute" your working environment with all the cross-compilation settings, as happens when you source cross.env. Disadvantage: it's easy to forget what you're doing and type make when you mean cross-make, which results in a big mess.

Whichever variant of the "cross-configure" approach you employ, you may run into problems building dlls. For some reason libtool (I'm currently using version 1.5.10) does not seem to want to make Windows dlls on Linux (I can get static libraries OK). There are several "issues" here -- I'm gradually coming to understand them, but I don't have a sure fix at this point. [Update October 6, 2004: Making a bit more progress -- details here.]

In the meantime, I tend to resort the following…

DLL-building magic

Here's a sample of a Makefile that "works for me" for cross-building dlls (this one makes a dll out of the Cephes library code for figuring probability-values). If I get stuck using other methods I copy-n-paste from this and modify as needed.

CC = mingw32-gcc -Wall -O2 -mms-bitfields
AS = mingw32-as
DLLWRAP = mingw32-dllwrap

CFLAGS = -I.

PROBSRC = bdtr.c btdtr.c chdtr.c drand.c expx2.c fdtr.c gamma.c gdtr.c \
	igam.c igami.c incbet.c incbi.c mtherr.c nbdtr.c ndtr.c ndtri.c \
        pdtr.c stdtr.c unity.c polevl.c const.c

PROBOBJ = $(PROBSRC:.c=.o)

%.o: %.c
	$(CC) -c $(CFLAGS) $<

DLLWRAP_FLAGS = --as=$(AS) --export-all --driver-name $(CC) -s

# build libprob.dll, and create a corresponding import library
# libprob.a

libprob.dll: $(PROBOBJ) 
	$(DLLWRAP) $(DLLWRAP_FLAGS) \
	--output-def libprob.def --implib libprob.a \
	-o $@ $^ 

The routine is: make all the object files as usual, then package them into a dll using mingw32-dllwrap. If you need additional libraries linked in, stick them onto the end of the dllwrap command, as in:

libgretl.dll: $(LIBOBJ) $(MINOBJ) 
        $(DLLWRAP) $(DLLWRAP_FLAGS) \
        --output-def libgretl.def --implib libgretl.a \
        -o $@ $^ -lf2c -lm -L$(imports) -lxml2 -lz -lintl -lprob -lgmp \
        -lmingwex $(GLIBLIB) $(LAPACK_LIBS)

Simple makefile example

Here's an example of a complete cross-Makefile for a trivial GTK program. Note the compiler flag -mwindows: this is required to produce a windows application as opposed to a win32 console application (which automatically spawns a console when invoked via a menu or icon).

CC = mingw32-gcc -O2 -Wall -mms-bitfields -mwindows
PKG_CONFIG_PATH = /opt/cross-tools/mingw32/lib/pkgconfig

CFLAGS := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) \
          pkg-config --cflags gtk+-win32-2.0)
LIBS := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) \
          pkg-config --libs gtk+-win32-2.0)

foo.exe: foo.c
        $(CC) -o $@ $(CFLAGS) $< $(LIBS)

GTK runtime files

By "GTK runtime files" I mean the set of DLLs, modules, configuration files and message catalogs that support the actual execution of a GTK program.

When you distribute a GTK application for Linux, it's natural to assume that the GTK runtime will already be in place -- or if it's not, that the user will be able to install it easily from packages such as rpms or debs. It's not your responsibility as an application developer to provide the basic runtime.

On MS Windows, of course, GTK is not part of the basic kit and you face a choice:

If you decide on the first option you may want to take a look at the GTK installers offered by Alex Shaduri and Jernej Simoncic.

I have gone for the second option: I prefer to give users the simplest possible one-step installation. If you take this approach you will probably want to find out what is the minimal subset of GTK runtime files required to support your application. You will surely need all the basic DLLs for atk, glib, gtk and pango, plus their essential dependencies such as zlib, libxml2, libiconv and libintl. You'll need some of the dynamically-loadable modules for gtk and pango, but probably not all of them. You may or may not need message catalogs for gtk and friends. It may take a bit of trial and error to figure out the essential subset.

For reference, I show below a listing of the GTK runtime files that I distribute with my application. The path to these files is relative to the installation directory chosen by the user at install time (see Creating a self-installing exe below).

The GPL:

COPYING

Core GTK DLLs:

libatk-1.0-0.dll
libgdk_pixbuf-2.0-0.dll
libgdk-win32-2.0-0.dll
libglib-2.0-0.dll
libgmodule-2.0-0.dll
libgobject-2.0-0.dll
libgthread-2.0-0.dll
libgtk-win32-2.0-0.dll
libpango-1.0-0.dll
libpangoft2-1.0-0.dll
libpangowin32-1.0-0.dll

Basic dependency DLLs:

iconv.dll
intl.dll
libpng12.dll
libxml2.dll
zlib1.dll

Basic config files:

etc/pango/pango.modules
etc/pango/pango.aliases
etc/gtk-2.0/gdk-pixbuf.loaders
etc/gtk-2.0/gtkrc

Image-file loaders (subset for the image formats used by my app):

lib/gtk-2.0/2.4.0/loaders/libpixbufloader-png.dll
lib/gtk-2.0/2.4.0/loaders/libpixbufloader-xpm.dll

Message catalogs (subset for the languages supported by my app):

lib/locale/es/LC_MESSAGES/atk10.mo
lib/locale/es/LC_MESSAGES/glib20.mo
lib/locale/es/LC_MESSAGES/gtk20.mo
lib/locale/fr/LC_MESSAGES/atk10.mo
lib/locale/fr/LC_MESSAGES/glib20.mo
lib/locale/fr/LC_MESSAGES/gtk20.mo
lib/locale/it/LC_MESSAGES/atk10.mo
lib/locale/it/LC_MESSAGES/glib20.mo
lib/locale/it/LC_MESSAGES/gtk20.mo
lib/locale/ja/LC_MESSAGES/glib20.mo
lib/locale/ja/LC_MESSAGES/gtk20.mo
lib/locale/pl/LC_MESSAGES/glib20.mo
lib/locale/pl/LC_MESSAGES/gtk20.mo

Pango modules (subset: my app only supports European languages):

lib/pango/1.4.0/modules/pango-basic-win32.dll
lib/pango/1.4.0/modules/pango-basic-fc.dll

"WIMP" support (optional: let the user make the app look more Windows-like):

etc/gtk-2.0/gtkrc.wimp
lib/gtk-2.0/2.4.0/engines/libwimp.dll

Message catalogs for your application

If your app is internationalized, you'll want to make binary message catalogs (.mo) in win32 format. I do this by using msgfmt.exe (available in the GNU gettext package for win32) under wine.

Creating a self-installing exe

Yes, you can even do this without leaving Linux -- with a little help from wine. Jordan Russell makes available a nice free installer-builder, Inno Setup. It's a Windows program, but it runs fine on Linux under wine (its own self-installer works fine under wine too.) It is fully scriptable and its compiler can be run non-interactively.

In case you're interested, here is a sample script, gretl.iss, for use with Inno Setup.


Allin Cottrell <cottrell@wfu.edu>

Last modified: Wed Oct 6, 2004

Valid HTML 4.0!