Thursday, November 22, 2007

Multi-file projects and the GNU Make utility

C-Scene Issue #2
Multi-file projects and the GNU Make utility
Author: George Foot
Occupation: Student at Merton College, Oxford University, England
IRC nick: gfoot

Disclaimer: The author accepts no liability whatsoever for any
damage this may cause to anything, real, abstract or
virtual, that you may or may not own. Any damage
caused is your responsibility, not mine.

Ownership: The section `Multi-file projects' remains the
property of the author, and is copyright (c) George
Foot May-July 1997. The remaining sections are the
property of CScene and are copyright (c) 1997 by
CScene, all rights reserved. Distribution of this
article, in whole or in part, is subject to the same
conditions as any other CScene article.

0) Introduction

This article will explain firstly why, when and how to split
your C source code between several files sensibly, and it will
then go on to show you how the GNU Make utility can handle all
your compilation and linking automatically. Users of other make
utilities may still find the information useful, but it may
require some adaptation to work on other utilities. If in doubt,
try it out, but check the manual first.

1) Multi-file projects

1.1 Why use them?

Firstly, then, why are multi-file projects a good thing?
They appear to complicate things no end, requiring header
files, extern declarations, and meaning you need to search
through more files to find the function you're looking

In fact, though, there are strong reasons to split up
projects. When you modify a line of your code, the
compiler has to recompile everything to create a new
executable. However, if your project is in several files
and you modify one of them, the object files for the other
source files are already on disk, so there's no point in
recompiling them. All you need to do is recompile the file
that was changed, and relink the object files. In a large
project this can mean the difference between a lengthy
(several minutes to several hours) rebuild and a ten or
twenty second adjustment.

With a little organisation, splitting a project between
files can make it much easier to find the piece of code
you are looking for. It's simple - you split the code
between the files based upon what the code does. Then if
you're looking for a routine you know exactly where to
find it.

It is much better to create a library from many object
files than from a single object file. Whether or not this
is a real advantage depends what system you're using, but
when gcc/ld links a library into a program at link time it
tries not to link in unused code. It can only exclude
entire object files from the library at a time, though, so
if you reference any symbols from a particular object file
of a library the whole object file must be linked in. If
the library is very segmented, the resulting executables
can be much smaller than they would be if the library
consisted of a single object file.

Also, since your program is very modular with the minimum
amount of sharing between files there are many other
benefits -- bugs are easier to track down, modules can
often be reused in another project, and last but not
least, other people will find it much easier to understand
what your code is doing.

1.2 When to split up your projects

It is obvisouly not sensible to split up *everything*;
small programs like `Hello World' can't really be split
anyway since there's nothing to split. Splitting up small
throwaway test programs is pretty pointless too. In
general, though, I split things whenever doing so seems to
improve the layout, development and readability of the
program. This is in fact true most of the time.

The decision about what to split and how is of course
yours; I can only make general suggestions here, which you
may or may not choose to follow.

If you are developing a fairly large project, you should
think before you start how you are going to implement it,
and create several (appropriately named) files initially
to hold your code. Of course, don't hesitate to create new
files later in development, but if you do then you are
changing your mind and should perhaps think about whether
some other structural changes would be appropriate.

For medium-sized projects, you can use the above technique
of course, or you might be able to just start typing, and
split the file up later when it is getting hard to manage.
In my experience, though, it is a great deal simpler to
start off with a scheme in mind and stick to it or adapt
it as the program's needs change during development.

1.3 How to split up projects

Again, this is strictly my opinion; you may (probably
will?) prefer to lay things out differently. This is
touching on the controversial topic of coding style; what
I present here is simply my personal preference (along
with reasons for each of these guidelines):

i) Don't make header files which span several source
files (exception: library header files). It's much
easier to track and usually more efficient if each
header file only declares symbols from one source
file. Otherwise, changing the structure of one
source file (and its header file) may cause more
files to be rebuilt that is really necessary.

ii) Where appropriate, do use more than one header file
for a source file. It is often useful to seperate
function prototypes, type definitions, etc, from the
C source file into a header file even when they are
not publicly available. Making one header file for
public symbols and one for private symbols means
that if you change the internals of the file you can
recompile it without having to recompile other files
that use the public header file.

iii) Don't duplicate information in several header files.
If you need to, #include one in the other, but don't
write out the same header information twice. The
reason for this is that if you change the
information in the future you will only need to
change it once, rather than hunting for duplicates
which would also need modifying.

iv) Make each source file #include all the header files
which declare information in the source file. Doing
this means that the compiler is more likely to pick
out mistakes, where you have declared something
differently in the header file to what it is in the
source file.

1.4 Notes on common errors

a) Identifier clashes between source files: In C,
variables and functions are by default public, so that
any C source file may refer to global variables and
functions from another C source file. This is true even
if the file in question does not have a declaration or
prototype for the variable or function. You must,
therefore, ensure that the same symbol name is not used
in two different files. If you don't do this you will
get linker errors and possibly warnings during

One way of doing this is to prefix public symbols with
some string which depends on the source file they appear
in. For example, all the routines in gfx.c might begin
with the prefix `gfx_'. If you are careful with the way
you split up your program, use sensible function names,
and don't go overboard with global variables, this
shouldn't be a problem anyway.

To prevent a symbol from being visible from outside the
source file it is defined in, prefix its definition
with the keyword `static'. This is useful for small
functions which are used internally by a file, and
won't be needed by any other file.

b) Multiply defined symbols (again): A header file is
literally substituted into your C code in place of the
#include statement. Consequently, if the header file is
#included in more than one source file all the
definitions in the header file will occur in both
source files. This causes them to be defined more than
once, which gives a linker error (see above).

Solution: don't define variables in header files. You
only want to declare them in the header file, and
define them (once only) in the appropriate C source
file, which should #include the header file of course
for type checking. The distinction between a
declaration and a definition is easy to miss for
beginners; a declaration tells the compiler that the
named symbol should exist and should have the specified
type, but it does not cause the compiler to allocate
storage space for it, while a definition does allocate
the space. To make a declaration rather than a
definition, put the keyword `extern' before the

So, if we have an integer called `counter' which we
want to be publicly available, we would define it in a
source file (one only) as `int counter;' at top level,
and declare it in a header file as `extern int

Function prototypes are implicitly extern, so they do
not create this problem.

c) Redefinitions, redeclarations, conflicting types:
Consider what happens if a C source file #includes both
a.h and b.h, and also a.h #includes b.h (which is
perfectly sensible; b.h might define some types that
a.h needs). Now, the C source file #includes b.h twice.
So every #define in b.h occurs twice, every declaration
occurs twice (not actually a problem), every typedef
occurs twice, etc. In theory, since they are exact
duplicates it shouldn't matter, but in practice it is
not valid C and you will probably get compiler errors
or at least warnings.

The solution to this problem is to ensure that the body
of each header file is included only once per source
file. This is generally achieved using preprocessor
directives. We will #define a macro for each header
file, as we enter the header file, and only use the
body of the file if the macro is not already defined.
In practice it is as simple as putting this at the
start of each header file:

#ifndef FILENAME_H
#define FILENAME_H

and then putting this at the end of it:

Using C/C++ libraries with Automake and Autoconf

Using C/C++ libraries with Automake and Autoconf
* Introduction.
* Example Files
* Recommended Reading
If you have read Using Automake and Autoconf with C++ then you
already know how to use automake and autoconf to build your C/C
++ programs. This document will show you what you need to add to
those and files to link your code with
a shared library.

I have included an example, which links to the examplelib
library used in Building C/C++ libraries with Automake and

The Makefile needs 2 pieces of information - how to find the
library's header files and how to link to the library itself.
These are traditionally stored in variables ending in CFLAGS
(for the headers' include argument) and LIBS (for the linker
argument). For instance, GTKMM_CFLAGS and GTKMM_LIBS. These
variables will be set in the file.
Your script should find the library and set the
CFLAGS and LIBS variables:

Libraries which have installed a .pc pkg-config file
Recently, libraries have started to use pkg-config to
provide includes and linker information, instead of the
methods described below. In this case, you should use
the PKG_CHECK_MODULES() macro in your file.
For instance:

PKG_CHECK_MODULES(DEPS, gtkmm-2.0 >= 1.3.3 somethingelse-1.0 >= 1.0.2)

DEPS_CFLAGS and DEPS_LIBS will then include the include
and linker options for the library and all of its
dependencies, for you to use in your file. I
have used the DEPS prefix to mean 'dependencies', but
you can use any prefix that you like. Notice that you
can get information about several libraries at once,
putting all of the information into one set of _CFLAGS
and _LIBS variables. You can also use more than one
PKG_CHECK_MODULES() line to put information about
different sets of libraries in separate _CFLAGS and
_LIBS variables.

Of course you must ensure that you have installed

Libraries which have installed an AM_PATH_* macro
Some libraries, such as gtkmm 1.2, install an m4 macro
which makes your life slightly easier. You can call the
macro from your It will set *_CFLAGS and
*_LIBS variables which you can use in your
files. For instance:

AM_PATH_GTKMM(1.2.2,,AC_MSG_ERROR([Cannot find correct gtkmm version]))

This macro will call the gtkmm-config script and sets
the GTKMM_CFLAGS and GTKMM_LIBS variables with the data
that it returns. It will also report the library version
found and complain if the library is not installed, or
if it is the wrong version.

When you call aclocal the macro will be copied to the
aclocal.m4 file in your project's directory. If you did
not install the library at the same prefix (e.g. /usr
or /usr/local) as the aclocal tool, then you will need
to call aclocal with the -I argument. For instance:

# aclocal -I /home/myinstalls/share/aclocal
Libraries which have installed a *-config script
Some libraries do not install an AM_PATH_* m4 macro, but
they do install a *-config script. In this situation you
need to call the script and set the variables in your
own code.

For instance,

# GNOME--:
# (These macros are in the 'macros' directory,
# copied from the gnome-libs distribution.)
# GNOME_INIT sets the GNOME_CONFIG variable, among other things:

# GNOME-CONFIG script knows about gnomemm:
# ('gnome-config' is installed by GNOME)
# So call gnome-config with some arguments:

Libraries with no macro and no script
There are still many libraries which do not use *-config
scripts or macros to make your life easier. The best
thing to do in this situation is to allow the user to
tell the configure script where to find the library. You
can do this with the AM_ARG_WITH() macro. This adds a
command line argument to the configure script and
complains if it isn't used.

For instance:

# Ask user for path to libmysqlclient stuff:.
[ --with-mysql=<path> prefix of MySQL installation. e.g. /usr/local or /usr],
AC_MSG_ERROR([You must call configure with the --with-mysql option.
This tells configure where to find the MySql C library and headers.
e.g. --with-mysql=/usr/local or --with-mysql=/usr])
MYSQL_LIBS="-L${MYSQL_PREFIX}/lib/mysql -lmysqlclient"
The CFLAGS and LIBS variables are used in your

For programs
If you are using the library in a program, then you
should do something like the following.

For libraries
If you are using the library from another library, then
you should do something like the following. This will
not actually link with a shared library - it will just
tells your library that it needs to link with the other
library at run time.

libsomething_la_LIBADD = $(DEPS_LIBS)
Example Files
You may download this example which demonstrates how to link an
executable to the example library used in Building C/C++
libraries with Automake and Autoconf.

Recommended Reading
* Building C/C++ libraries with Automake and Autoconf
* GNU's automake, autoconf, and libtool manuals

Building C/C++ libraries with Automake and Autoconf

Building C/C++ libraries with Automake and Autoconf
* Introduction.
* libtool
* Directory structure
* Installing headers
* Version numbers
* Making your library easy to use
* C++ namespaces
* Example Files
* Recommended Reading
If you have read Using Automake and Autoconf with C++ then you
should already know how to use automake and autoconf to build
your C++ programs. This document will show you how to use the
same tools to build a reusable library. I have included an
example which demonstrates these ideas.

You may also wish to read Using C/C++ libraries with automake
and autoconf to see what users of your library will expect.

Why use libtool?
Each platform has its own way of implementing the shared
(or 'dynamic') library idea, and there are various tools
needed to build these libraries. Libtool delegates to
these platform-specific tools and presents the developer
with a simpler set of options. Automake and autoconf can
use libtool to build libraries for many OS and
development environments using the same build files.

Libtool also makes it easy to build a static library or
a dynamic library from the same project.

When you start your project files you need to issue the
'libtoolize' command to add libtool support files to
your project.

You need to call AM_PROG_LIBTOOL in your

libtool variables
When building an executable you use something like this
in your

PROGRAMS = someapp
someapp_SOURCES =

To build a library you use the LTLIBRARIES set of
variables instead:

something_la_SOURCES = something.h
Parallel installs
Notice that the library is called, including the version number
in its name. This will allow the next version,
libsomething-2.0, to be installed alongside,
without preventing use of the previous version.

Directory structure
Don't use 'src'
When the library is installed, its headers will be
installed in their own directory in the 'include'
directory. Code that uses the library should #include
them like so:

#include <something/something.h>
#include <something/extrabits.h>

If you put your source files in a 'src' directory then
the #include lines in your own headers will not work
when they are installed, and the #includes in your
examples (in the 'examples' directory') will be
misleading. At best, they will include like so:

#include <something.h>
#include <extrabits.h>

I suggest that you put your sources in a directory that
has the same name as your library. Then the examples
inside your distribution and any external examples will
use the same path in their #include directives.

Sources in sub directories
In Using automake and autoconf with C++ I explained how
to build intermediate static libraries in each sub
directory. The idea is very similar when building a
library, but the syntax is slightly different.

* Libtool libraries have the .la suffix, instead
of .a
* We need to use _LIBADD instead of_ LDADD.

For instance

something_la_SOURCES =
something_la_LIBADD = sub/
This technique is demonstrated in the downloadable example.

Note that, at the time of writing, there are two
problems with libtool that you should be aware of:

* Libtool will not add libtool libraries
recursively. Therefore you need to list all of
the convenience libraries in one place. For
something_la_LIBADD = sub/
* Libtool will not differentiate between two
libraries with the same name in different
directories. Therefore you should probably
include the full path in the name of your
convenience libraries. For instance:
something_la_LIBADD = foo/
foo/sub/ goo/

Hopefully these problems will be fixed in the next
version of libtool. Please tell me when they have been
fixed, so that I can update this page.

Installing headers
When the user types 'make install' the library's header files
should be installed as well as the library itself. You can make
this happen by using these variables in your files:

library_include_HEADERS = something.h foo.h

This will put something.h and foo.h in
Users of the library would then #include your headers like so:

#include <something/something.h>
Parallel installs
Notice that the headers should be installed in a
version-specific directory. This will allow the next
version's headers to be installed alongside in
something-2.0, without preventing use of the previous
version's headers.

The generated config.h header should be installed in the
lib directory, because it is architecture-dependent.
Actually, I'd like a better explanation than that to put

For example, in your Makefile,am file:

something_configdir = $(libdir)/something-1.0/include
something_config_DATA = config.h
Version numbers
Your library should have two numbers - the 'release number' and
the 'version number'.

The release number uses a scheme of your own devising. Generally
it indicates how much functionality has been added since the
last version, and how many bugs were fixed.

The version number uses an established scheme to indicate what
type of changes happened to your library's interface. The
following diagram can be found in many files:

| | |
+------+ | +---+
| | |
| | |
| | +- increment if interfaces have been added
| | set to zero if interfaces have been removed
| | or changed
| +- increment if source code has changed
| set to zero if current is incremented
+- increment if interfaces have been added, removed or changed

Use this version number in your file:

libsomething_la_LDFLAGS= -version-info $(EXAMPLE_LIBRARY_VERSION) -release $(EXAMPLE_RELEASE)
Making your library easy to use
Experts can use your library if they are given just the headers
and the library, but you can make life much easier for people
who are using automake and autoconf. In my opinion, your library
will appear more complete, and will be used by more people if
you use pkg-config. This tool was created relatively recently to
improve upon the old method, described here. It allows you to
install details about your library, specifically the linker and
include options that should be used with it. Developers can add
a line to their files that reads this infomation
back, along with the options required for your library's

The file
Your library should install a .pc file, describing the
linker and include options for your library. But those
are dependent on the --prefix given to the configure
script, so you'll need to crate a file. For


Name: something
Description: Some library.
Requires: somethingelse-2.0 somethingmore-1.0
Version: @VERSION@
Libs: -L${libdir} -lsomething-1.0
Cflags: -I${includedir}/something-1.0 -I${libdir}/something-1.0/include

You'll need to mention this new .in file in your script, like so:

AC_OUTPUT( Makefile \
something/Makefile \
something/sub/Makefile \

And you'll need to mention it in your file,
so that it gets installed and distributed. For instance:

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = something-1.0.pc
Read Using C/C++ libraries with automake and autoconf to
see how this pkg-config file would be used.

Parallel Installs
The file should include the version number in its
name. For instance, This will allow
the next version of the library to install its own
something-2.0.pc file alongside, without preventing use
of the previous version.

C++ namespaces
If you are writing a C++ library, I strongly suggest that you
put all the classes in a namespace. For instance, in the header

namespace Something
class Example
} /* namespace Something */

And in the implementation file:

namespace Something

} /* namespace Something */

This will prevent name clashes and make it more obvious when
other code is using the library.

Example Files
You may download this example which demonstrates how to put all
these ideas together.

This example uses some 'generic' variables instead of repeating
the library name several times. This should make the project
files easier to maintain, and it is used to generate the
examplelib-config script automatically. Thanks to Cedric Gustin
for this idea.

The document Using C/C++ libraries with automake and autoconf
contains an example which links to this library.

Recommended Reading
* Using Automake and Autoconf with C++
* GNU's automake, autoconf, and libtool manuals

automake related

Using Automake and Autoconf with C++
* Introduction.
* make and configure
* automake and autoconf
* Sub Directories
* Example Files
* Recommended Reading
* Translations
The automake and autoconf tools can be used to manage C++
projects under Unix. They should save a lot of time compared to
make and configure, while ensuring that your project is
structured according to GNU standards.

However, it is difficult for beginners to get started.
Hopefully, this tutorial will provide enough information for C++
programmers who are new to Unix to create their first C++
projects, while gaining a superficial understanding of what the
tools are doing.

I am not an expert on automake and autoconf, so I welcome
constructive advice on this tutorial. If you find problems with
the examples, please try to provide patches.

make and configure
The make tool can be used to manage multi-file projects. make
uses the Makefile file in your project folder, which lists the
various compiling and linking steps, targets, and dependencies.
make is well explained in C-Scene: Multi-File Projects and the
GNU Make Utility.

A configure script can be used to aid cross-platform compiling.
A suitable configure script should interpret a file
and then create a platform-specific Makefile file. It will do
this after performing several tests to determine the
characteristics of the platform.

This allows a user to type './configure' and then 'make' to
compile a project on his platform.

automake and autoconf
Obviously most well-written Makefiles and configure scripts will
look very similar. In fact, GNU provide guidelines about what
should be in these files. Therefore, GNU created automake and
autoconf to simplify the process and ensure that the Makefile
and configure script conform to GNU standards.

Here is a brief explanation of how these tools are used. You can
see examples of the files used by these tools in the Examples
Files section.

Note: These tools use the m4 programming language. aclocal adds
aclocal.m4 to your project directory, which contains some m4
macros which are needed.

autoconf looks for a file called (or,
previously, It then creates the configure
script, based on the macros which it finds.

Whenever you add a macro to, you should run
aclocal as well as autoconf, because aclocal scans to find which macros it should provide.

Lines which every should have
Every should have lines like the







The AC_INIT macro can take any source file as an
argument. It just checks that the file is there,
which should, in turn, mean that the source
directory is there.

The AM_INIT_AUTOMAKE line adds several standard
checks. It takes the program name and version
number as arguments.

AC_PROG_CC indicates that the source code may be
in C. If the source code is C++ then we also

AC_PROG_INSTALL will generate an install target
so that users may just type 'make install' to
install the software.

AC_OUTPUT indicates the name of the Makefile
which will be generated.

Using a Config Header
The AM_CONFIG_HEADER(config.h) line indicates
that you will be using a config.h file. autoconf
will then need a file, which it
processes to create the config.h file. This is
#included by your source code and provides a way
for people to customise the configuration for
their platform, via #defines. can be
generated automatically with the autoheader

However, you need a stamp-h file in your project
to ensure that automake regenerates config.h
from Type 'touch stamp-h' to add
this file to your project.

automake looks for a file called It then
creates a, based on the macros which it
finds. This is later used by the configure script (see

GNU-style projects, or not
Because automake tries to make a GNU-style
project by default, it will add a COPYING file
and complain if some other necessary informative
text files are missing. You can add blank files
with the following command:


If you do not want these GNU-style files, then
you could add the following to your


Thanks to Marc van Woerkom for this suggestion.

Telling automake about your source files
Use lines like the following to name your
program and list its source files:

bin_PROGRAMS = hello

hello_SOURCES = hello.h

Note that the second variable is prefixed with
the value of the first variable. This is a
common practice with autoconf and automake.

The Whole Process
Assuming that you have written appropriate
and files (there are examples below), you
should be able to build your project by entering the
following commands:

* 'autoheader' - creates
* 'touch NEWS README AUTHORS ChangeLog'
* 'touch stamp-h'
* aclocal - adds aclocal.m4 to directory. Defines
some m4 macros used by the auto tools.
* 'autoconf '- creates configure from
* 'automake' - Creates from
* './configure' - creates Makefile from >
* 'make'

Just repeat the last 5 steps to completely rebuild the
project. Most projects have an script that
runs everything up to the configure step.

Sub Directories
Project files should, of course, be organised in sub folders.
Ideally, all of the source files should be in a folder called
'src' in the project folder, with all of the other files (such
as makefiles, configure scripts, and readmes) separate at the
top. Projects which have several levels of folders are called
'Deep' projects. I have listed the necessary steps here, but you
should look at the Example Files section to see them in context.

When using sub-directories, you need to do the following:

1. Add a SUBDIRS entry to the top-level For

SUBDIRS = doc intl po src tests

Note that the directory names are separated only by spaces.

2. Add a file to every sub directory. The
sub-directories do not need files. Be sure to add
the Makefiles to the list in the AC_OUPUT macro in the top-level

For sub directories containing additional source code
3. Add the AC_PROG_RANLIB macro to your
This will allow you to build code in sub-directories
into temporary libraries, which make will then link in
with the rest of the code.

4. Add some macros to the of any source
directory under src. These will build a non-installing
library. You need to give the library a name beginning
with 'lib', specify the sources, and specify the
locations of any header files. For example:

noinst_LIBRARIES = libfoo.a

libfoo_a_SOURCES = foo.h

INCLUDES = -I@top_srcdir@/src/includes

Notice that the SOURCES macro uses the library name with
an underscore instead of a dot. Also, notice the use of
the top_srcdir variable to refer to the top-level of the

5. Use a LDADD_ macro in the of a higher
directory to link the temporary library with any code
that uses it. For example,

LDADD = foofiles/libfoo.a
For sub directories containing non-source files
3. The in the sub-directory should contain a
line like the following:

EXTRA_DIST = somefile.txt someotherfile.html

This tells automake that you want the files to be
distributed, but that they do not need to be compiled.

Example Files
Here are some example and files. They
are sufficient to manage a C++ project which uses the Standard

See the automake and autoconf manuals for information on the
macros and variable names used in these files. I do not want to
make this seem more complicated by explaining each line of these

These examples are for a 'Deep' project with the following












AC_OUTPUT(Makefile src/Makefile src/foofiles/Makefile)
SUBDIRS = src for the src directory
bin_PROGRAMS = hello

hello_SOURCES = hello.h

SUBDIRS = foofiles

LDADD = foofiles/libfoo.a for foofiles directory under src
noinst_LIBRARIES = libfoo.a

libfoo_a_SOURCES = foo.h

INCLUDES = -I@top_srcdir@/

You may download a simple example project here:

Recommended Reading
* Building C/C++ libraries with automake and autoconf
* Using C/C++ libraries with automake and autoconf
* GNU Autoconf, Automake, and Libtool: New online book by the
* C-Scene: Multi-File Projects and the GNU Make Utility
* GNU's automake and autoconf manuals
* Learning autoconf and automake
* Learning the GNU Development tools
* Programming with GNU Software, Mike Loukides & Andy Oram

Blog Archive