GNU Autotool

From Dyle's Wiki

Jump to: navigation, search

Contents

Prerequisites

This page is not about C/C++ (or Java) development. You should be familiar with the programming language of your software already. Also while reading this page you should be firm -- well: very firm - oh my: very, very firm -- with makefiles. How make works, what it does (and why) is detailed at The GNU make.

Introduction

The GNU Autotools are the de facto standard way of software development within the Open Source community. Many successfull software packages like KDE are built using the GNU autotools. Although the GNU Autotools are widespread used, there is a lot of confusion to this issue due to the complexity they impose.

This site tries to introduce the GNU Autotools as soft as possible ... =)

As opposed to the normal, common, simple hello-world examples, a much more complex software development process is introduced.

The GNU Autotools

The GNU Autotools is a set of small apps working together to perform:

  • A full build of the software based on the pure source
  • Installations steps to make the compiled software accessable to the system/user
  • To create a package containing all what's necessary to distribute the software as a tarball (every seen those "superdooperapp-0.23.10.tar.gz" somewhere?)

All this (those small apps, except the compiler and the actual make command itself) is purely based on bash-scripts ... spelled: bash, not c-shell, korn-shell or whatsoever-shell. Nope. Again, read my lips slowly: BASH.

On the user side

The funny part.

The overall goal of the GNU Autotools is not convenience but portability. Therefore the system prerequisites for a having a tarball compiled and installed on the user side is tight:

  • bash
  • common binutils together with sed and awk
  • gzip and tar to extract the tarball

So, suppose you spotted some software, which you want to test. The software is "The Super Dooper App" and is presented not as an RPM (or DEB) but rather as the source tarball named superdooperapp-0.23.10.tar.gz. This is the common name syntax for GNU Autotools created software tarballs. The first part lists the name of the software (superdooperapp). After a dash the version is listened. Therefore this tarball contains version 0.23.10. There is no restriction on version numbers nor any common way to handle version numbers. So version numbers may look whatever they like: 0.23.10, 1.2, 3.4.0, 9999.0.0.0.1.000.1, ... Whatever they mean is left to the developer (but usually higher numbers indicate superior versions ...).

First you have to extract the tarball. Suppose you placed them into ~/tmp:

$ cd ~/tmp
$ ls
superdooperapp-0.23.10.tar.gz
$ gzip -dc superdooperapp-0.23.10.tar.gz | tar -xv

Sure, you can use any other extraction line than the one presented above ... be creative! E.g. use a $ tar -xzvf ...

./configure

Now you'll have a directory created named superdooperapp-0.23.10. It's time to start the build process by invoking ./configure.

$ cd superdooperapp-0.23.10
$ ./configure

Your terminal will be swept with a lot of checking statements.

checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
checking target system type... i686-pc-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
...

What's happening? Well, ./configure generates Makefiles for each project and subproject it knows, depending on the system configuration and abilities. It's this step which places a Makefile to be invoked later on. Any further step of the GNU Autotools depends solely on targets listened in those Makefiles.

./configure details

./configure fullfills 3 jobs:

  1. Check the system for any quirks (e.g. having the standard headers elsewhere as opposed to the common way).
  2. Check the system, that all the needed stuff is there to get the software installed. So, if the software depends on having KDE, MySQL and Doxygen installed, ./configure will look for those by performing several steps to "touch" these apps.
  3. Based on the gathered knowledge, ./configure creates the Makefiles.

All this is done by a very sophisticated interaction system of tiny functions written as bash-scripts.

As the install step later on also is done via make the destination to install the software is declared at this stage already. ./configure will create Makefiles which install the software into /usr/local. You can influence this behavior with the --prefix option of ./configure. So stating

$ ./configure --prefix="/usr"

will install the software into /usr. (Linux-)Distributions usually have their own flavor of the system's folder hierarchy. Therefore be cautious. Also, if you know, that superdooperapp is a KDE application (better known as kuperdooperapp) and you want to install it right into the KDE system a

$ ./configure --prefix="`kde-config --prefix`"

will do the job.

However, all this --prefix stuff might be ignored by the developers of the software completely (they are free to ignore it, although it's a "No-No") and place the binaries right into some /opt/superdooper.


./configure succeeds ...

If ./configure succeeds without an error message, all those Makefiles are generated and you'll likely start make. He-hoo.

./configure fails ...

./configure may fail. Then the whole build process stops. There is no "ignore and continue" option. It stops. Point.

This happens usually if software required is not installed. The final lines of the ./configure output should hint you the way to fix the problem. Although these lines are made up by developers and may be irritating and misleading as well. :-(

Example:

$ ./configure
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
...
checking for Qt ... failed!
Qt is not installed! Please install Qt and re-run configure.

./configure fails, because it couldn't find a Qt version installed. However, you may have Qt installed, but not the development version, which is needed by the software in the sample above. The developer who has written the check-script for that Qt thingie simply missed to tell you that. Sadly, one may find himself installing the very same software over and over again without any success because the message of the developer is not precisely enough and therefore misleading ...

BUT: you should have the knowledge (at least at the bottom of this page) how to check that: where to look for what the ./configure really wanted!

Performing the build

Now it's time to invoke make.

$ make

If any error occurs within this step, it's all up to the developers of the software (not the GNU Autotools, nor the user). If this one fails, the developers published a buggy tarball. All what is necessary to build the software has already been checked previously!

Install the software

make passed successfully? Good. Now become root and install the software:

$ su -m
Password:
# make install
...
# exit

That's it.

Yup, ... but take a guess: who is responsible for the correct installment commands? Right, the developers of the software. It's up to them to place the binaries, documentation, graphics, etc. into the right folders, registering some parts, modifying system files, ... Could this be errornous? Yup, too. However, most software gets properly installed (doesn't it?).

Uninstalling the software

Uninstalling the software is done calling ./configure using the same --prefix option as for the install step and then invoking make uninstall.

$ ./configure
...
$ su -m
Password:
# make uninstall
...
# exit

However, what's true for the install procedure is also true for the uninstall: it is totally up to the developers to correctly remove the software from the system. Therefore there is no guarantee that the software has been really totally removed ...

All this ain't a drawback compared to MS Windows platforms though it might look more error-prone. Since all install and uninstall statements are placed in Makefiles, which are readable by the use of some very ordinary text editor the user has the chance to know, what the software tried to do, what it did and what it should do. Along comes the possibility to change the Makefile to correct a buggy behavior by the user without calling any developer hot-line support station. However, this is sure a complex task and requires qualified people. On the other side, in Windows you just don't have to chance to know about these things. This does not indicate that all installs and uninstalls well ... quite the opposite. The GNU Autotools enables you to have the chance to know and correct it. That's an advantage.

On the developer side

The not-so-funny part.

Read on!

Directories, directories, everywhere!

Before we start, we have to take a more closer look at system's directory structure, because this is crucial when building and installing software. Unix (*BSD) and Unix-like (Linux-Distros) systems do follow -- more or less -- the FHS, the File Hierarchy Standard.

On Windows systems you actually have these folders:

  • "C:\Program Files" (or their lingual pendants) for holding every application software
  • "C:\WINDOWS" for holding the operating system files
  • "C:\Documents and Settings" for holding personal user files

There is no real recommendation on how each application should use these folders or how it should install its data. All is pretty laid out for free use ... and therefore is a pretty mess.

On Unix and Unix-like systems things are different.

/              root-folder (there is no such thing like "C:", "D:", ... you know that)
/bin           regularly basic programs to be used for everyone
/home          place to store personal data
/lib           prime (kernel) libraries and modules
/root          root's "home"
/sbin          basic programs which require privileged access
...
/usr           "common" files
/usr/bin       more sophisticated programs usable for everyone
/usr/doc       documentation of applications
/usr/include   include files holding declarations of types and methods in shared libraries
/usr/lib       place for shared libraries
/usr/sbin      more sophisticated programs useable only for root
/usr/share     holds data associated with an application (e.g. images, sound, ...)
...
/tmp           temporary files (can be usually safely destroyed)
/var           files created by an application without user relationship e.g. web-files, logs, database-backends, etc (can be usually NOT safely destroyed)
...

So if an application gets installed onto a Unix or Unix-like system you'll get:

  • the executable(s) placed in /usr/bin
  • any shared library in /usr/lib
  • header files stating how to access the libs in /usr/include
  • documentation, handbook, readme.txt, etc. in /usr/doc
  • any images, sounds, etc. in /usr/share

Most likely the application will install its executables directly into /usr/bin but will create some subfolders named after the application in all the others. To summarize for an application superdooper and the term /usr replaced with prefix:

prefix/bin                     The superdooper executables themselves.
prefix/doc/superdooper         All the documentation for the superdooper app.
prefix/include/superdooper     The header files for the libraries superdooper offers to the rest of the system to be used
prefix/lib                     The superdooper libraries
prefix/share/superdooper       Superdooper images, sound, etc.

Now the prefix part can be exchanged with any other folder e.g. /usr/local (usually your Linux-Distro-Vendor knows best ...) and this is also the variable ./configure reacts upon.

So when installing the application keep this idea of folders in mind, because it is up to you (as a developer) to write the files during the installation process to the correct locations as well as removing them from these locations during an uninstall.

When running the application you also have to keep these folders in mind which have a very dynamical aspect and will differ from time to time, from user to user, from distro to distro:

  • The current folder the application has been called of: the current working directory.
  • The home of the user ${HOME}.
  • The place where all additional resources have been installed: prefix/share/....

An application which is capable of reacting on all these dynamic folders and still fullfill its job accurate is called relocatable. The most tricky part hereof is figuring out where those shared stuff is around: getting the correct prefix/share folder since all the other should be pretty easy discovered. Now tools exist to enhance this detection: Guide to Making Relocatable Applications. Basically it all boils down in using the /proc pseudo-filesystem (on Linux) which has the ability to provide a process with its vital information. So if the process has been started by loading the /usr/local/bin/superdooper executable file, then the shared data is most likely in /usr/local/share to be found.

Of course there are other solutions to all this folder issue:

  • There is a folder called /opt which lets you install software in such a mess Windows is used to. This is the Linux-Version of C:\Program Files.
  • Huge packages may introduce their own sub-structures like KDE does.

However, keeping things as laid out before (FHS) does provide some order to the system and keeps things clear and precise (well, still more precise than the Windows way).

The project structure

When building software always the question arises on how to organize your files. Ok, things go pretty easy when doing a Hello-World-job. There is hello.cpp and Makefile. Point.

Regarding the fictional superdooper application things are different:

  1. We have not one but two executables: super and dooper
  2. There are static libraries created: abc, def and ghi. abc and def are used by super, def and ghi are used by dooper.
  3. Also there are two shared libraries: sharethis.so and sharethat.so
  4. There are tons of graphical data, e.g. for all that icons in the toolbar and menus, some of theme are unique to either super and dooper, some are really shared.
  5. We have a bunch of documentations in HTML and PDF format.

Puhhh ... and as fate strikes we have two development teams: one for super and one for dooper but on each team we have some of project members can't stand the other ... so development goes on two very separated paths ...

So these are the projects to be maintained:

  • super
  • dooper
  • abc
  • def
  • ghi
  • sharethis.so
  • sharethat.so
  • graphics
  • documentation

Each one has its own Makefile and dependencies. Graphics and Documentation are each one a single project although they contain data only relevant to one part of the whole application package (super or dooper).

Now a GNU idea: create two directories for projects:

  1. A source directory holding the source files and a description on how to compile them given the idea that every dependency has already been solved prior.
  2. Another directory holding files to be executed to solve each dependency.

When opening up GNU-projects you see this behavior by having some sort of project-root-folder, holding files like configure and other scripts and within that a folder usually called src for the plain source of an application. Now placing each sub-project into a VCS (a versioning control system like CVS or Subversion) you can arrange your project structure rather freely. Regarding the superdooper-applications above, we might have in a folder-system:

~/Sources
  +-- super             project-root for super
  .   +-- abc           abc library sources
  .   +-- def           def library sources
  .   +-- sharethis     sharethis sources
  .   +-- sharethat     sharethat sources
  .   +-- super         super sources
  .   +-- gfx           graphics
  .   +-- doc           documentation
  +-- dooper
  .   +-- def           def library sources
  .   +-- ghi           ghi library sources
  .   +-- sharethis     sharethis sources
  .   +-- sharethat     sharethat sources
  .   +-- dooper        dooper sources
  .   +-- gfx           graphics
  .   +-- doc           documentation
 

Obviously a lot of code is duplicated (e.g. def-library), but since one team works on the super and another on dooper they don't interfere and due to a VCS they are guaranteed to be at the same code basis. Moreover also those project-root folders are stored into the VCS omitting the child-folders. So one can freely generate an whole project combining each one:

~/Sources
  +-- superdooper       project-root for the total superdooperpackage
  .   +-- abc           abc library sources
  .   +-- def           def library sources
  .   +-- ghi           ghi library sources
  .   +-- sharethis     sharethis sources
  .   +-- sharethat     sharethat sources
  .   +-- super         super sources
  .   +-- dooper        dooper sources
  .   +-- gfx           graphics
  .   +-- doc           documentation
 

Ok, to summarize:

1. Generate a project-root folder. This folder directly contains scripts and other stuff to check dependencies, build order, etc. This is some sort of meta-project. This is were those configure script comes in as well as the configure.ac and Makefile.am.
2. Generate a sub-folder for each project containing the pure sources along with a description to build them (hence, the term description as opposed to Makefile).

The GNU tools

Now it is time to arm ourselves with the GNU tools. There are 3 prime tools:

  • autoconf Autoconf is used to generate those configure scripts and the final Makefile. Its input is configure.ac. It is used solely in the project-root folder.
  • automake Automake is used to generate a Makefile.in which is then taken as an input for configure to produce the final Makefile. Its input is Makefile.am. Those Makefile.am are situated in each folder holding some source to be compiled and/or installed.
  • libtool Libtool does some compiler and platform specific extensions necessary when building libraries. It also deals with the gcc when debugging applications linked with libraries.

There are others as well like autoheader, but they play some sort of inferior role (and talking about them also might blast your browser).

To get each final Makefile this is the path of input:

configure.ac (only in project-root) -----------> Makefile
                                                     ^
                                                     |
                                                     |
Makefile.am (in each project + project-root) --> Makefile.in

configure.ac

configure.ac is the file holding all the meta-information about the whole application-package. A sample file looks like this:

# ------------------------------------------------------
# configure.ac for superdooper

AC_PREREQ(2.59)
AC_INIT(superdooper, 0.23.10)

AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET

AM_INIT_AUTOMAKE

# ------------------------------------------------------
# prime files to be configured

AC_CONFIG_FILES([ \
    Makefile \
    abc/Makefile \
    def/Makefile \
    ghi/Makefile \
    sharethis/Makefile \
    sharethat/Makefile \
    super/Makefile \
    dooper/Makefile \
    gfx/Makefile \
    doc/Makefile \
])

# ------------------------------------------------------
# prime checks

# Checks for programs
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_PROG_LIBTOOL

# Check for header files.
AC_STDC_HEADERS

# ------------------------------------------------------
# additional lib checks here ...

# check Qt
gw_CHECK_QT

# ------------------------------------------------------
# final ...

AC_OUTPUT

if test "${all_tests}" = "bad"; then
  if test ! "${cache_file}" = "/dev/null"; then
    echo 
    echo Please remove the file ${cache_file} after changing your setup
    echo so that configure will find the changes next time.
    echo 
  fi
else
  echo 
  echo Good - your configure finished. Start make now.
  echo 
fi

This file is actually a bash-script. Well, most part become one later on.

Therefore every line empty or starting with a hash `#` is ignored.

AC_PREREQ(2.59)

This tells the GNU autoconf, which minimum version to use (of autoconf).

AC_INIT(superdooper, 0.23.10)

This line holds the information that we are talking about the superdooper application package, version 0.23.10.

AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET

These lines provide some basic checking performed later on by the final configure-script.

AM_INIT_AUTOMAKE

... gets up automake. configure will now use Makefile.am to build Makefile.in needed for Makefile ... ah, yes.

AC_CONFIG_FILES([ \
    Makefile \
    abc/Makefile \
    def/Makefile \
    ghi/Makefile \
    sharethis/Makefile \
    sharethat/Makefile \
    super/Makefile \
    dooper/Makefile \
    gfx/Makefile \
    doc/Makefile \
])

This line tells, what final files have to be "configured". These are all the Makefiles to be build. Note, that order here is not importand nor does this imply some build dependency.

AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_PROG_LIBTOOL

These are checks for the presence of a suitable C and C++ compiler as well for the Libtool program.

gw_CHECK_QT

This is an additional check for the Qt library, not included in the autoconf standard check library.

AC_OUTPUT

When this line is finally reached, all the files declared to be configured will actually be written. The rest of the file, the echo lines, are a final goodie.

This configure.ac is taken as-is and is expanded to final configure script ... and yes: it gets heavenly expanded!

How is it expanded? Well, the whole configure.ac file is given to m4. M4 is rarely used outside the GNU tools. It is a macro expander quite comparable to the standard C preprocessor. Its basic usage is demonstrated by the example below:

~/tmp $ echo "Hello World" > test
~/tmp $ cat test
Hello World
~/tmp $ m4 -DWorld=Oliver test > result
~/tmp $ cat result
Hello Oliver

Fair simple. When running autoconf the configure.ac file is examined by m4 and every macro defined will be replaced (as well as some standard stuff added) to produce the final configure script. So all this `AC_xxx` and `AM_xxx` are really macros defined elsewhere.

aclocal.m4 and acinclude.m4

But where takes m4 its macro definitions from? Well, when you install the GNU tools you'll also get a /usr/share/aclocal which holds a vast bunch of m4-files filled with many, many common macros. Most of these standard macros are already described on Autoconf. Now, when running autoconf it places a lot of additional files into the project-root. One of it is aclocal.m4 holding all the necessary standard m4 macros.

Everytime autoconf is invoked it examines configure.ac and generates a new aclocal.m4. This is the prime file where all this Checking...xyz echos come from.

The standard macros are quite comprehensive, but may lack support for some additional software needed by superdooper. These checks are not included in the list of the standard macros. What to do?

Easy: you create another file: acinclude.m4 which holds all additional non-standard m4 macros fitting your needs. Place in here every macro you find on the web or create your own one. Why not place your specific macros directly into aclocal.m4? Because, this file goes to trash each time you run autoconf and will be recreated. acinclude.m4 stays.

A good starting point for searching additional m4 macros for autoconf is: Autoconf Macros by Category. If not available you can add your own macro. For exercise: superdooper relies on having a file called ThisIsReallyNeeded containing something in /usr/share/needed. For later usage the content has to be saved somewhere and accessed later on as a variable in the Makefile.

Extend the acinclude.m4 with something like this (remember: this is really bash ... more or less)

# This is a certain superdooper check macro

AC_DEFUN([SD_ThisIsReallyNeeded], [
   
   AC_MSG_CHECKING([ThisIsReallyNeeded])
   
   NEEDED=`cat /usr/share/needed/ThisIsReallyNeeded 2>/dev/null`
   if test x${NEEDED} = x; then
      AC_MSG_ERROR([Woha!! /usr/share/needed/ThisIsReallyNeeded not found or empty! Please get one installed.])
   fi

   AC_MSG_RESULT([${NEEDED}])

   AC_SUBST([NEEDED])
])

Step by step:

  1. AC_DEFUN declares a new macro assigning it the name `SD_ThisIsReallyNeeded`.
  2. AC_MSG_CHECKING puts out that "Checking..." echos along with some text.
  3. Then simple bash scripting of testing the content.
  4. AC_MSG_ERROR echos some error message and aborts configure.
  5. AC_MSG_RESULT adds some text to the "Checking..." line previously in case of success.
  6. The last one, AC_SUBST, tells the system to provide a variable named NEEDED along with its current content for later use (e.g. in Makefiles).

Now right along within your configure.ac you can type prior to AC_OUTPUT:

...
# Check for the ThisIsReallyNeeded file
SD_ThisIsReallyNeeded
...

That was it. Done. When re-running autoconf now, your personal check is included in the configure script and you have access throughout your Makefiles to the very content of a variable named ${NEEDED}, which is system dependend.

Inside your own AC_DEFUN macro you can do whatever you want to. Some macros really do create a temporary C++ file, invoke the gcc and check if that file gets compiled right. They do this! However, be aware that the main goal of the GNU tools is portability. Avoid some freaking bash tricks using exotic commands, this may work on your machine, but maybe not on the user side running some freaking, exotic operating system.

Collecting definitions

Normaly, any defintions which turn things on or off, are passed on the command line to the compiler. As for using the GNU Autotools you'll likely get a very long command line for the compiler holding a mess of -DThis -DThat -D_Do_Not_That and such.

You can do better by declaring

AC_CONFIG_HEADERS([config.h])

which now will create a file config.h holding a lot of

#define This 1

Autoheader (which is responsible for this macro) will now substitue the long macro definition list to the compiler with a small -DHAVE_CONFIG_H indcating that there is a file called config.h which now holds all necessary definitions. Naturally this leads to code like this in source files:

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Note: the file config.h is created by ./configure each time!

Common Macros

These are macros commonly used:

MacromeaningSample
AC_CONFIG_SUBDIRS([LIBS])Run configure recursively on LIBSAC_CONFIG_SUBDIRS([plugins main])
AC_DEFINE([NAME], [Value])Create a define statement for NAME=Value.AC_DEFINE([_GNU_SOURCE], [1])
AC_CHECK_HEADERS([HEADER], [FOUND], [NOT-FOUND])Check the header files listended in HEADER and perform FOUND or NOT-FOUND Action. Hence, if the header file is found then a HAVE_NAME_H (NAME_H subsituted) define is set. If you are using autoheader, this define is placed in the config.h file.AC_CHECK_HEADERS([assert.h stdbool.h], , [AC_MSG_ERROR([Error!])])
AC_CHECK_LIB([LIB], [FUNC], [FOUND], [NOT-FOUND]) Check a library called LIB having the function FUNC. As for AC_CHECK_HEADERS the some is true for this one. A HAVE_LIB is defined ... and the library automatically added to the list of libraries for linkage.AC_CHECK_LIB([pthread], [pthread_getspecific], , [AC_MSG_ERROR([Error!])])
AC_SEARCH_LIB([FUNC], [LIBS])Search a function FUNC in a set of LIBS (if found the systems adds the libraries for linkage and sets a certain define).AC_SEARCH_LIBS([gettext], [text txt])

enable/disable build features

Your software may have the ability to introduce additional features. E.g. you may build your application with KDE-support or with Gnome-support or none at all. As for the source, this is going to be something like this:

#ifdef KDE_FEATURE
#include <blah>

... code ...

#endif

So, your task is to provide the configure script a method to set or unset this KDE_FEATURE define.

This is accomplished by using the AC_ARG_ENABLE macro.

Let's stick to our sample project and think of the ability to introduce some plugin subsystem. Therefor you need to set PLUGIN define. The configure.ac now contains:

... other macros ...

AC_ARG_ENABLE([plugin], 
    AC_HELP_STRING([--enable-plugin], [build with plugin support]),
    AC_DEFINE([PLUGIN], [1]))

... other macros ...

Now, when typing ./configure --help this additional output is presented:

...
Optional Features:
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
...
  --enable-plugin         build with plugin support

And when running ./configure --enable-plugin a -DPLUGIN=1 is passed to the compiler --- He Ho!! Done! Roger! Over & out!

Makefile.am

As configure.ac is the kick-off for the configure script, so is Makefile.am for the final Makefile. And again, things get substituted and expanded in a very heavy manner.

First: for every project, including the project-root, you'll need a Makefile.am. This is quite opposite to configure.ac which is a singelton inside the project-root.

Second: As a Makefile is input to make, Makefile.am syntax is the common normal make-syntax.

The project-root Makefile.am should be far easy:

# Makefile.am for superdooper

# build order
SUBDIRS = abc def ghi sharethis sharethat gfx doc super dooper 

SUBDIRS is introduced by automake to list all the sub-projects (as folder names) to be invoked by make, and ... yes ... order here is important. The final Makefile will now step into each listed folder and invokes make there.

SUBDIRS is the first of a whole series of predefined macros of automake. There are a lot more to come.

Here is super's Makefile.am:

# Makefile.am for super 

# general automake options
AUTOMAKE_OPTIONS = subdir-objects

# add compiler flags (if any)
AM_CXXFLAGS := $(CXXFLAGS) $(QT_CXXFLAGS)

# moc settings
moc_%.cpp: %.h
    @echo Moc\'ing ... $<
    @$(MOC) -o $@ $<

# uic settings
%.cpp: %.ui %.h
    @echo Uic\'ing ... $<
    @$(UIC) -i $*.h -o $@ $<
    
%.h: %.ui
    @echo Uic\'ing ... $<
    @$(UIC) -o $@ $<


bin_PROGRAMS = super

# Qt stuff
super_UICS = 
super_UIC_DECLS = 
super_UIC_IMPLS = 
super_UIC_MOCS = 
super_MOCS = $(super_UIC_MOCS) \
    moc_SuperDialog.cpp

# main sources
super_SOURCES = SuperDialog.cpp \
    SuperClass.cpp \
        main.cpp

# these source are to be build, but are not part of the dist: build them first
BUILT_SOURCES = $(super_UIC_DECLS) $(super_UIC_IMPLS) $(super_UIC_MOCS)

# sources to be compiled but not distributed in a tarball (e.g. they are generated somehow during the build process)
nodist_super_SOURCES = $(super_UIC_IMPLS) $(super_MOCS)

# header files to be included in the build but not installed (2nd: not distributed also)
noinst_HEADERS = SuperClass.h SuperDialog.h
nodist_noinst_HEADERS = $(super_UIC_DECLS)

# additional compiler flags
super_CPPFLAGS = -I$(top_srcdir)/abc -I$(top_srcdir)/def -I$(top_srcdir)/sharethis -I$(top_srcdir)/sharethat

# additional libs to link with (in case of exec)
super_LDADD = -L$(top_builddir)/abc -L$(top_builddir)/def -L$(top_builddir)/sharethis -L$(top_builddir)/sharethat -labc -ldef -lsharethis -lsharethat $(LDADD) $(QT_LDADD) -L$(libdir) 

# extra included in a distribution (tarball)
EXTRA_DIST = $(super_UICS)

# ---------- additional clean ----------

# place here any stuff to be done when cleaning the project
clean-local: 
    @echo 'rm -f $(super_MOCS) $(super_UIC_IMPLS) $(super_UIC_DECLS)'
    @rm -f $(super_MOCS) $(super_UIC_IMPLS) $(super_UIC_DECLS)

# ---------- post build steps ----------

# place here any stuff to be done when doing it all
all-local: 

# ---------- additional install steps ----------

# place here any stuff do be done when installing the executables
install-exec-local:

# place here any stuff do be done when installing data
install-data-local:

# place here any stuff to be done when uninstalling
uninstall-local:

Let's go into detail:

AUTOMAKE_OPTIONS = subdir-objects

Well, this tells automake to generate a Makefile which stores all object files into the source folder. Object-files can be generated also elsewhere ...

AM_CXXFLAGS := $(CXXFLAGS) $(QT_CXXFLAGS)

This declares common compiler flags as derived by a previous run of the configure script.

# moc settings
moc_%.cpp: %.h
    @echo Moc\'ing ... $<
    @$(MOC) -o $@ $<

# uic settings
%.cpp: %.ui %.h
    @echo Uic\'ing ... $<
    @$(UIC) -i $*.h -o $@ $<
    
%.h: %.ui
    @echo Uic\'ing ... $<
    @$(UIC) -o $@ $<

These statements deal with the generation of moc-files typical to Qt projects. As you see these are make normal statements.

bin_PROGRAMS = super

Ah! Here it is! This line tells automake many, many, many things:

1. We are going to build a program.
2. The program is called super.
3. and it will be stored into the prefix/bin folder.

This is not just a simple make variable definition. When automake gets it, it will shake it, squeeze it and stretch it!

First we have this: "bin_". This is actually some sort of configuration and/or command to automake. Precisely here it refers to a folder named "bin" with a destination prefix added. This destination prefix is derived through configure and defaults to /usr/local. So, if the user does not explicitly state another installation path, super will be placed into /usr/local/bin.

There are others out there, e.g. "lib_" for libraries and "sbin_" for privileged programs.

Second a primary is declared: PROGRAMS. This declares that we are building an executable program. Again, there are others: LIBRARIES, LTLIBRARIES, SCRIPT, ...

Third, the value is super which gives the name of the final executable but also serves as an configuration determiner for stuff later on specific to the build of super (see below).

Here are two Makefile.am files for libraries.

Makefile.am for abc:

...
lib_LIBRARIES = libabc.a
...

Makefile.am for sharethis:

...
lib_LTLIBRARIES = libsharethis.so
...

(Rather) next:

# main sources
super_SOURCES = SuperDialog.cpp \
        SuperClass.cpp \
        main.cpp

The primary here is SOURCES and tells automake that this lists the sources to be compiled. For what? Well, by prefixing with "super_" automake knows that these are the sources for the super application. That "super_" is derived from the name given at the PROGRAMS primary.

Why so complicated? Isn't it sufficient to just state "SOURCES" without a prefix?

No. Because you can build more than one thing at once. E.g.:

bin_PROGRAMS = super dooper
...
super_SOURCES = SuperDialog.cpp SuperClass.cpp supermain.cpp
dooper_SOURCES = DooperDialog.cpp DooperClass.cpp doopermain.cpp
...

Without the "super_" and "dooper_" prefix automake simply has no chance to decide which one is the correct set of files to use.

However, there is also some sort of substitution of the names due to special usage of the "." in Makefiles. Remember the "lib_LIBRARIES = libabc.a" above? Well, naturally the sources for that library will go along like:

...
libabc.a_SOURCES = ...
...

make will certainly get confused by that dot "." here. So every dot "." is substituted by an underscore "_", which final leads to:

...
libabc_a_SOURCES = ...
...

Ok,right before that SOURCE primary we had other stuff:

# Qt stuff
super_UICS = 
super_UIC_DECLS = 
super_UIC_IMPLS = 
super_UIC_MOCS = 
super_MOCS = $(super_UIC_MOCS) \
    moc_SuperDialog.cpp

Note, that none of the above primaries (UICS, UIC_DECLS, ...) are known to automake. Therefore automake doesn't do anything with them. These are really mere variables without any special automake side effect.

BUILT_SOURCES = $(super_UIC_DECLS) $(super_UIC_IMPLS) $(super_UIC_MOCS)

The BUILT_SOURCES is a primary which tells automake to get all the listened files somehow created prior to compilation. In this example the Qt uic and moc commands are executed. The term somehow created really means that the developer has to provide some make rules to produce these targets ...

noinst_HEADERS = SuperClass.h SuperDialog.h

These are the header files. The prefix "noinst_" tells automake to not install these files into some ".../include" folder on the system. Likely a pure program may not have any header files to be installed, so each one may be listened here.

nodist_noinst_HEADERS = $(super_UIC_DECLS)

Again header files to be not installed. But adding the "nodist_" also states, that these files are also not included in a tarball ("distribution").

nodist_super_SOURCES = $(super_UIC_IMPLS) $(super_MOCS)

Here are some source files which are also not included in a tarball. Likely they will be created at "BUILT_SOURCES" each compilation.

# additional compiler flags
super_CPPFLAGS = -I$(top_srcdir)/abc -I$(top_srcdir)/def -I$(top_srcdir)/sharethis -I$(top_srcdir)/sharethat

Yet even more compiler flags can be given precisely to the compilation of super. As you see the most frequent use is to specify the path to the header files needed for compilation (that's why this primary was formerly called "INCLUDES").

$(top_srcdir) is a variable which gets substituted with ... well ... the project-root. You may also use $(srcdir) which reflects to the current source folder.

# additional libs to link with (in case of exec)
super_LDADD = -L$(top_builddir)/abc -L$(top_builddir)/def -L$(top_builddir)/sharethis -L$(top_builddir)/sharethat -labc -ldef -lsharethis -lsharethat $(LDADD) $(QT_LDADD) -L$(libdir) 

Additional linker flags. Again, you can state $(top_builddir) and $(builddir).

# extra included in a distribution (tarball)
EXTRA_DIST = $(super_UICS)

This primary lists all the files, which should also get into a tarball besides the files listened at SOURCES and HEADERS.

# place here any stuff to be done when cleaning the project
clean-local: 
    @echo 'rm -f $(super_MOCS) $(super_UIC_IMPLS) $(super_UIC_DECLS)'
    @rm -f $(super_MOCS) $(super_UIC_IMPLS) $(super_UIC_DECLS)

# ---------- post build steps ----------

# place here any stuff to be done when doing it all
all-local: 

# ---------- additional install steps ----------

# place here any stuff do be done when installing the executables
install-exec-local:

# place here any stuff do be done when installing data
install-data-local:

# place here any stuff to be done when uninstalling
uninstall-local:

Here some additional targets for make are declared. These are really additional targets. automake already sets up the Makefile for building the whole source ("all:"), installing it ("install:"), uninstalling it ("uninstall:") and cleaning it ("clean:"). These targets here are invoked if they are present and do extra stuff.

As a consequence: do not declare common standard targets like "`all:`" because automake uses them right away. Same is true for some settings like ".PHONY".

Small summary

prefixmeaning
bin_files are installed in ../bin
lib_files are installed in ../lib
sbin_files are installed in ../sbin
data_files are installed in ../share
noinst_files will NOT be installed
nodist_files will be NOT included in tarball
primarymeaning
SUBDIRSlist of folders to cd into and call make (recursively)
PROGRAMSprograms are built
LIBRARIESstatic libraries are built
LTLIBRARIESshared libraries are built
SOURCESlist of source files
HEADERSlist of headers
CPPFLAGSadditional compiler flags
LDADDadditional linker settings
BUILT_SOURCESextra files to generate prior to built
EXTRA_DISTextra files to be included in tarball
variablemeaning
top_srcdirtopmost source folder
srcdircurrent source folder
top_builddirtopmost build folder
builddircurrent build folder
prefixcurrent installation prefix (defaults to "/usr/local")
datadirdata folder usually equivalent to $(prefix)/share
targetmeaning
all-localpost-build steps
clean-localpost-clean steps (e.g. remove additional temporary files)
install-exec-localpost-install steps for executable
install-data-localpost-install steps for data
uninstall-localpost-uninstall steps
distcreate a distribution (tarball)
distcleanremove everything temporary. This is the radical "hyper-clean", the clean-clean, the "if-it-isn't-fast-enough-kill-it"-clean

Note, this is really a short and insufficient intro into the capabilities of automake. This ain't a replacement for Automake but maybe serves as a good starting point to get firm with it.

Bootstrapping

Good. We have now talked about the project structure; created a project-root; added the source files into some sub-folders; set up a proper configure.ac along with a acinclude.m4 within the project-root; placed various Makefile.am into each source sub-folder. What's next?

Bootstrapping: get it to live!

First create a small script called autogen.sh in project-root, because this comes very handy and might get used frequently:

#! /bin/bash
autoreconf --force --install

It (re-)runs autoconf and any subsequent GNU tools for your entire project.

When run the first time it may complain about some missing files. These are usually:

  • "AUTHORS" listening the developers.
  • "COPYING" the license.
  • "Change`Log" lists what are the "improvements" to previous versions of the software.
  • "INSTALL" how to install the software.
  • "NEWS" some "news" related to the software.
  • "README" the file no-one ever reads.

All these are pure text files and you are free to simply `touch` them.

If autogen.sh passes, we are on! Now, call ./configure and make.

Have fun!

Projects without a build

Some projects are not to be compiled. They may be programs - yes - but of SCRIPT nature. Or plain documentation. Or simply graphics. How to deal with them?

Well you may use the SCRIPT primary ... or simply none at all. Just fill in the install-data-am target in the Makefile.am.

E.g.:

# Makefile for some data files

# we want to install our data (files a and b) into prefix/shared/myApp folder ...
myappdir= $(datadir)/myApp
myapp_DATA = a b

# include the files a and b in the distribution tarball
EXTRA_DIST = a b

That's all. The GNU Autotools now take care of installing and uninstalling the files a and b to $(prefix)/share/myApp.

Easy, isn't it?


Providing menus and icons on the desktop

Menus and icons are tackled usually tackled by each Window Manager differently. However many of them like KDE, Gnome, Xfce and others do follow the recommendations by the freedesktop.org. Search for XDG.

What's the idea? Well, you have Desktop-Files describing an entry in a menu structure. desktop-entry-spec declares the syntax for such a file.

[Desktop Entry]
Name=my_app
GenericName=My Application
Comment=This is my own Application
Exec=my_app
Icon=my_app
DocPath=
StartupNotify=true
Type=Application
Categories=Applications;Qt;Science;Physics

This file above declares an application called "my_app" with some additional information (like !GenericName or Comment). This application will have the icon "my_app" attached to it. It fits to "Applications". Also it is a "Qt" application and serves some "Science" research in "Physics".

When a Window Manager displays this application, it searches its internal cache for an icon labeled "my_app" holding current relevant dimensions (starting form 16x16 up to 128x128). If found the Window Manager displays now the icon along with the !GenericName, Name, Exec or Comment. By clicking the displayed information, the Exec line is launched.

A different structure is now responsible for inserting and displaying these desktop items and thus creating a menu structure. This is tackled by menu-spec. Desktop entries are associated to this menu structure by their Categories attributes. Several Distribution vendor like to introduce their own menu scheme, so do not expect a certain layout.

Installing these desktop files (and menu directory files if you insist in creating your own menu substructure) is sure a hard task, since it involves reading and writing of environment variables up to XML file parsing ...

... but the freedesktop.org has published the xdg-utils, which is a set of small command line utilities capable of installing and removing desktop icons, desktop files, menu files and more, without the need to tackle with the concrete Window Manager underneath directly.

It's very wise to get known to them, introduce an autoconf-macro to check for the existence of those utils and solely rely on them in order to get your icons/applications on the desktop ...

Let's talk dirty! With the help of the xdg-utils:

Our sample project has two execuatbles: super and dooper. We create icons for them at least in dimension 32x32 and 64x64. We also got to need to have two desktop files ready: super.desktop and dooper.desktop. Within these two files the properties are settled, however we don't know yet where the Exec line should point to since we don't know in advance what's the proper value of prefix.

So, content of super.desktop:

[Desktop Entry]
Encoding=UTF-8
Type=Application
Exec=PATHTOSUPER
Icon=super
Name=SuperApp
Name[de]=Super-Anwendung

the content of dooper.desktop is likewiese.

Now the Makefile.am:

...
EXTRA_DIST = \
    super.desktop \
    dooper.desktop \
    32x32/super.png \
    32x32/dooper.png \
    64x64/super.png \
    64x64/dooper.png
...


install-exec-local:
    xdg-icon-resource install --size 32 32x32/super.png
    xdg-icon-resource install --size 32 32x32/dooper.png
    xdg-icon-resource install --size 64 64x64/super.png
    xdg-icon-resource install --size 64 64x64/dooper.png
    sed -i "s/PATHTOSUPER/$(prefix)/bin/super/g" super.desktop
    sed -i "s/PATHTODOOPER/$(prefix)/bin/dooper/g" dooper.desktop
    xdg-desktop-icon install ./super.desktop
    xdg-desktop-icon install ./dooper.desktop

...

uninstall-local:
    xdg-icon-resource uninstall --size 32 super
    xdg-icon-resource uninstall --size 32 dooper
    xdg-icon-resource uninstall --size 64 super
    xdg-icon-resource uninstall --size 64 dooper
    xdg-desktop-icon uninstall super.desktop
    xdg-desktop-icon uninstall dooper.desktop
...

Done.


Feel free to ask any further questions Dyle.


The first version of this article has been written by me for my employer and has been published at https://itq.arcs.ac.at/trac-secoqc/wiki/GNU Autotools (may be broken)

Personal tools