Adding GNOME

Most of the information presented so far in this chapter has been pretty generic. You could apply it to virtually any open-source UNIX project. Here's where we tie it all together, adding the GNOME side of things.

GNOME Macros

GNOME is a very complicated environment by itself; furthermore, all of it sits on top of the X Window System and the GIMP Toolkit (GTK+), each one of which is complex enough to leave your head spinning for weeks. Any measures we can take to ease the confusion and make our software more stable and easier to compile will pay off many times over in the long run. To this end, GNOME supplies quite a few m4 macros that you can use with autoconf. They all fit nicely in your configure.in file, although in some cases you must be careful of the order in which you call them. In particular, the AC_PROG_CC macro must be called before any macros that compile test programs to check for features.

To use these features you'll need a recent copy of the GNOME macros directory.3 You can get this from the GNOME source code repository-using the Concurrent Versions System (CVS) tool4-in the gnome-common module. Simply copy the macros subdirectory from the gnome-common module into your project, directly under the top-level directory. Then add the following line to configure.in, before any other calls to the GNOME macros:

AM_ACLOCAL_INCLUDE(macros)
        

This command makes the m4 files in the macros directory available to the autoconf system. If you forget to add it, aclocal won't pull those macros into aclocal.m4, autoconf will fail to expand them, and configure will bail out when it hits the raw, nonexpanded macros because the bash shell does not understand what you mean when you tell it to run the GNOME_INIT command.

This brings us to one of the most fundamental GNOME macros: GNOME_INIT. GNOME_INIT sets up the compile environment for GNOME, including a host of AC_SUBST macros for such variables as GNOME_LIBS, GNOMEUI_LIBS, GNOME_LIBDIR, and GNOME_INCLUDEDIR. It also tests for the gnome-config script (see Section 3.5.2). For a full accounting, take a look at gnome-common/macros/gnome.m4.

GNOME relies a lot on the X Window System. Most of the X-specific checks you'll need are covered by the GNOME_X_CHECKS macro. This macro takes care of searching for GTK+, by internally using the AM_PATH_GTK macro (which, incidentally, is installed by GTK+). It also checks for X11 session management, the xpm library, and the pthread library.

When you're still developing your application, you may want easy control of the number of warnings the compiler generates. GNOME has just the answer for this need: the GNOME_COMPILE_WARNINGS and GNOME_CXX_WARNINGS macros. You can slip either of these macros into configure.in, to enable a wide array of handy compile-time feedback. The former macro is geared toward the C compiler (currently, it's gcc specific), and the latter macro is for C++. To turn on the C warnings, you can add this flag to configure: --enable-com- pile-warnings. The C++ version is --enable-cxx-warnings. The acceptable values are yes, no, and minimum; minimum is the default. Thus if a developer wanted to compile a C++ package with maximum warnings just before a release, he or she could invoke the configure script like this:

./configure --enable-cxx-warnings=yes
        

The only real difference among the three settings is the variety of -W flags added to the CFLAGS and CXXFLAGS variables.

gnome-config

After you have configure.in set up, the next logical step is creating the makefile. Nothing in GNOME mandates that you should use automake to generate your makefiles (or even autoconf to set up your configure script). These tools make it much easier to set up a project, but they are still a convenience, not a requirement. If you do decide you want to write all your makefiles by hand, or if you want to compile a single test file from the command line, you will prob- ably want to use the gnome-config tool to keep your compiler options consistent and accurate. You can also use it in your Makefile.am files, although as we'll see in Section 3.5.3, this isn't usually necessary because GNOME takes the liberty of setting up some makefile variables for you.

The gnome-config tool is a shell script supplied with the gnome-libs package. autoconf creates it during the gnome-libs configuration stage from the gnome-config.in file, substituting the various values that it detects in the GNOME_INIT m4 macro. These variables are hard-coded into the generated gnome-config script; you can access them in many forms, through command line options to gnome-config. For example, if you need to know the install prefix to the gnome-libs package, you can call gnome-config --prefix. The GNOME include directory is in gnome-config --includedir, and the libraries are in gnome-config --libdir. Thus if you wanted to set a variable in your handwritten makefile for the GNOME include path, you could do something like this:

GNOME_INCLUDE_DIR=`gnome-config --includedir`
MYAPP_CFLAGS="-I$(gnome_include_dir)"
        

Notice the special back-ticks around the command (commonly found in the upper left-hand corner of the keyboard, on the same key as the tilde, ~), as opposed to the normal single quotation marks (usually found on the same key as the double quotation mark). The back-ticks instruct a shell to execute the contents they delineate and use the results of that command instead of the text of the command. This is known as a command expansion.

When you use back-ticks inside a makefile, the command will not be expanded until make fires off a shell to execute a rule that references it. make blindly passes it on to the shell, unaware that the back-ticks mean anything special. Thus you should depend on the expanded form of back-ticks only within the context of makefile rules.

In the example just given, if gnome-libs were installed to /usr/local, the $(GNOME_INCLUDE_DIR) variable would resolve to /usr/local/include when it was used in a make rule, but it would still contain the contents `gnome-config --includedir` anytime it was referenced inside the makefile.

An alternative GNU-specific syntax for command expansion instructs make to execute a shell immediately rather than waiting for a makefile rule. It takes the form of $(shell command). You can use this form without the limitations of the back-tick form, but be aware that this second form is not supported by most non-GNU flavors of make. With the GNU form of command expansion, our example would look like this:

GNOME_INCLUDE_DIR=$(shell gnome-config --includedir)
        

If you're using autoconf and automake and you've included the GNOME_INIT macro in your configure.in file, things will be even easier for you. The $(GNOME_CONFIG) variable will be available to your Makefile.am files, and you should use that instead of calling gnome-config directly. In most cases, however, you shouldn't need to invoke gnome-config at all. GNOME_INIT defines some helpful makefile variables on the basis of some commonly used invocations of gnome-config, including the imaginary $gnome_include_dir variable we introduced here. We'll go into these variables in more detail in Section 3.5.3.

The gnome-config script also lets you poll various GNOME library modules for information about such things as version numbers, compile-time flags, and link-time flags. gnome-config addresses two types of modules: core modules that are integrated directly into the gnome-config script, and external modules that gnome-config dynamically detects during runtime. You can get a list of both types of modules by running gnome-config --help at the command line. The core libraries are listed in the "Known values for LIBRARY" section of the output; the external modules come at the very end of the help message. Listing 3.7 shows one possible listing of the options for a typical gnome-libs installation.

Listing 3.7 Output of gnome-config --help

$ gnome-config --help
Usage: gnome-config [OPTION]... [LIBRARY]...

Generic options
  --version     output gnome version information.
  --modversion  output the module version information.
  --help        display this help and exit.

Compilation support options
  --cflags      print pre-processor and compiler flags
  --libs        print library linking information
  --libs-only-L only print the -L/-R part of --libs
  --libs-only-l only print the -l part of --libs

Install directories gnome-libs was configured to
  --prefix  --exec-prefix  --bindir  --sbindir  --libexecdir
  --datadir  --sysconfdir  --sharedstatedir  --localstatedir
  --libdir  --infodir  --mandir  --includedir

Known values for LIBRARY are:

    glib        (calls glib-config)
    idl         (to be used with orbit-idl)
    gnome
    gnomeui
    gnorba
    gtk         (calls gtk-config)
    gtkxmhtml   (only --libs)
    zvt         (only --libs)

If LIBRARY is none of these,

    /opt/gnome/lib/<LIBRARY>Conf.sh

is looked in for the necessary information. Those currently
installed appear to be:

applets, bonobo, capplet, docklets, gdk_pixbuf,
gnomecanvaspixbuf, gnomemm, libIDL, libart, libglade,
libgtop, obGnome, print, vfs, vfscorba, vfspthread, xml
        

To retrieve information about a specific module, include that module on the command line with the appropriate flag. If you wanted to find out what compiler flags you should use in your new GNOME application, you could add the following lines to your makefile:

MYAPP_CFLAGS=`gnome-config gnomeui --cflags`
MYAPP_LDFLAGS=`gnome-config gnomeui --libs`
        

These two commands will give you all the compile options you need to build a GNOME application. From the command line, they look something like this, assuming gnome-libs was installed with a prefix of /opt/gnome:

$ gnome-config gnomeui --cflags
-I/opt/gnome/include -DNEED_GNOMESUPPORT_H
-I/opt/gnome/lib/gnome-libs/include
-I/opt/gnome/lib/glib/include
$ gnome-config gnomeui --libs
-rdynamic -L/opt/gnome/lib -L/usr/X11/lib -lgnomeui -lart_lgpl
-lgdk_imlib -lSM -lICE -lgtk -lgdk -lgmodule -lXext -lX11 -lm
-lgnome -lgnomesupport -ldb -lglib -ldl
        

If for some reason you need to separate the library objects (-l) from the library paths (-L), you can use the --libs-only-l and --libs-only-L options instead.

$ gnome-config gnomeui --libs-only-l
-rdynamic -lgnomeui -lart_lgpl -lgdk_imlib -lSM -lICE -lgtk
-lgdk -lgmodule -lXext -lX11 -lm -lgnome -lgnomesupport -ldb
-lglib -ldl
$ gnome-config gnomeui --libs-only-L
-rdynamic -L/opt/gnome/lib -L/usr/X11/lib
        

To find out the name and version number of an installed package, you can use the --modversion parameter. As the name implies, this parameter works predictably only on the external modules. The return value for --modversion defaults to the version of gnome-libs; if a module doesn't explicitly override it, gnome-config will return that default. This leads to confusing behavior, like this:

$ gnome-config --modversion bonobo
bonobo-0.10
$ gnome-config --modversion gtk
gnome-libs-1.2.0
        

GTK+ does not share a version number with gnome-libs. If you want to find out the version number of GTK+ or GLib, you should call gtk-config or glib-config directly. If you want to know the version of gnome-libs itself, you should use --version instead of --modversion.

$ gtk-config --version
1.2.8
$ gnome-config --version
gnome-libs 1.2.0
        

The gnome-config script is a very handy interface for retrieving package information and compile parameters. It is fairly consistent, but not always so. You should always test your invocation syntax on the command line before putting it into a makefile, and be prepared for gnome-config's behavior to change in subtle ways between major releases of gnome-libs.

GNOME Makefile Variables

As if gnome-config didn't already make life easy enough, the GNOME_INIT macro in gnome.m4 defines a handful of shrink-wrapped variables that you can use in your Makefile.am files without any other preparation on your part, aside from including GNOME_INIT in your configure.in file.

Some of these variables concern GNOME libraries that we won't cover in this book, such as zvt, the virtual terminal library used by the gnome-terminal application, and gnorba, the CORBA helper library used by gnome-libs. The four variables that are most interesting to us concern libgnome and libgnomeui. The contents of these four variables arise from calling gnome-config on the gnome and gnomeui modules with different options. Here is the relevant code from the GNOME_INIT macro, cleaned up a little for clarity:

AC_PATH_PROG(GNOME_CONFIG, gnome-config)
GNOME_LIBS="`$GNOME_CONFIG --libs-only-l gnome`"
GNOMEUI_LIBS="`$GNOME_CONFIG --libs-only-l gnomeui`"
GNOME_LIBDIR="`$GNOME_CONFIG --libs-only-L gnomeui`"
GNOME_INCLUDEDIR="`$GNOME_CONFIG --cflags gnomeui`"
AC_SUBST(GNOME_LIBS)
AC_SUBST(GNOMEUI_LIBS)
AC_SUBST(GNOME_LIBDIR)
AC_SUBST(GNOME_INCLUDEDIR)
        

You can probably get a good idea of what each variable is useful for, just by looking at the gnome-config command for each one. We'll see how and where to use them in Makefile.am files in Section 3.5.5. If you're interested in digging around behind the scenes to see how all the various options are generated and where the contents come from, take a look at the gnome-config.in file in the gnome-libs package.

autogen.sh

As we saw in the libtool example in Section 3.4.3, all these tools can lead to quite a long series of command invocations, each of which needs to be called in the right order, with the right parameters. Developers have enough tech support problems that result from buggy code without worrying about debugging the install process for each new user that comes along. To cut down on this excess traffic, and to make it easier for themselves, the GNOME developers created a template build script, called autogen.sh, to make sure everything is called in the right order. Also, it's simply more convenient to have to run only one command.

The autogen.sh script runs everything for you, up to but not including the make command. The install procedures for nearly all packages properly modified for GNOME look identical:

$ tar xzf <package>.tar.gz
$ cd <package>
$ ./autogen.sh
$ make
$ make install
        

The master autogen.sh script resides in the macros directory with the GNOME m4 files. Each distribution typically has a thin wrapper autogen.sh script in the top-level directory, next to the configure.in file. In this wrapper script you can add some additional checks before calling the master autogen.sh. Most wrapper scripts look something like Listing 3.8.

Listing 3.8 Sample autogen.sh Wrapper Script from Gnome-games

#!/bin/sh
# Run this to generate all the initial makefiles, etc.

srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.

PKG_NAME="Gnome Games"

(test -f $srcdir/configure.in \
  && test -d $srcdir/gnomine \
  && test -d $srcdir/same-gnome) || {'
    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
    echo " top-level gnome directory"
    exit 1
}

. $srcdir/macros/autogen.sh
        

The master autogen.sh script does various things. It checks for the existence of the autoconf and automake tools-although it doesn't specifically check the current version of them, so an out-of-date version of autoconf or automake can still cause problems-as well as the GNU gettext library. It then embarks on a recursive search, starting with the top-level directory, looking for all configure.in files in the package. It does this in case you have multiple subprojects in your distribution. Normally you'll have only one configure.in file, in the top directory, but just in case, autogen.sh checks for more. This check is necessary because you must run autoconf separately on each configure.in file. If you run autoconf on only the top-level directory, the sublevel configure.in files will end up being ignored.

The autogen.sh script makes good use of grep, on each iteration looking in your configure.in file for certain macros and responding accordingly. For example, if it finds AM_GNU_GETTEXT or AM_GNOME_GETTEXT, it will run gettextize to set things up for internationalization (see Section 4.6). If autogen.sh spies an AM_PROG_LIBTOOL macro, it will run libtoolize for you. If it sees AM_CONFIG_HEADER, it will run autoheader. Next it runs automake and autoconf, with the proper parameters. Finally, it runs configure for you, passing along any parameters you gave to autogen.sh. To top things off, it adds a message to remind you that you still have to run make.

This is a good example of clever build scripting. Take a look at the master version of autogen.sh when you have a chance.

Some Grumpy GNOME Examples

We've covered quite a lot of ground in this chapter. The GNOME build environment is extremely complex and covers many different strata, including over a dozen scripts of various types. It is time now to tie everything together and see if we can come up with a few practical test cases that you can use in your own development efforts.

In this section we will cover four build topologies, four ways of building and installing the same set of source files (see Figure 3.3). Since we already have working sample code from Section 3.4.3, we'll recycle that for our GNOME examples, adding a few minor touches as we go. Our four build cases all use the same code base, but they differ in the way they link the libraries together:

Figure 3-3. Four Grumpy Library Cases

We won't change much of the original grump example. First we'll split grump.c into two separate files, one for each function. This is important for the fourth build case, in which we create two libraries, one from each file, grump.c and grump_more.c. We'll also change the #include statement in grump.h to pull in gnome.h rather than stdlib.h (this works because gnome.h in turn includes stdlib.h). Finally, to make use of gnome.h we will change our printf( ) statements to g_print( ) statements. Technically, since g_print( ) is part of GLib, not gnome-libs, we aren't really making good use of gnome.h, but for purposes of demonstration, g_print( ) is fine.

You can use the same techniques to set up most, if not all, of your full-blooded GNOME applications. Listings 3.9 through 3.12 contain the source code for the four files in our example. Listing 3.13 contains our configure.in file. These five files will remain the same throughout all four cases; only the Makefile.am files will change.

Listing 3.9 grump.h for GNOME Grump Example

# include <gnome.h>

void grump_some(  );
void grump_a_lot_more(  );
        
Listing 3.10 grump.c for GNOME Grump Example

# include "grump.h"

void grump_some(  )
{
  g_print("Oh, bother!...\n\n");
}
        
Listing 3.11 grump_more.c for GNOME Grump Example

# include "grump.h"

void grump_a_lot_more(  )
{
  int i, index;
  char *grumps[5] = { "Aargh!", "Be gone!", "Sigh...",
    "Not again!", "Go away!" };

  for (i = 0; i < 5; i++)
  {
    index = (5.0 * rand(  ) / (RAND_MAX + 1.0));
    g_print("%s\n", grumps[index]);
  }
}
        
Listing 3.12 main.c for GNOME Grump Example

# include "grump.h"

int main(int argc, char *argv[])
{
  grump_some(  );
  grump_a_lot_more(  );
}
        
Listing 3.13 configure.in for GNOME Grump Example

AC_INIT(grump.c)
AM_INIT_AUTOMAKE(myapp, 0.0.1)

AC_PROG_CC
AC_HEADER_STDC
AM_PROG_LIBTOOL
GNOME_INIT

AC_OUTPUT([
Makefile
])
        

In case 1, whose Makefile.am is outlined in Listing 3.14, we'll link all three .c files directly into the grumpalot executable. We'll pass GNOME's compile flags to gcc in automake's global $(INCLUDES) variable. automake will add the contents of $(INCLUDES) to the compile line for every .c file we list in that Makefile.am file. We want our final executable target, grumpalot, to be installed in $(bindir), so we'll assign it to bin_PROGRAMS. Finally, we'll list the source files under the _SOURCES primary, and the flags we want to send to the linker in the _LDADD primary.

Listing 3.14 Makefile.am for Case 1 of GNOME Grump Example

INCLUDES = $(GNOME_INCLUDEDIR)

bin_PROGRAMS = grumpalot

grumpalot_SOURCES = main.c grump.c grump_more.c
grumpalot_LDADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS)
        

To configure and compile the GNOME version of our grumpalot program, we follow the same series of commands we used for the one in Section 3.4.3:

$ libtoolize
$ aclocal
$ touch NEWS README AUTHORS ChangeLog
$ automake --add-missing --gnu
$ autoconf
$ ./configure
$ make
        

If you also run make install, the makefile will copy the grumpalot executable into the bin directory of whatever prefix you pass in to the configure script. The default prefix is /usr/local, but you probably don't want to in- stall this experimental grumpalot project into that directory, especially if you don't have root privileges on the system you're working on. You can set things up to install grumpalot into your home directory with the following command (substituting the path to your own home directory):

$ ./configure --prefix=/home/jsheets/wga
        

This command will place the grumpalot executable file in the /home/jsheets/wga/bin directory, any libraries in /home/jsheets/wga/lib, and any include files in /home/jsheets/wga/include. When you're done experimenting, you can run make uninstall or rm -rf /home/jsheets/wga.

Listing 3.15 shows the Makefile.am file for case 2, in which we want to link the two grump source files into a library, then statically link that library into grumpalot. Although this may at first seem a little silly-why don't we just compile them into the executable like we did in case 1?-you may someday have to do this in your own project. If you have a lot of files in your project, you might want to divide some of them up into separate directories to keep things organized and reduce the clutter.

Listing 3.15 Makefile.am for Case 2 of GNOME Grump Example

INCLUDES = $(GNOME_INCLUDEDIR)

bin_PROGRAMS = grumpalot

grumpalot_SOURCES = main.c
grumpalot_LDADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS) \
  libGrump.la

noinst_LTLIBRARIES = libGrump.la

libGrump_la_SOURCES = grump.c grump_more.c
libGrump_la_LIBADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS)
        

Unfortunately, GNU make, and thus automake, has a limitation that prevents us from targeting source files outside the current directory. If we were to move grump.c and grump_more.c to a subdirectory, we would have to create an additional Makefile.am file for that subdirectory and then compile those files into a library. Then we could pull that temporary helper library into our main executable by adding it to the grump_LDADD variable in our main Makefile.am file. This is exactly what we'll do in case 2, except without the subdirectory. Our grump_LDADD line will look like this:

grump_LDADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS) \
  libGrump.la
        

We name libGrump.la explicitly, without the implicit -l flag, so that the linker will not mistakenly try to link against libGrump.so. We don't have to do anything else to set up the static link. The linker will link libGrump.la into our binary file. Our case 2 grumpalot program won't require any external libraries. automake knows this because we told it to use the noinst target for the library; consequently, the generated makefile will not bother generating any dynamic .so libraries.

With case 3, we create a dynamic library rather than a static one. This brings up a handful of new concerns. We now have to install the library, since the executable loads it during runtime. We also have to install any header files that define the library's interface. If we didn't install the headers, other people would be unable to use our library because they wouldn't know which functions it contained; more to the point, the compiler needs the header file to satisfy the #include "grump.h" statement in main.c (or it would if the grump library were distributed separately from the main executable). Finally, we should set the library version number as we discussed in Section 3.4.5 to make sure the loader grabs the right library. This is a simple matter of adding the -version-info option to libGrump_la_LDFLAGS. See Listing 3.16 for the case 3 Makefile.am file.

Listing 3.16 Makefile.am for Case 3 of GNOME Grump Example

INCLUDES = $(GNOME_INCLUDEDIR)

bin_PROGRAMS = grumpalot

grumpalot_SOURCES = main.c
grumpalot_LDADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS) \
  -lGrump -L$(top_builddir)/.libs

lib_LTLIBRARIES = libGrump.la

libGrump_la_SOURCES = grump.c grump_more.c
libGrump_la_LDFLAGS = -version-info 0:0:0
libGrump_la_LIBADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS)

libgrumpdir = $(includedir)
libgrump_HEADERS = grump.h
        

First let's figure out how to make our library shared rather than static. As it turns out, the main thing we have to do is change our noinst_LTLIBRARIES automake variable to lib_LTLIBRARIES. This change tells automake that we want our package's libraries installed in $(libdir). Since we are installing libraries, libtool takes the liberty of generating dynamic .so libraries in addition to the static .a and .la libraries it generated in case 2. Furthermore, it puts these new .so files in the .libs subdirectory rather than in the same directory as the grumpalot executable. For this reason we must add an -L option to grump_LDADD:

grump_LDADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS) \
  -lGrump -L$(top_builddir)/.libs
        

We provide this as a relative path off of $(top_builddir) so that make can find the .libs directory even if administrators perform their builds in directories outside of the source tree. This makes it possible to build more than one set of executables with different configure options, simultaneously, off of the same source tree. If you use relative paths inside your source tree, or mistakenly use $(top_srcdir), to refer to your object and library files, you will break this functionality. The linker will end up looking for your object files inside the source tree rather than inside your build tree, where they will reside.

To see what it's like to build a package outside its source tree, you can create a build directory outside the source tree, cd into it, and invoke the configure script from that directory. configure will create Makefile files inside the build directory (the Makefile.am and Makefile.in files will still land in the source tree). When you run the resulting Makefile, it will create all the object files, libraries, and executables inside the current directory path, in the build directory tree. To see how this works, run all the commands up to and including autoconf inside the source tree, just as you always have. Then type in the following commands (assuming that the myapp subdirectory is the root of your source tree):

$ cd ..
$ mkdir build
$ cd build
$ ../myapp/configure
$ make
$ ./grumpalot
        

The dynamically linked grumpalot executable should now reside inside the build directory. If you wanted to test a static build of grumpalot without pulverizing the current dynamic build, you could create a new directory- buildstatic, for example-and then invoke configure from there, with the --disable-shared option. Presto! You now have two copies of grumpalot on your system-one that dynamically links to libGrump.so, and one that is statically linked to libGrump.a-and both of them arise from the same Makefile.am.

If you're following along diligently and trying each example at the keyboard as you go, you probably came across a problem in trying to run the dynamically linked case 3 grumpalot executable. Chances are, you got an error that looks something like this:

$ ./grumpalot
./grumpalot: error in loading shared libraries
libGrump.so.0: cannot open shared object file: 
No such file or directory
        

The problem is that libGrump.so.0 is tucked away in the .libs directory, where your system's loader can't find it. Rather than permanently adding temporary .libs directories to your loader's search path for each project you're testing, you can set the LD_LIBRARY_PATH environment variable to a colon-separated list of extra library paths. To run our case 3 grumpalot executable in the bash shell, for example, you can invoke it like this, in the same directory as grumpalot:

$ LD_LIBRARY_PATH=.libs ./grumpalot
        

This is only a problem if you try running grumpalot from inside the build tree, prior to installing it. If you've already installed grump with the make install target, things should work fine, as long as you installed it into a path that your system's dynamic link loader knows.

Our final concern for case 3 is how to install grump.h into the include directory. We can do this in two lines in our Makefile.am file:

libgrumpdir = $(includedir)
libgrump_HEADERS = grump.h
        

The first line creates the arbitrarily named $(libgrumpdir) variable, and sets it to point to $(includedir). This will install all files listed in $(libgrump_HEADERS) into $(includedir). This is good enough for our purposes in case 3. In some projects, however, you may not want all your include files to go into the same directory. You can use a similar approach to place header files into subdirectories of $(includedir), or anywhere else for that matter. For example, if our grump source tree had header files in the subdirectories grump, grump/net, and grump/gui, we could mirror this layout on the target system by adding something like this to our Makefile.am file:

libgrumpdir = $(includedir)/grump
libgrump_HEADERS = grump.h
libgrumpnetdir = $(includedir)/grump/net
libgrumpnet_HEADERS = grump-net.h
libgrumpguidir = $(includedir)/grump/gui
libgrumpgui_HEADERS = grump-gui.h
        

automake would see to it that the corresponding directories were created and that the include files ended up in the proper install location.

Our final case is a combination of cases 2 and 3, in which we have both static and dynamic libaries in the same project. The case 4 Makefile.am file has the same structure as the one in case 3, except for a few additions (see Listing 3.17). Most notably, we must add a noinst_LTLIBRARIES target to build a new static library from the grump_more.c source file. We'll call this new library libGrumpMore.la. The _SOURCES and _LIBADD primaries for libGrumpMore.la are almost identical to the ones we used for the static library in case 2, except that we are linking only one source file into it.

Listing 3.17 Makefile.am for Case 4 of GNOME Grump Example

INCLUDES = $(GNOME_INCLUDEDIR)

bin_PROGRAMS = grumpalot

grumpalot_SOURCES = main.c
grumpalot_LDADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS) \
  -lGrump -L$(top_builddir)/.libs

noinst_LTLIBRARIES = libGrumpMore.la
lib_LTLIBRARIES = libGrump.la

libGrump_la_DEPENDENCIES = libGrumpMore.la
libGrump_la_SOURCES = grump.c
libGrump_la_LDFLAGS = -version-info 0:0:0
libGrump_la_LIBADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS) \
  libGrumpMore.la

libGrumpMore_la_SOURCES = grump_more.c
libGrumpMore_la_LIBADD = $(GNOME_LIBDIR) $(GNOMEUI_LIBS)

libgrumpdir = $(includedir)
libgrump_HEADERS = grump.h
        

The library targets will need a little work to convert them from case 3 to case 4, but not much. First we remove grump_more.c from libGrump_la_SOURCES, since it now resides in the libGrumpMore.la library. Next we add libGrumpMore.la to libGrump_la_LIBADD, just as we added the static libGrump.la to grump_LDADD in case 2. As a result, the linker will statically link libGrumpMore.la into libGrump.la so that we end up with only one distributable library.

Our final tweak addresses a timing issue with the order of compilation. We must make sure that our makefile creates libGrumpMore.la before it tries to create libGrump.la. Simply adding libGrumpMore.la to the libGrump.la link line is not enough to generate a dependency check. We must state the dependency explicitly, or we will have no guarantee that libGrumpMore.la will exist when libGrump.la needs it. We can do this with automake's _DEPENDENCIES primary:

libGrump_la_DEPENDENCIES = libGrumpMore.la
        

The _DEPENDENCIES primary simply adds its contents to the list of dependencies for the libGrump.la target in the makefile, forcing make to build our libGrumpMore.la file first.