Monday, February 21, 2011

Configuring Eclipse to Compile a GTK+ Project Written in C, on a Windows Platform.


There are plenty of websites detailing how to get started with GTK+ programming. This page aims to fill a gap: how to setup the Eclipse environment operating in Windows XP.  This can be a frustrating task for someone who isn't very familiar with pkg-config and gcc command line switches.  Assuming you understand the basics of header files, libraries, and the Windows command line, this page should tell you what you need to know.

Background

When setting up a GTK+ based C project, there are a lot of headers and libraries that must be included.  This creates several issues in Eclipse, and even more when programming in a Windows environment.  The standard solution (which does not work for us) is to use the pkg-config utility to provide this info on the fly, as demonstrated below:
gcc -Wall -g helloworld.c -o helloworld `pkg-config --cflags gtk+-2.0` \
`pkg-config --libs gtk+-2.0`
This is taken straight out of the GTK+ 2.0 Tutorial - Compiling Hello World . It is a perfect solution for command line based development using a Linux style shell.  This will NOT work with Windows. Trying it will produce output like this:

 cc1.exe: error: unrecognized command line option "-fcflags"
cc1.exe: error: unrecognized command line option "-flibs"
The pkg-config utility is placed in back-ticks (usually above the tab key, on the same key as the "~" character), which tells the shell to run the command between the backticks first, and place the output in that place.  Running the pkg-config utility with the gtk+-2.0 package will output all the options and switches that gcc needs to add in the GTK+ libraries - specifically the header file paths, the libraries, and some other gcc switches. Windows does not do this. 

The key is to figure out what information pkg-config is trying to pass to gcc, and manually enter it in to Eclipse.  This process is long and it will take a while.  When you do it correctly, you get the added benefit of Eclipse knowing where all the header files are, which will allow the IDE to use all of those features that make IDEs so nice to work with.

How-To

1) Download Eclipse IDE for C/C++ Developers.  I am using the "Helios" release, which is the latest version as of this posting.  Every new version they increment the first letter of the name, so the next release will start with "I".  To guarantee compatibility with these instructions, stick to "Helios".  Unzip the folder wherever you want the program to reside.  It does not use an installer, it just sits in the folder and runs from there (NICE!)

2) Download the GTK+ Development Package.  Get the "All-in-one Bundle", version 2.22.  Unzip the folder wherever you would like to keep the files.

3) In Eclipse create a new "Hello World" project using the C language.  Verify that it runs, etc.

4) Copy and paste the Hello World in GTK+ example from the tutorial into your main.c file.  Note that it will NOT compile, and it cannot find the gtk/gtk.h file.  Eclipse will highlight the #include line and complain of an "Unresolved Inclusion". 

5) We need to add all the information for the header file locations, as well as some other gcc flags that are provided by the pkg-config utility, for the "cflags" option and enter it into your Eclipse project.  I warn you, this will not work right if there any spaces in the directory names in the path to your GTK+ bundle files. I advise that you at least make a temporary copy of the bundle to  C:\gtk+bundle\ to get the output from pkg-config.

Open a command prompt (Start->Run, "cmd"), navigate to your GTK+ bundle directory, to the "bin" subfolder. Run the following command:
pkg-config --cflags gtk+-2.0 > _temp.txt
This will run the pkg-config utility, specifying the gtk+-2.0 package, with the --cflags option.  The output will be piped into a file called _temp.txt.

Now you can open up the file _temp.txt in that directory to see the output.  Mine looks something like this:

-mms-bitfields -IC:/gtk+bundle/include/gtk-2.0 -IC:/gtk+bundle/lib/gtk-2.0/include -IC:/gtk+bundle/include/atk-1.0 -IC:/gtk+bundle/include/cairo -IC:/gtk+bundle/include/gdk-pixbuf-2.0 -IC:/gtk+bundle/include/pango-1.0 -IC:/gtk+bundle/include/glib-2.0 -IC:/gtk+bundle/lib/glib-2.0/include -IC:/gtk+bundle/include -IC:/gtk+bundle/include/freetype2 -IC:/gtk+bundle/include/libpng14 
Again, these are all flags that are intended to get passed to gcc when you compile. If all you want is to compile from the command line, you could probably put this string into an environment variable and use that environment variable in place of the pkg-config call.

I generally start by rearranging this output in notepad so that it is more readable, giving each option its own line:

-mms-bitfields
-IC:/gtk+bundle/include/gtk-2.0
-IC:/gtk+bundle/lib/gtk-2.0/include
-IC:/gtk+bundle/include/atk-1.0
-IC:/gtk+bundle/include/cairo
-IC:/gtk+bundle/include/gdk-pixbuf-2.0
-IC:/gtk+bundle/include/pango-1.0
-IC:/gtk+bundle/include/glib-2.0
-IC:/gtk+bundle/lib/glib-2.0/include
-IC:/gtk+bundle/include
-IC:/gtk+bundle/include/freetype2
-IC:/gtk+bundle/include/libpng14  
Here there are two types of things being specified: compiler options (-mms-bitfields) and header include file paths (-I).  These need to get entered into Eclipse.  Here is how you add them to the C project.  Note: This change is only for the open project, if you make a new project you will have to do this again.

First you need to right click your project in the Eclipse Project Explorer pane, which is the tree that is usually on the left of the screen.  Click Properties to bring up Properties of your project.  Navigate the tree on the left to C/C++ Build/Settings  and then on the Tool Settings tab go to the GCC C Compiler item in the tree:


Place the various option we got from pkg-config between "${COMMANDS}" and "${FLAGS}" in the "command line pattern" box. In this case it is the "-mms-bitfields" option.

Now we need to add the include files.  I like to use path name variables to simplify the process if I decide to move things around later.  The way I chose to do this was to create a path name variable called GTK_BUNDLE_PATH. This path variable can be used in place of the directory string, and if I want to move the bundle, I only have to change this one variable instead of all the include paths.  To set this up, in the Project Properties window navigate in the tree to C/C++ Build->Build Variables, and click "Add..." on the right side:

Fill it in as shown, with the Variable Name set to GTK_BUNDLE_DIR, type as PATH, and Value as the directory, in this case C:\gtk+bundle\.  "Apply to all configurations" will do this to both the "Debug" and "Release" build configurations:


Now that the path variable is established, let's take our reformatted pkg-config output from before, remove the options that we already entered ("-mms-bitfields" in my case), remove the -I include flag prefixes, and replace the package directory with a reference to the path variable, where we use ${GTK_BUNDLE_PATH} to signify that we want the value of GTK_BUNDLE_PATH placed there.  Also put quotes around each line:
"${GTK_BUNDLE_PATH}/include/gtk-2.0"
"${GTK_BUNDLE_PATH}/lib/gtk-2.0/include"
"${GTK_BUNDLE_PATH}/include/atk-1.0"
"${GTK_BUNDLE_PATH}/include/cairo"
"${GTK_BUNDLE_PATH}/include/gdk-pixbuf-2.0"
"${GTK_BUNDLE_PATH}/include/pango-1.0"
"${GTK_BUNDLE_PATH}/include/glib-2.0"
"${GTK_BUNDLE_PATH}/lib/glib-2.0/include"
"${GTK_BUNDLE_PATH}/include"
"${GTK_BUNDLE_PATH}/include/freetype2"
"${GTK_BUNDLE_PATH}/include/libpng14"
Be careful! Only replace "C:\gtk+bundle" with ${GTK_BUNDLE_PATH}  no more, no less!


Now that you have the include directories all preformatted, you can enter them into the project.  In the Project Properties window, you can navigate the tree to C/C++ Builds->Settings, in the Tool Settings tab navigate the tree to GCC C Compiler->Includes.  The top list on the right is the "Include Paths (-I)". Add each line from our list above as a seperate item to this list, using copy-paste, one at a time:


That should cover all the c flags, and should get your #include issues to go away.  Now the library files need to be added.



6) Next we need to add the library settings to the project.  Again we get these from pkg-config, with the --libs option:
pkg-config --libs gtk+-2.0 > _temp2.txt
 The output (in the text file _temp2.txt) should be something like this:
-LC:/gtk+bundle/lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0 -lintl 
Again let's clean up the formatting in notepad, giving each item it's own line:
-LC:/gtk+bundle/lib
-lgtk-win32-2.0
-lgdk-win32-2.0
-latk-1.0
-lgio-2.0
-lpangowin32-1.0
-lgdi32
-lpangocairo-1.0
-lgdk_pixbuf-2.0
-lpango-1.0
-lcairo
-lgobject-2.0
-lgmodule-2.0
-lgthread-2.0
-lglib-2.0
-lintl 
You need to know that -L specifies the path to look for libraries, and -l (lowercase L) specifies the name of a library.   I do a pre-cleanup of the list by getting rid of the option switches, and replacing the bundle path with the path variable we setup previously:


Paths:
${GTK_BUNDLE_PATH}/lib

Libraries:
gtk-win32-2.0
gdk-win32-2.0
atk-1.0
gio-2.0
pangowin32-1.0
gdi32
pangocairo-1.0
gdk_pixbuf-2.0
pango-1.0
cairo
gobject-2.0
gmodule-2.0
gthread-2.0
glib-2.0
intl  

(Side note: the -l option actually specifies to look for a library with the name that follows it, preceded with "lib" and ending with ".a" , so "-lfoo" ACTUALLY means a file called "libfoo.a".  Luckily you can ignore this for Eclipse purposes because it wants the text in exactly the same format as the -l option specifies).

Navigate the Eclipse Project Properties window to C/C++ Build->Settings, and on the Tool Settings tab navigate the tree to MinGW C Linker -> Libraries.  Here I add the libraries and the path, line by line, into the respective lists:



6) Say a prayer, and attempt to build.  If you get a bunch of errors, I am sorry.  Hopefully you can troubleshoot it.

Maybe someone out there who is experienced with eclipse can create some kind of utility to speed up the process?  If you know of any easier way, please let me know!