The GNU Build System (1/2)

Introduction

The GNU build system, also known as the Autotools, is the build system developed by the GNU project [1]. The tools included in the GNU build system are Autoconf, Automake, and Libtool. The goal of these tools is to make it easier to develop cross-platform software. They also make it easier for end users to build the software from source and install it.

Installing software from the source

When a Linux software package is distributed from the sources, the person installing the package is usually told to excute the following commands:

$ ./configure
$ make
$ make install

Having a standardized way to install software from the sources makes it much easier. The GNU build system is designed to help developers generate the files needed for this kind of installation.

The user can pass in arguments to the configure script to customize the installation. One of these arguments is --prefix that allows the user to specify the destination directory. By default the destination directory is /usr/local/.

The tools of the GNU build system

The following tools are part of the GNU build system (this is not an exhaustive list).

These tools have documentation available from the GNU website. See GNU Autoconf [2] and GNU Automake [3].

The examples in this tutorial are based on aclocal 1.9.6, autoconf 2.59 and automake 1.9.6. To get the version of the tools and at the same time check that they are installed you can do aclocal --version, autoconf -V and automake --version. The examples were executed on CentOS 5 but they should run on any Linux distribution.

A simple example

To describe how to use the tools let's start with the traditional "hello world" example. The files for this example can be downloaded from the Downloads page. Here are the contents of our Main.cpp file. For this example we aren't really interested in what this code does. It only matters that this is a C++ file and somehow we need to create an executable from it.

#include <stdio.h>

int main(int argc, char** argv)
{
   printf("Hello World!\n");
   return 0;
}

Let's add the needed build files to this simple example. To keep it simple we will put all the files in the same directory as Main.cpp. There are quite a few files to add but they are all small so it shouldn't take long. Here is the list of files to add.

  1. Makefile.am
  2. configure.in
  3. AUTHORS
  4. COPYING
  5. NEWS
  6. README
  7. ChangeLog
We discuss each of these files in the next sections.

Makefile.am

The first file we will add is Makefile.am. This file is used to describe how to build the binaries from the source files. In summary it replaces the traditional makefile by a much simpler syntax. Here are the contents of our Makefile.am file.

bin_PROGRAMS = helloworld
helloworld_SOURCES = Main.cpp

The syntax is easy to use but it needs some explanation.

The bin_PROGRAMS variable specifies that we want a program called helloworld to be built and installed in the /usr/local/bin/ directory when make install is run. You notice that this variable is made of two parts separated by an underscore. The PROGRAMS part indicates that this is an executable. The bin part indicates that this program should be installed in the /usr/local/bin/ directory.

Although we said that the program would be installed in /usr/local/bin/ this is not entirely true as this depends on the options passed into the configure script. /usr/local/bin/ is the default but if the --prefix option is used then that value replaces the /usr/local/ part. For instance if configure --prefix=my_dir is used then the program will be installed in /my_dir/bin. So the correct statement is that bin indicates that the program should be installed in <prefix>/bin.

The helloworld_SOURCES variable specifies the source files used to build the helloworld target. For each target there must be a statement of the form <target>_SOURCES = <file1> <file2> ...

configure.in

Now we add the configure.in file. As the name implies this is used to configure the build. For instance it will find the compiler to use.

AC_INIT(Main.cpp)

PACKAGE=helloworld
VERSION=1.0.0

AM_INIT_AUTOMAKE($PACKAGE, $VERSION)

AC_PROG_CXX

AC_OUTPUT(Makefile)

The AC_INIT macro performs essential initialization for the generated configure script. Every configure.in script should start with this statement. It takes as an argument a filename from the source directory to ensure that the source directory has been specified correctly.

The PACKAGE and VERSION variables declare the name and version of the package.

The AM_INIT_AUTOMAKE macro performs the initialization required by Automake.

AC_PROG_CXX is needed because our program is written in C++. This statement will find the C++ compiler on the platform and initializes the build variables to use this compiler to compile the C++ source files. There are other such variables for other compilers or tools like for instance AC_PROG_F77 that is used with Fortran 77 sources.

AC_OUTPUT create the Makefiles.

AUTHORS

This file is part of the documentation required by the GNU coding standards. The contents of this file have no impact on the build process (it can even be empty) but the tools expect the file to be present. This file lists the authors of the package.

Xavier Leclercq <xavier.leclercq@needfulsoftware.com> Developer

COPYING

This is also part of the documentation required by the GNU coding standards. The contents of this file have no impact on the build process (it can even be empty) but the tools expect the file to be present. This file contains the licence for the package. For a lot of GNU packages this would be the GPL [5] but as our package is a simple example we use the more permissive ISC licence [6].

Copyright (c) 2007, Xavier Leclercq <xavier.leclercq@needfulsoftware.com>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.


THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

NEWS

Another documentation file required by the GNU coding standards. The contents of this file have no impact on the build process (it can even be empty) but the tools expect the file to be present. As the name indicates this file contains news/historical information about the package.

First version of HelloWorld (16/10/2007)
----------------------------------------
First version of example that shows basic use of the GNU build system.

README

Another documentation file required by the GNU coding standards. The contents of this file have no impact on the build process (it can even be empty) but the tools expect the file to be present. This file contains general information about the package or otherwise useful information for people who want to use this package.

This is an example that shows how to use the GNU build system. The homepage
for the tutorial is located at http://www.needfulsoftware.com/ReferenceMaterial/
DevelopmentTools/Build/GNUBuildSystem/GNUBuildSystem.php.

ChangeLog

This is the last file required by the GNU coding standards. The contents of this file have no impact on the build process (it can even be empty) but the tools expect the file to be present. It contains the release notes for the package.

2007-10-16 Xavier Leclercq <xavier.leclercq@needfulsoftware.com>

     * HelloWorld 1.0.0 released.

Building the example

Now that all files are in place we can run the tools to build the component. Execute the following commands.

$ aclocal
$ autcoconf
$ automake --add-missing

aclocal copies some needed things in the current directory. After running this you should have a new file called aclocal.m4 and a new directory called autom4te.cache in the current directory.

autoconf creates the configure script.

automake --add-missing generates the links : depcomp, INSTALL, install-sh and missing.

After doing this you now have everything necessary to build and install the package. At this point you could package up the directory in a tarball and distribute it. The package can be built and installed by executing the usual commands:

$ ./configure
$ make
$ make install

This should build helloworld and put it in /usr/local/bin/. Of course you need permission to write in this directory. If you don't you can always specify another directory with configure's --prefix option.

Building and using a static library

We are now going to show how to create a static library and link against it. The example can be downloaded from the Downloads page. It can be found in the Basic/StaticLibrary folder.

This example is a modification of the previous "hello world" example. We split the program in two parts : HelloWorldClient and HelloWorldLib. As the names imply HelloWorldLib is a library used by HelloWorldClient to display the "hello world" string.

As in the previous example the AUTHORS, COPYING, NEWS, README and ChangeLog files are needed but the contents are similar so we won't dicuss these files anymore.

Building the static library

We have two source files for HelloWorldLib : HelloWorld.h and HelloWorld.cpp. They are reproduced below.

#ifndef _HELLOWORLD_H_
#define _HELLOWORLD_H_

void PrintHelloWorld();

#endif

#include <stdio.h>

void PrintHelloWorld()
{
   printf("Hello World!\n");
}

configure.in needs minor updates since we changed the name of the binary and source files. The only important difference is that we add the AC_PROG_RANLIB macro needed to configure the tools needed to build a static library.

AC_INIT(HelloWorld.cpp)

PACKAGE=helloworldlib
VERSION=1.0.0

AM_INIT_AUTOMAKE($PACKAGE, $VERSION)

AC_PROG_CXX
AC_PROG_RANLIB

AC_OUTPUT(Makefile)

And here are the contents of Makefile.am.

lib_LIBRARIES = libhelloworld.a
libhelloworld_a_SOURCES = HelloWorld.cpp
include_HEADERS = HelloWorld.h

lib_LIBRARIES is similar to bin_PROGRAMS but this time it means that we will build a library called libhelloworld.a and put it in <prefix>/lib.

As always the source files are specified by a macro of the form <target>_SOURCES = <file1> <file2> .... The only detail that needs explaining though is the replacement of the dot by an underscore. This is because target names are normalized when they become part of a macro name. This is simply because not all characters can be part of macro name. For this example you just need to remember that all dots become underscores and thus we have libhelloworld_a_SOURCES and not libhelloworld.a_SOURCES. The exact details of the normalization process can be found in [3] (search for Canonicalization).

The last statement is used to export the header files needed to use our library. This is again a macro made of two parts. The HEADERS part indicates that these are headers that we want to copy to some directory. The include part indicates that the target directory is <prefix>/include.

We can now create the package using the same commands as previously.

$ aclocal
$ autcoconf
$ automake --add-missing

The library can now be built and installed using the traditional commands.

$ ./configure
$ make
$ make install

After running these commands the following files should be present : /usr/local/lib/libhelloworld.a and /usr/local/include/HelloWorld.h.

Building the program that uses the static library

Now that our library is ready to be used. We'll write a small program that uses it. Here is the source for our little program. As you see we include the header from our library and use the PrintHelloWorld function.

#include <HelloWorld.h>

int main(int argc, char** argv)
{
   PrintHelloWorld();
   return 0;
}

The configure.in is almost identical to the original "hello world" example. We just changed the package name.

AC_INIT(Main.cpp)

PACKAGE=helloworldclient
VERSION=1.0.0

AM_INIT_AUTOMAKE($PACKAGE, $VERSION)

AC_PROG_CXX

AC_OUTPUT(Makefile)

And here is the Makefile.am.

bin_PROGRAMS = helloworldclient
helloworldclient_SOURCES = Main.cpp
helloworldclient_LDADD = /usr/local/lib/libhelloworld.a

As you see we added a line to indicate the dependency on libhelloworld.a. We used the macro <target>_LDADD = <lib1> <lib2> ... to indicate that these additional libraries only apply for the helloworlclient target. It is possible to specify additional libraries for all targets in the Makefile.am by using the LDADD (without the <target>_ prefix). In our example since there is only one target it doesn't matter which form is used.

We can now execute the usual commands to build helloworldclient.

$ aclocal
$ autcoconf
$ automake --add-missing
$ ./configure
$ make
$ make install

After this you should have the file /usr/local/bin/helloworldclient and executing it displays "Hello World!" on the standard output.

Additional resources

There are quite a few other pages explaining how to use the GNU build system. As mentioned at the start of this tutorial there is the GNU documentation itself but it can be hard to understand if you don't have any knowledge of the tools. The inti project has a nice and simple tutorial [4].

Bibliography

  1. The GNU project
  2. GNU Autoconf - Creating Automatic Configuration Scripts
  3. GNU Automake
  4. Inti Tutorial: Building a GNU Autotools Project
  5. GNU General Public License
  6. ISC licence
  7. An Introduction to GCC - Link order of libraries

blog comments powered by Disqus

Copyright(c) 2006-2017 Xavier Leclercq | Privacy policy

Home
Contact Us
Search