GNU Autotool
From Dyle's Wiki
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
sedandawk -
gzipandtarto 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:
- Check the system for any quirks (e.g. having the standard headers elsewhere as opposed to the common way).
- 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,
./configurewill look for those by performing several steps to "touch" these apps. - Based on the gathered knowledge,
./configurecreates 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
/optwhich lets you install software in such a mess Windows is used to. This is the Linux-Version ofC:\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:
- We have not one but two executables:
superanddooper - There are static libraries created:
abc,defandghi.abcanddefare used bysuper,defandghiare used bydooper. - Also there are two shared libraries:
sharethis.soandsharethat.so - There are tons of graphical data, e.g. for all that icons in the toolbar and menus, some of theme are unique to either
superanddooper, some are really shared. - 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:
- 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.
- 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 thoseconfigurescript comes in as well as theconfigure.acandMakefile.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 toMakefile).
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
configurescripts and the finalMakefile. Its input isconfigure.ac. It is used solely in the project-root folder. - automake Automake is used to generate a
Makefile.inwhich is then taken as an input forconfigureto produce the finalMakefile. Its input isMakefile.am. ThoseMakefile.amare 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:
- AC_DEFUN declares a new macro assigning it the name `SD_ThisIsReallyNeeded`.
- AC_MSG_CHECKING puts out that "Checking..." echos along with some text.
- Then simple bash scripting of testing the content.
- AC_MSG_ERROR echos some error message and aborts
configure. - AC_MSG_RESULT adds some text to the "Checking..." line previously in case of success.
- 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:
| Macro | meaning | Sample |
| AC_CONFIG_SUBDIRS([LIBS]) | Run configure recursively on LIBS | AC_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
| prefix | meaning |
| 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 |
| primary | meaning |
| SUBDIRS | list of folders to cd into and call make (recursively)
|
| PROGRAMS | programs are built |
| LIBRARIES | static libraries are built |
| LTLIBRARIES | shared libraries are built |
| SOURCES | list of source files |
| HEADERS | list of headers |
| CPPFLAGS | additional compiler flags |
| LDADD | additional linker settings |
| BUILT_SOURCES | extra files to generate prior to built |
| EXTRA_DIST | extra files to be included in tarball |
| variable | meaning |
| top_srcdir | topmost source folder |
| srcdir | current source folder |
| top_builddir | topmost build folder |
| builddir | current build folder |
| prefix | current installation prefix (defaults to "/usr/local") |
| datadir | data folder usually equivalent to $(prefix)/share |
| target | meaning |
| all-local | post-build steps |
| clean-local | post-clean steps (e.g. remove additional temporary files) |
| install-exec-local | post-install steps for executable |
| install-data-local | post-install steps for data |
| uninstall-local | post-uninstall steps |
| dist | create a distribution (tarball) |
| distclean | remove 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)

