Part II: Automake
Table of Contents
This page contains a tutorial on the usage GNU Automake. This is the second part of the Autotools tutorial.
1 Introduction
Using a simplified syntax, Automake produces functional Makefiles
which are consistent with the GNU coding standards. This implies that
the Makefiles use standard build targets
(make install
, make clean
, make dist
,
…) and standard installation paths (/usr/bin
, /usr/share/
,
/usr/include
, /usr/man
, …). To avoid struggling too much with
Automake, it is important to understand the usual build, test and
install philosophies common to GNU projects, as well as the underlying
infrastructure managed by Autotools.
You have seen in the Autoconf tutorial how to write configure.ac
and Makefile.in
. Here, we introduce Automake which will produce
the Makefile.in
from a high-level specification file Makefile.am
.
The produced file will automatically add extra functionalities to the
Makefile, using standard target names.
2 GNU coding standards
Some aspects of the GNU coding standards (GCS) are well known to
users. If you use them, your users will be grateful because it will
avoid them to read your installation instructions! In addition, it
will make the packaging of your code much simpler if
you want to make it available as a Linux distribution package
(.deb
, .rpm
, …), or as a package for
Homebrew,
Guix,
Conda,
Spack,
EasyBuild, …
2.1 Standard targets
The GCS defines standard target names to be included in Makefiles, such that the behavior of make becomes uniform among multiple packages. Here is a short list of the main targets:
make all
- Builds all the targets sufficient to declare the package built.
make clean
- Removes all derived files.
make check
- Runs unit tests provided by the package.
make install
- Installs the package
make uninstall
- Uninstalls the package
make html
- Builds the documentation in HTML format
make check
- Performs self-tests
2.2 Filesystem Hierarchy Standard
The Filesystem Hierarchy Standard (FHS) defines the directory structure and contents in Linux distributions. All Unix-like systems follow more or less the FHS, which defines standard places for files like:
Path | Description |
---|---|
/bin |
Essential binaries |
/lib |
Essential libraries |
/etc |
Editable Text Configuration files |
/sbin |
Essential system binaries |
/share |
Platform-independent files |
/tmp |
Directory for temporary files |
/usr |
Secondary hierarchy for user data |
/usr/bin |
Non-essential binaries |
/usr/lib |
Libraries for non-essential binaries |
/usr/include |
Standard include files |
/usr/local |
Tertiary hierarchy for local data |
It is important to know what these directories contain, because it will let you know where to install your software and the extra files that are distributed with it such as the documentation or the configuration files.
2.3 Standard Makefile.in
variables
Installation directories are named by standard variables, so it gives the user the flexibility to install the package in a non-standard place. For example, this feature is required for packaging where the software is installed in a temporary directory.
The main variables are:
Variable | Default | Description |
---|---|---|
prefix |
/usr/local |
Root of the hierarchy |
exec_prefix |
$(prefix) |
Root of the hierarchy of executable files |
bindir |
$(exec_prefix)/bin |
Where to install the binaries |
libdir |
$(exec_prefix)/lib |
Where to install the libraries |
includedir |
$(prefix)/include |
Where to install include files |
datarootdir |
$(prefix)/share |
The root of the platform-independent files |
datadir |
$(datarootdir) |
Where to install platform-independent files |
mandir |
$(datarootdir)/man |
Where to install platform-independent files |
sysconfdir |
$(prefix)/etc |
Where to install configuration files |
docdir |
$(datarootdir)/doc/$(package) |
Where to install the documentation |
srcdir |
Set by configure |
The directory of the sources being compiled |
In the Makefile.in
, these variables may be used between @
symbols, like @bindir@
for instance.
These variables can take different values by specifying them as
arguments of the configure
script:
./configure --prefix=$HOME/install --sysconfdir=$HOME/.config/
2.4 Standard environment variables
The GCS also defines a set of user-defined environment variables allowing to change the compilation behavior of the package.
Variable | Description |
---|---|
CC |
C compiler |
FC |
Fortran compiler |
CXX |
C++ compiler |
CPP |
C/C++ preprocessor |
CFLAGS |
C compiler flags |
FCFLAGS |
Fortran compiler flags |
CXXFLAGS |
C++ compiler flags |
CPPFLAGS |
C/C++ preprocessor flags |
LDFLAGS |
Linker flags |
LIBS |
Libraries to add to the linker |
To use alternate compiler flags without changing the Makefile, you
can set these variables on the command line as arguments to
make
. For example:
make FC=ifort FCFLAGS="-xAVX -O2"
You can also do it at configuration time:
./configure FC=ifort FCFLAGS="-xAVX -O2"
To see the complete list of user variables, you can run
./configure --help
3 Writing portable Makefiles
The Makefiles produced by Automake are made to be portable among Linux and Unix systems. Before diving into Automake, we will present a few techniques to write portable Makefiles.
The most popular implementation of make
is GNU Make. GNU Make is much more
evolved than the POSIX standard, so Makefiles designed for GNU Make are not
guaranteed to work with alternate implementations of make
, such as BSD make for
instance. If you want your software to be really portable, you should stick to the
POSIX standard in your Makefiles by including the line
.POSIX:
at the top of your Makefile.
3.1 What you will lose by sticking to the POSIX standard
Pattern rules, such as
%_test.o: %_test.c $(CC) $< -o $@
- Special functions
$(wildcard *.c)
,$(patsubst %.f, %.o, $(SRC))
, … - The ability to have one rule with multiple output files
3.2 Guidelines that will make everything more portable
- Don't use any other automatic variable than
$@
(the name of the current target) - Write your scripts with _bash{/bin/sh} instead of
/bin/bash
- Don't put your compiled files (
.o
) in a separate location than the source files - Give up writing your Makefiles by hand. Use scripts (Bash, Python) to write them for you.
- Install
bmake
as an alternative to GNU make to check the portability of your Makefiles. - Use
$(MAKE)
in your Makefiles instead ofmake
for recursive builds - Similarly, use POSIX constructs for your shell scripts or shell
constructs in
configure.ac
(tests with a single square bracket).
4 Automake basics
Automake reads a file named Makefile.am
and produces the Makefile.in
file. The produced file may contain thousands of lines, and is
guaranteed to have the basic functionalities (make all
,
make clean
, …) in a portable Makefile.
Makefile.am
contains portable make
syntax, and the code it
contains will be inserted at the proper location in the Makefile.in
file.
Automake enables the use of the +=
operator, which is an
extension of the POSIX standard.
We will now create the minimal working example for the code
presented in the Autoconf section. First, inform Autoconf that
you will be using Automake by inserting the AC_INIT_AUTOMAKE
macro in configure.ac
:
AC_INIT([sherman-morrison], [0.0.1], []) AM_INIT_AUTOMAKE([foreign subdir-objects silent-rules])
The foreign
flag informs Autotools that you are not building a
GNU project, otherwise you will be warned that file files NEWS
,
README
, AUTHORS
, ChangeLog
and COPYING
should be present in
the project.
The silent-rules
option causes Automake to generate Makefiles
that don't display the complete command line of the actions it
performs, but a less verbose output similar to CMake.
The subdir-objects
option informs Automake that source files
can be located in subdirectories. It is the case here because all
the source files are located in the src/
and tests/
directories.
Autoconf macros start with AC_
and Automake macros start with AM_
.
Then, create the file Makefile.am
with the following content:
bin_PROGRAMS = test_h5 test_h5_SOURCES = $(SOURCES) SOURCES = src/SM_Maponi.cpp \ src/SM_Standard.cpp \ src/Woodbury.cpp \ src/SMWB.cpp \ src/Helpers.cpp \ tests/test_h5.cpp
The syntax of this file will be explained in the next section.
In the definition of the SOURCES
variable, all the
required files are explicitly written. As Automake produces
portable Makefiles, it is not possible to benefit from the GNU
make syntax such as $(wildcard src/*.cpp)
.
Automake will overwrite Makefile.in
. Before running Automake the
first time, don't forget to make a backup copy of your Makefile.in
file.
We can now create the Makefile.in
and the Makefile
by running
$ autoreconf $ ./configure
and build the project
$ make
You can remark that many standard targets are available.
make clean
cleans the compiled files, make maintainer-clean
removes more files, including the Makefile
and config.status
.
Automake also provides make install
and make uninstall
. To
test it, you can create a directory named _install
in the current
directory and configure the package to be installed in this
particular directory:
$ mkdir _install $ ./configure --prefix=$PWD/_install [...] $ make install make[1]: Entering directory '/home/test/Sherman-Morrison-0.1-demo' /bin/mkdir -p '/home/test/Sherman-Morrison-0.1-demo/_install/bin' /usr/bin/install -c test_h5 '/home/test/Sherman-Morrison-0.1-demo/_install/bin' make[1]: Nothing to be done for 'install-data-am'. make[1]: Leaving directory '/home/test/Sherman-Morrison-0.1-demo' $ ls _install/ bin $ ls _install/bin/ test_h5 $ make uninstall ( cd '/home/test/Sherman-Morrison-0.1-demo/_install/bin' && rm -f test_h5 ) $ ls _install/bin/ $
5 Creating a distributed tarball
5.1 Maintainer mode
There are two types of people who will try to compile the source code:
- The end-users having downloaded a tarball with sources
- The developers of the code, who produce the source distribution of the code
These two profiles should be differentiated, because their system
requirements are usually different. The first difference is that the
code developers will need to install Autotools as they will
produce the configure
script, but Autotools will not be needed
by the end-users.
A more critical example is when complex Makefiles are produced by a script. The script generating the Makefiles should be stored in the version control system for developers, as opposed to the generated Makefiles. For the end-user's point of view, only the generated Makefiles are needed for the compilation of the sources, and the script should not be present in the source distribution.
To handle these two types of profiles, Autotools provides the
maintainer mode. For instance, in maintainer mode the configure
script is re-generated when configure.ac
has changed, as opposed
to standard mode. All these rules are activated in the generated
Makefiles by running
./configure --enable-maintainer-mode
5.2 Source distribution targets
Automake provides automatically the dist
target.
make dist
creates a .tar.gz
file which contains all the
files necessary to install the package: installation instructions,
configure
script, source code, documentation, man pages, unit
tests, etc.
$ make dist [...] $ tar --list -f sherman-morrison-0.0.1.tar.gz sherman-morrison-0.0.1/ sherman-morrison-0.0.1/m4/ sherman-morrison-0.0.1/m4/ax_lib_hdf5.m4 sherman-morrison-0.0.1/src/ sherman-morrison-0.0.1/src/SM_Maponi.cpp sherman-morrison-0.0.1/src/SM_Standard.cpp sherman-morrison-0.0.1/src/Woodbury.cpp sherman-morrison-0.0.1/src/SMWB.cpp sherman-morrison-0.0.1/src/Helpers.cpp sherman-morrison-0.0.1/tests/ sherman-morrison-0.0.1/tests/test_h5.cpp sherman-morrison-0.0.1/Makefile.am sherman-morrison-0.0.1/configure sherman-morrison-0.0.1/configure.ac sherman-morrison-0.0.1/aclocal.m4 sherman-morrison-0.0.1/Makefile.in sherman-morrison-0.0.1/compile sherman-morrison-0.0.1/depcomp sherman-morrison-0.0.1/install-sh sherman-morrison-0.0.1/missing
Can you figure out if this archive is OK?
To answer this question, we can use the power of Automake and run
the distcheck
target:
$ make distcheck [...] ../../src/SM_Maponi.cpp:4:10: fatal error: SM_Maponi.hpp: No such file or directory #include "SM_Maponi.hpp" ^~~~~~~~~~~~~~~ compilation terminated. make[1]: *** [Makefile:449: src/SM_Maponi.o] Error 1 make[1]: Leaving directory '/home/test/Sherman-Morrison-0.1-demo/sherman-morrison-0.0.1/_build/sub' make: *** [Makefile:613: distcheck] Error 1
Oh no! This archive is not valid. By inspecting the content of the archive, we notice we forgot to add the header files in the sources. Although the project builds on our platform, the Makefile is incomplete because it lacks some dependencies.
We can update the SOURCES
variables as
SOURCES = src/SM_Maponi.cpp \
src/SM_Standard.cpp \
src/Woodbury.cpp \
src/SMWB.cpp \
src/Helpers.cpp \
tests/test_h5.cpp \
include/Helpers.hpp \
include/SM_Maponi.hpp \
include/SM_Standard.hpp \
include/SMWB.hpp \
include/Woodbury.hpp
and run make dist
again to rebuild the source distribution. Note
that the current Makefile detects that Makefile.am
has changed, so
it re-runs Automake and also re-generates the Makefile using
config.status
before producing the updated archive.
$ tar --list -f sherman-morrison-0.0.1.tar.gz sherman-morrison-0.0.1/ sherman-morrison-0.0.1/include/ sherman-morrison-0.0.1/include/Helpers.hpp sherman-morrison-0.0.1/include/SM_Maponi.hpp sherman-morrison-0.0.1/include/SM_Standard.hpp sherman-morrison-0.0.1/include/SMWB.hpp sherman-morrison-0.0.1/include/Woodbury.hpp sherman-morrison-0.0.1/m4/ sherman-morrison-0.0.1/m4/ax_lib_hdf5.m4 sherman-morrison-0.0.1/src/ sherman-morrison-0.0.1/src/SM_Maponi.cpp sherman-morrison-0.0.1/src/SM_Standard.cpp sherman-morrison-0.0.1/src/Woodbury.cpp sherman-morrison-0.0.1/src/SMWB.cpp sherman-morrison-0.0.1/src/Helpers.cpp sherman-morrison-0.0.1/tests/ sherman-morrison-0.0.1/tests/test_h5.cpp sherman-morrison-0.0.1/Makefile.am sherman-morrison-0.0.1/configure sherman-morrison-0.0.1/configure.ac sherman-morrison-0.0.1/aclocal.m4 sherman-morrison-0.0.1/Makefile.in sherman-morrison-0.0.1/compile sherman-morrison-0.0.1/depcomp sherman-morrison-0.0.1/install-sh sherman-morrison-0.0.1/missing
We can now test again the source distribution:
$ make distcheck [...] make[1]: Entering directory '/home/test/Sherman-Morrison-0.1-demo/sherman-morrison-0.0.1/_build/sub' depbase=`echo src/SM_Maponi.o | sed 's|[^/]*$|.deps/&|;s|\.o$||'`;\ g++ -DPACKAGE_NAME=\"sherman-morrison\" -DPACKAGE_TARNAME=\"sherman-morrison\" -DPACKAGE_VERSION=\"0.0.1\" -DPACKAGE_STRING=\"sherman-morrison\ 0.0.1\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DPACKAGE=\"sherman-morrison\" -DVERSION=\"0.0.1\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_HDF5=1 -I. -I../.. -I/usr/include -I/usr/include/hdf5/serial -I./include/ -g -O2 -std=c++11 -MT src/SM_Maponi.o -MD -MP -MF $depbase.Tpo -c -o src/SM_Maponi.o ../../src/SM_Maponi.cpp &&\ mv -f $depbase.Tpo $depbase.Po ../../src/SM_Maponi.cpp:4:10: fatal error: SM_Maponi.hpp: No such file or directory #include "SM_Maponi.hpp" ^~~~~~~~~~~~~~~ compilation terminated. make[1]: *** [Makefile:449: src/SM_Maponi.o] Error 1 make[1]: Leaving directory '/home/test/Sherman-Morrison-0.1-demo/sherman-morrison-0.0.1/_build/sub' make: *** [Makefile:613: distcheck] Error 1
Oh no! Still the same error! But now we know that the header file is present.
Can you figure out what is wrong now?
Inspecting the compilation line, we notice that the compilation
takes place in the sherman-morrison-0.0.1/_build/sub
directory.
Automake is now trying to make an out of source build: building
the package outside of the source directory. In the previous
tutorial, we have introduced -I./include
to the CPPFLAGS
variable in the configure.ac
file. Using such a relative path
implies that we are running make in the source directory, which is
not desirable if you want to be able to make RPM packages for instance.
So now we need to move this definition into the Makefile.am
file.
configure.ac
becomes:
CPPFLAGS="${HDF5_CPPFLAGS} ${CPPFLAGS}"
and Makefile.am
has the extra line:
AM_CPPFLAGS = -I$(srcdir)/include/
You can remark that we have now introduced the srcdir
variable
which points the the root of the source. For in source builds,
srcdir
is .
but for out of source builds it is set to the
relative path to the root. If for some reason you need the absolute
path, you can use abs_srcdir
instead.
Now, let us test our source distribution:
$ make distcheck [...] ======================================================== sherman-morrison-0.0.1 archives ready for distribution: sherman-morrison-0.0.1.tar.gz ========================================================
Success! the distribution compiles and we now have something that we can give to our users!
You can specify which tarball format to use with make dist
by
adding the dist-zip
, dist-bzip2
, dist-xz
, … option to the
AM_INIT_AUTOMAKE
macro in configure.ac
6 Automake Syntax
6.1 Variables
Here is the list of the main variables that are used in
Makefile.am
:
AM_CPPFLAGS = -I/dir1 -I/dir2 -I$(srcdir)/include ...
- List the
-I
flags that need to be passed to the compiler to find the include files AM_LDFLAGS = -L/dir1 -L/dir2 ...
- Flags to be passed to the linker
LDADD = file.o $(top_builddir)/dir1/libmylib.la -lmylib ...
- Object files and built libraries to be linked with all executables
CLEANFILES = my_file.mod ...
- List of extra files to remove
with
make clean
EXTRA_DIST
Additional files to be shipped with the binary distribution.
User variables such as
CFLAGS
orCPPFLAGS
should never be overwritten in aMakefile.am
. This explains why these variables have theAM_
prefix.
6.2 Product list variables
In the previous section, we have seen two special variables
bin_PROGRAMS
and test_h5_SOURCES
.
bin_PROGRAMS
contains the names of the programs we are building,
and these will be installed in $(exec_prefix)/bin
upon invocation of
make install
.
Product list variables (PLV) conform to the following template:
[modifier-list]prefix_PRIMARY = product1 product2 ... productN
6.2.1 Installation location prefixes
The prefix
can be an installation location prefix, as in for
example bin_PROGRAMS
or lib_LIBRARIES
. The prefix
corresponds
to a variable defining a directory chopping off the dir
suffix. For example, the bin
prefix corresponds to the $(bindir)
variable. You can make your own prefix, for example:
gpudir = $(exec_prefix)/gpu gpu_PROGRAMS = cublas_test
6.2.2 Other prefixes
noinst
- Products that should not be installed
check
- Products that should be built for testing
EXTRA
- Products that are built conditionally
6.2.3 Primaries
A primary is a class of products. This defines the rules to create in the Makefile to satisfy the needs of the product.
PROGRAMS
- Create rules to build the binary executables
LIBRARIES
- Create rules to build the libraries
LTLIBRARIES
- Create rules to build the libraries using Libtool
SCRIPTS
- Create rules to install the scripts
DATA
- Arbitrary files (documentation, test data, etc)
HEADERS
- Header files to be installed
6.3 Product source variables
In our example, the name of the program is test_h5
, so by defining
test_h5_SOURCES
we can list the source files needed to build
test_h5
. Automake will then do everything necessary in the
Makefile.in
to build test_h5
.
Product source variables (PSV) conform to the following template:
[modifier-list]product_SOURCES = file1 file2 ... fileN
product
refers to the built product. If the product contains
special characters, these should be replaced by underscores. For
example:
lib_LIBRARIES = libc++.a libc___a_SOURCES = ...
6.4 PLV and PSV modifiers
Modifiers change the default behavior of the variable. The main
modifiers are dist
and nodist
, which indicate if the files
should be part of the distributed package or not.
Modify the Makefile.am
to add the LICENSE
file to the source
distribution package, and to install the README.md
file into the
documentation directory.
7 Testing
Automake provides a framework for testing your program. The TEST
variable contains a list of tests to execute when the user runs
make check
. The tests should be written such that success
corresponds to an exit code equal to 0
. An exit status of 77
means that the test should be ignored. For all other exit codes, the
test failed.
Once you have added the TEST
variable to Makefile.am
, you need to
run autoreconf -i
. It will detect that you will need the testing
framework and install it in your package.
- Provide a dummy test for the current package which always succeeds, and another one which always fails.
- Add the tests to the
Makefile.am
- Run
make check
The prefix check
is used for products that should only be
built when make check
is run.
In Makefile.am
, we add
TESTS = tests/success tests/failure check_PROGRAMS = $(TESTS) tests_success_SOURCES = tests/success.cpp tests_failure_SOURCES = tests/failure.cpp
We can now test the package by running
$ make check [...] PASS: tests/success FAIL: tests/failure ============================================================================ Testsuite summary for sherman-morrison 0.0.1 ============================================================================ # TOTAL: 2 # PASS: 1 # SKIP: 0 # XFAIL: 0 # FAIL: 1 # XPASS: 0 # ERROR: 0 ============================================================================ See ./test-suite.log ============================================================================
which results in an error.
Sometimes, you know that a test fails, but you don't have time to
fix it right away. You can add this test to the XFAIL_TESTS
variable, which contains the list of tests that are expected to
fail. Similarly, the XPASS_TESTS
are tests that are not supposed
to pass.
TESTS = tests/success tests/failure XFAIL_TESTS = tests/failure check_PROGRAMS = $(TESTS) tests_success_SOURCES = tests/success.cpp tests_failure_SOURCES = tests/failure.cpp
PASS: tests/success XFAIL: tests/failure ============================================================================ Testsuite summary for sherman-morrison 0.0.1 ============================================================================ # TOTAL: 2 # PASS: 1 # SKIP: 0 # XFAIL: 1 # FAIL: 0 # XPASS: 0 # ERROR: 0 ============================================================================
Now, when we run make distcheck
, the tests will also be run when
building the source distribution package.
8 Using pkg-config
When your software depends on external libraries and you try to compile it on an unknown system, you need to figure out what are the correct flags to access the include files of the library and how to link it. `configure` will try to find the libraries in the standard locations, but if the configuration is a bit exotic, this will require some extra knowledge.
At the moment you install a library on a system, you have all the
knowledge required to use this library in other software: what
compiler options to add. All this information can be saved in a
file with the suffix .pc
stored in a standard location such as
/usr/lib/pkgconfig
, and this information can be retrieved later on
with the pkg-config
command.
Let's assume you have compiled and installed the QMCkl
library. The library will produce the system-specific file
/usr/local/lib/pkgconfig/qmckl.pc
:
prefix=/usr/local exec_prefix=${prefix} includedir=${prefix}/include libdir=${exec_prefix}/lib Name: qmckl Description: Quantum Monte Carlo kernel library URL: https://github.com/trex-coe/qmckl Version: 0.1.1 Cflags: -I${includedir} Libs: -L${libdir} -lqmckl Libs.private: -L/home/scemama/TREX/trexio/_install/lib -L/usr/lib/x86_64-linux-gnu/hdf5/serial -ltrexio -lhdf5 -lpthread -lm
This file tells you that you need to include the flag
-I/usr/local/include
to the C preprocessor (the CPPFLAGS
variable), and the -L/usr/local/lib -lqmckl
flag at the link stage
(the LDFLAGS
variable) if you want to use the dynamically linked library.
This information can be accessed using the pkg-config
command:
$ pkg-config --cflags qmckl -I/usr/local/include/ $ pkg-config --libs qmckl -L/usr/local/lib -lqmckl
In your configure.ac
file, you can simply use
PKG_CHECK_MODULES([qmckl]) CFLAGS="${CFLAGS} `pkg-config --cflags qmckl`" LDFLAGS="${LDFLAGS} `pkg-config --libs qmckl`"
to include the appropriate flags. The PKG_CHECK_MODULE
macro
will fail if pkg-config is not installed on the system, or if the
qmckl.pc
file is not found.
The pkg-config files are searched in the directories defined by the
PKG_CONFIG_PATH
environment variable.
9 Back to Autoconf
9.1 Searching for dependencies
9.1.1 C definitions
If you are using the C/C++ preprocessor, you can enable conditional compilation in your code. This is particularly frequent when you use MPI. For example:
#ifdef HAVE_MPI rc = mpi_reduce(...) #endif
allows you to activate the MPI calls only when the -DHAVE_MPI
option is passed in the command line, or if an included header file
defines the HAVE_MPI
variable.
In the configure.ac
file, the AC_CONFIG_HEADERS
macro produces a
configuration header file which will enable the definition of C
preprocessor variables. The AC_DEFINE
macro in configure.ac
allows to create C definitions.
AC_CONFIG_HEADERS([include/config.h]) case $CC in *icc*) AC_DEFINE([HAVE_INTEL], [], [Define if using the Intel compiler]) ;; *) ;; esac
Will produce the following in include/config.h
if the Intel
compiler is used:
/* Define if using the Intel compiler */ #define HAVE_INTEL /**/
and this code if not
/* Define if using the Intel compiler */ /* #undef HAVE_INTEL */
Similarly, you can give values to the definitions, to pass values
determined at configuration time, like the installation prefix
to locate the configuration files for instance:
AC_DEFINE_UNQUOTED([PREFIX_DIR], ["`echo $prefix`"], [Installation prefix])
$ ./configure --prefix=/usr/local
/* Installation prefix */ #define PREFIX_DIR "/usr/local"
9.1.2 Checking for headers
You may need to check if some headers are available. For this, you
can use the AC_CHECK_HEADERS
macros:
AC_CHECK_HEADERS ([header-file ...], [action-if-found], [action-if-
not-found], [includes])
For example, one can remark in our example that the
include/Helpers.hpp
requires the mkl_lapacke.h
header which is
not standard. We can introduce a check for it, and force the
failure of configure
if it is not found.
AC_CHECK_HEADERS([mkl_lapacke.h], [],
[AC_MSG_ERROR([Unable to find mkl_lapacke.h])])
If later on in configure.ac
you need to remember if you have or
not the header, the result is stored in ac_cv_header_<header_file>
.
9.1.3 Checking for libraries
As we have seen in Part I, we can look in the archive of Autoconf
macros to see if we find a macro for searching a particular library.
If not, then we can use the AC_SEARCH_LIBS
macro:
AC_SEARCH_LIBS (function, search-libs, [action-if-found], [action-if-not-found], [other-libraries])
function
is a function provided by the library, and search-libs
is a list of libraries that can provide the function we search for
(for example [mpich openmpi]
). other-libraries
contains the
list of other libraries to link in the test that checks if the
requested library is found.
If the library is found, it will be inserted into the LIBS
variable.
If later on in configure.ac
you need to remember if you have or
not the library, the result is stored in ac_cv_search_<function>
.
9.1.4 Checking for external programs
You package might need some other programs to be functional. For
example, you might need a Python interpreter for some scripts.
The presence of other programs can be checked with the
AC_CHECK_PROGS
macro:
AC_CHECK_PROGS(variable, progs-to-check-for, [value-if-not-found], [path = ‘$PATH’])
The following exampl checks for a Python interpreter and saves the
result in the PYTHON
variable:
AC_CHECK_PROGS(PYTHON, [python3 python], [not_found])
9.2 Printing
The following macros will be useful to display messages to the users running configure:
AC_MSG_CHECKING (feature-description) |
Inform the user that configure is checking something |
AC_MSG_RESULT (result-description) |
Return to the user the results of a check |
AC_MSG_ERROR (error-description, [exit-status = $?/1]) |
Make configure fail with an error message |
AC_MSG_NOTICE(message) |
Print the message |
AC_MSG_WARN (problem-description) |
Inform the user of a possible problem |
9.3 Using external software
The usage of an external software can be controlled by the user by
specifying some options to the configure
script:
--with-<package>=<arg>
or --without-<package>
.
Let us now add the possibility to compile our program with or
without MPI. To do that, we start by adding to the configure.ac
file:
AC_ARG_WITH(mpi, [AS_HELP_STRING([--with-mpi=yes/no], [Activate MPI])])
After regenerating the configure
script, we see that now
configure --help
shows the --with-mpi
option.
If the --with-mpi
is absent of the command line, the with_mpi
variable will be empty. Otherwise, it will contain yes
.
AC_ARG_WITH(mpi, [AS_HELP_STRING([--with-mpi], [Activate MPI])]) AS_IF([test "x$with_mpi" == "xyes"], [ AC_DEFINE([HAVE_MPI], [], [MPI is activated]) AC_MSG_NOTICE([MPI is configured]) AC_CHECK_PROGS(MPICC, [mpicc]) AC_CHECK_PROGS(MPICXX, [mpicxx]) CC=$MPICC CXX=$MPICXX ])
10 Autoscan
Autoscan scans the source tree and tries to fix vulnerabilities
encountered in the configure.ac
.
$ autoscan configure.ac: warning: missing AC_CHECK_FUNCS([memset]) wanted by: include/Helpers.hpp:86 configure.ac: warning: missing AC_CHECK_FUNCS([pow]) wanted by: include/Helpers.hpp:148 configure.ac: warning: missing AC_CHECK_FUNCS([sqrt]) wanted by: include/Helpers.hpp:262 configure.ac: warning: missing AC_CHECK_HEADER_STDBOOL wanted by: include/Helpers.hpp:192
Here, Autoscan has detected that we are using the memset
, pow
and sqrt
functions. It is safer to check that these functions are
available and functional. We simply need to follow the instructions
given by Autoscan.
If you do it at the very beginning of your transition to
Autotools, autoscan
will provide you a file named
configure.scan
which is a good starting point for creating the
configure.ac
file.
11 More about maintainer mode
11.1 Automatic maintainer/user switching
It is convenient to define a variable that allows to know if the
configuration should be for a maintainer or for an end user. The
simplest recipe is to check for the existence of the .git
directory at the root of the project. If it exists, we are not in
the source distribution but in a directory obtained by git clone
.
AC_CHECK_FILE([.git], [IS_MAINTAINER="yes"], [IS_MAINTAINER="no"])
Then, the commands specific to maintainer mode can be executed in
configure.ac
by checking the value of this variable:
AS_IF([test "$IS_MAINTAINER" == "yes"], [AC_MSG_NOTICE([Maintainer configuration])], [AC_MSG_NOTICE([User configuration])])
To propagate this knowledge to Makefile.am
, you can use in configure.ac
AM_CONDITIONAL([IS_MAINTAINER], [test "$IS_MAINTAINER" == "yes"])
and then, in Makefile.am
if IS_MAINTAINER [...] endif
11.2 Example: Saving the Git Hash
Inside configure.ac
, it is rather simple to get the git hash and
store it into a defined variable:
GIT_HASH=`git log | head -1 | cut -d ' ' -f 2`
When the distribution package is made, the information relative to the Git repository is not stored in the tarball. So it will not be possible to get the Git hash by executing this command for the end user. The Git hash is available only for maintainers and needs to be stored somewhere in the distribution package to be propagated to the users.
In configure.ac
:
AS_IF([test "$IS_MAINTAINER" = "yes"], [GIT_HASH=`git log | head -1 | cut -d ' ' -f 2 | tee ${srcdir}/.git_hash`], [GIT_HASH=`cat ${srcdir}/.git_hash`]) AC_DEFINE_UNQUOTED(GIT_HASH, ["${GIT_HASH}"], [Git SHA1 Hash])
In maintainer mode, we fetch the Git hash and store it in
.git_hash
. In user mode, we read the content of .git_hash
.
Then, we propagate this variable to config.h
with
AC_DEFINE_UNQUOTED
.
In Makefile.am
, we add the new file to the source distribution tarball:
EXTRA_DIST += .git_hash
and we add rules to always regenerate it in case we make a commit:
if IS_MAINTAINER CLEANFILES = .git_hash .git_hash: FORCE git log | head -1 | cut -d ' ' -f 2 > .git_hash all: .git_hash .PHONY: FORCE endif
12 Useful links
- Autoconf archive of macros
- CUDA and GNU Autotools
- Handling Tools that Produce Many Outputs
- Modern Fortran Makefiles: talks about the problems due to using modules, and proposes a solution for Automatic dependency generation
- fdep : Small set of scripts to teach autoconf/automake (using GNU make) about the additional dependencies in Fortran 90 files due to modules.
- Example: The QMCkl library
- Example: The StarPU library
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.