This is the second post of the autoconf tutorial series. In this post I’ll cover some fundamental units in autoconf and automake, and an example cross platform X11 program that uses the concepts in this post. After reading this post, you should be able to write your own build script for small scope projects.
Autoconf is part of the GNU Autotools build system. Autotools is a collection of three main packages: autoconf, automake, and libtools. Each of the package has smaller sub-packages including autoheader, aclocal, autoscan etc. I won’t cover the details of all the packages; instead I’ll only focus on how autoconf plays its role in the build chain.
Autoconf is mainly used to generate the
configure is a
shell script that detects the build environment, and output proper build
flags to the Makefile, and preprocessor macros (like
config.h. However, writing a good portable, extensible shell script isn’t
easy. This is where the gnu m4 macro comes in. Gnu m4 macro is an
implementation of the traditional UNIX macro processor. By using m4, you can
easily create portable shell script, include different pre-defined macros, and
define your own extensions easily.
In short, autoconf syntax is shell script wrapped by gnu m4 macro.
In the early days, writing portable shell scripts wasn’t that easy. For example
not all the
-p option, not all the shells are bash
compatible, etc. Using the m4 macro to perform the regular shell logics, like
AS_IF instead if
if [[ ]]; then...,
AS_MKDIR_P instead of
AS_CASE instead of
case ... esac makes your configure script works better on
all unix/unix-like environment, and more conventional. Most of the time you’ll
be using macros instead of bare bone shell script, but keep in mind that behind
the scene your final output is still shell script.
M4 Macro Basics
Though the first look at M4 macros is very alien and unfriendly, but it only consist two basic concepts:
- Macro expansion
You can define a macro like so:
1 2 3 4 5 6 7
It’s pretty much similar to C macro or Lisp macro. The macro expands at compile
configure). You can define a macro
expands to a snippet of shell script. Here we just expands it to
doesn’t have any meaning in shell script and can trigger an error.
Every symbol in your script is expandable. For example if you simply write
in your script, is it a shell symbol, or is it a m4 symbol that needs to expand?
The m4 system uses quoting to differentiate the two. The default quoting in
autoconf is square brackets
]. Though you can change it, but it is highly
Why does it matter? Consider these two examples
1 2 3 4 5 6 7 8 9
This is the base of all m4 macros. To recap, always quote the arguments for the macros, including symbols, expressions, or body statements. (I skipped some edge cases that requires double quoting or escapes, for the curious please check the autoconf language).
Now we know the basic syntax of m4, let’s see what are the functions it
provides. In the configure script, if you invoke
echo directly the output
would be redirected to different places. The convention to print message in
autoconf, is to use
AC_MSG_* macros. Here are the two macros that is most
1 2 3 4 5
For the more curious, check the Printing Messages section in autoconf manual.
To write an if condition in autoconf, simply invoke
AS_IF(test-1, [run-if-true-1], ..., [run-if-false]).
The best way to see how it works is by looking an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Note that we don’t use common shell test operator
]], instead we use
test because the square bracket is preserved for macro expansion. The
recommended way to invoke test is
test "X$variable" = "Xvalue". This is how we
avoid null cases of the shell variable.
Another common branching function is
AS_CASE(word, [pattern1], [if-matched1], ..., [default])
the logic is pretty much the same.
That all the basics we need to know for autoconf, let’s take a break and switch to automake.
Like autoconf, automake is additional semantics on top of another existing language – the Makefile syntax. Unlike autoconf, it’s not using m4 to extend the syntax. It uses a naming convention that converts to the actual logic. Most of the time, we only need to use the following two rules, which we’ll discuss in detail.
where_PRIMARY = targets
target_SECONDARY = inputs
where_PRIMARY = targets
This syntax has three parts,
PRIMARY, and where to install
where. Some examples shown as below:
1 2 3 4 5
targets is a list of targets with the type
PRIMARY. Depending on what
PRIMARY is, it can be a program, a library, a shell script, or whatever
PRIMARY supports. The current primary names are “PROGRAMS”, “LIBRARIES”,
“LTLIBRARIES”, “LISP”, “PYTHON”, “JAVA”, “SCRIPTS”, “DATA”, “HEADERS”, “MANS”,
There are three possible type of variables you can put into the
GNU standard directory variables (bindir, sbindir, includedir, etc.) omitting the suffix “dir”. See GNU Coding Standard - Directory Variables for list of predefined directories. Automake extends this list with
pkglibexecdirAutomake will check if your target is valid to install the directory you specified.
Self-defined directories. You can hack around automake default type check by defining your own directories. Do not do this unless you have a good reason!
1 2 3 4 5 6
- Special prefixes
noinst_indicates the targets that you don’t want to install;
check_is used for unit tests. For the others are less common, please check the automake manual for detail.
target_SECONDARY = inputs
Depending on what your
PRIMARY type is, there are different
you can use for further logic. The common
SECONDARY types are
_SOURCESdefines the source for primary type
_LDFLAGS, etc. compiler flags used for primary type
Note that the invalid character in
target name will get substituted with
underscore. The following example illustrate all the above:
1 2 3 4
The example above requires libtool. You need to declare
AC_PROG_LIBTOOL in your
configure.ac for it to work.
Wraps it up - A X11 example program
With everything we learnt so far, let’s write a more complicated autoconf
program. This is a very simple X11 program that aims to be portable on all
existing platforms with valid X11 installed. To test if X11 is installed, we use
AC_PATH_XTRA, the manual for this macro is defined in
autoconf existing test for system services.
The manual says: An enhanced version of
AC_PATH_X. It adds the C compiler flags
that X needs to output variable
X_CFLAGS, and the X linker flags to
X_DISPLAY_MISSING if X is not available. And in the
states “If this method fails to find the X Window System … set the shell
variable no_x to ‘yes’; otherwise set it to the empty string”. We can use the
logic and write our
configure.ac script as following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Note that the
AC_PATH_XTRA export variables
X_LIBS. To use
these variables in
Makefile.am, just surround it with
1 2 3 4 5 6 7
That all we need to build a platform independent X11 program! Check the full source on github. The X11 example program was written by Brian Hammond 2/9/96. He generously released this to public for any use.
This program can easily work on Linux. I’ll use OSX as an example of how cross platform works. Before you run the example, make sure you have XQuartz installed.
1 2 3 4 5
--x-libraries to proper directory if you
installed the xquartz to a different location.
I only introduced very little syntax for autoconf (if-else, print message) and
automake (primary/secondary rules, use of export variables by
@). But just
using these basic component is already very sufficient for writing conventional
build scripts. How to do it? Check the [existing tests provided by
autoconf][exsisting test]. Here are some of the most commonly used existing
- Library checks:
AC_SEARCH_LIBS. library documentation.
- Header checks:
AC_CHECK_HEADER[S]. header documentation.
- Compiler characteristics.
For the checks that are not included in the default autoconf package, it probably exists in the extended package autoconf archive, which I’ll cover in the next post.