w w w . a q u a m e n t u s . c o m
  What is autotools?
    Coding Standards
  Makefile.am
    Primaries/patterns
  configure.ac
    AC_INIT
    AM_INIT_AUTOMAKE
    AC_PROG_*
    AC_CONFIG_FILES
    AC_OUTPUT
  Specific examples
    Case 1a: one .c file
    Case 1b: one .c, with internal .h
    Case 2a: one .c file, in src/
    Case 2c: src/.c file with include/.h file
    Case 3: one .c file, and a README to install
    Case 4: one .c file, and a .h you want to export
    Case 5: one .c file, one man file
    Case 6: one .c file, one .texi file
    Case 7: multiple .c files
    Case 8: one .c file, and generate config.h
    Case 9a: one .c file, becoming an exported static lib
    Case 9b: one .c file, becoming an unexported static lib
    Case 9c: one .c file, becoming an exported static lib used internally
    Case 9a-2: one .c file, with exported static lib, but using libtool
    Case 10a: one .c file that turns into an exported dynamic lib
    Case 10b: one .c file becoming an unexported dynamic lib
    Case 10c: one .c file, becoming an exported dynamic lib used internally
    Case 11a: one .c, using an external .a
    Case 11b: one .c, using an external .so or .dylib

What is autotools?

Autotools is a collection of programs that help package up source code for distribution and deployment. Historically, it's most relevant for compiled languages (C and C++), since compilation is highly susceptible to platform issues. (For completeness, it directly supports a few other languages/tools: objective-C, objective-C++, fortran77, fortran90, yacc, lex..probably others.)

For purposes of this tutorial, I'm trying to get the most number of consumers running with the least amount of drink-from-the-firehose. Thus, this is targeted for C or C++ projects that don't use lex/yacc.

Autotools is really a trio of tools:

One question I haven't been able to answer is whether these are needed at all for POSIX systems, since the point of defining standards is to eliminate hacks for differences.

Since we're using something to generate the Makefile we usually manage by hand, that means there are new ways to do all the things you're used to do doing, such as:

Your original workflow would go something like this:

  1. edit Makefile
  2. edit source
  3. "make"
  4. if program problem, go to [2]
  5. if build problem, go to [1], then [3]

The autotools workflow is a little different:

  1. edit Makefile.am and/or configure.ac
  2. run "autoreconf --install"
  3. run "./configure"
  4. edit source
  5. "make"
  6. if program problem, go to [4]
  7. if build problem, go to [1], [2], [3], [5]

When all your code is ready to go, you can package it up like so:

  1. make dist # generates a $pkg.$ver.tar.gz file
  2. make distcheck # unpacks/builds the $pkg.$ver.tar.gz to make sure it's okay

Then your users can download it and install as usual like so:

  1. tar xf $pkg.$ver.tar.gz
  2. cd $pkg.$ver
  3. ./configure
  4. make
  5. make check
  6. make install

SourceForge has a pretty good tutorial here.

Coding Standards

It's worth mentioning the GNU coding standards because autotools tries valiantly to enforce them. At 85 pages, the Coding Standards are not a quick read, but there's some interesting stuff in there.

Here are the highlights you might want to know about for your autotools project.

First, put your code in a src/ directory, and your headers in an include/ directory. As your project gets more productized, you may also want to create man/, data/, info/, and who knows what else. Starting off with these functional directories is the first step to keeping your code manageable as it scales.

Second, since GNU packages are handed off to complete strangers who don't give one whit about internals, there is a distinct interface defined for packages. This interface makes GNU packages consistent to users, which -- as a user yourself -- I'm sure you can appreciate. To that end, the following additional files are expected (though not required):

Makefile.am

The first of two very important files, the Makefile.am is where you'll define what targets to make and what goes into them. This not a generic makefile, but one that expects targets with specific naming conventions in order to accomplish specific things.

(The ".am" suffix is for "automake", by the way.)

You'll have one Makefile.am in each directory to build, plus one at the top level. To follow standards, that means at least ./Makefile.am and src/Makefile.am. To say what the build directories are, the one at the top-level will contain:

SUBDIRS = src

Primaries/patterns

For whatever reason, automake uses the word "primaries" for what the rest of the world calls "makefile patterns", so when you read the documentation, that's what that means. Here are some of the suffix-type patterns/primaries you can use:

I've also seen .*_CPPFLAGS and .*_LDADD.

In addition, there are some prefix-type patterns:

Anything not matched by the Makefile.am patterns is considered a sort of identifier for the actual target. Here's an example of us coming up with a custom 'xml' target for some package:

xmldir = $(datadir)/xml
dist_xml_DATA = foo.xml   # foo.xml now installed to $(datadir)/xml

A very common identifier you'll see is bin, which semantically means the top-level program(s).

Because of the reliance on predictable patterns, the list of characters allowed in your file names is really strict. Anything in your actual target names that isn't a letter, number, or "@" will have to be converted to an "_".

Note: if you want to set compiler flags, set AM_CFLAGS or AM_CXXFLAGS. They will (eventually) get added to the real CFLAGS.

configure.ac

The configure.ac file is just a collection of function calls (which are executed via expansion by m4). These function calls usually begin with one of two things: "AC_" means it's an autoconf macro, and "AM_" means it's an automake macro.

As you've probably guessed, the ".ac" extension is for "autoconf".

Here are some of the more common functions you'll encounter/use.

AC_INIT

The AC_INIT function declares information about the package. It has three parameters:

  1. the name of the package
  2. the version of the package
  3. an email address where users can get ahold of you.

The version should adhere to common standards.

AC_INIT([foo], [0.1.0], [foo@bar.com])

AM_INIT_AUTOMAKE

This function fills in some control points for the Makefile that will eventually be generated. It takes one parameter, which itself is a space-separated list of random crap. Some of the random crap is flags to give to the compiler, and some of it is keywords that automake recognizes. In particular, you might want to specify foreign, which lifts the requirement of defining all the files needed to make the package adhere to GNU Coding Standards.

AM_INIT_AUTOMAKE([-Wall -Werror foreign])

AC_PROG_*

This function tells autoconf to check and make sure that a particular program exists on the target machine. This is usually used to make sure the compiler's found.

AC_PROG_CC

AC_CONFIG_FILES

This function takes a single parameter, which is a space-separated list of files to generate. This is where to declare that Makefile is one of the things you'd like to make.

AC_CONFIG_FILES([Makefile])

AC_OUTPUT

This very important function tells autoconf to actually generate the files. It's also very important is to call this last, after everything else has been set up or declared.

AC_OUTPUT

Specific examples

The objective of these examples is to show you a minimal working example, and then a bunch of the common extensions to that baseline. The minimal example is a project consisting entirely of a single .c file; some of the common extensions then are things like:

Case 1a: one .c file

This is the minimal project that we can send through autotools, so it's basically the "hello, world" example. The .c file is in the current dir. It requires no headers (either internal or external), does not generate a lib, does not use any external packages, and does not need the config.h.

  1. create your foo.c
  2. create your Makefile.am:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    
  3. create your configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo". omg it works!
  8. run "make dist". It creates foo-0.1.0.tar.gz which you can then publish.

Case 1b: one .c, with internal .h

This is the same as Case 1 except we also have a header file (which we don't want to export). Since we're not exporting it, and compilers don't need to be told to look in the current directory for headers, there's actually nothing special we need to tell autotools. Thus, this is the same as the above.

Case 2a: one .c file, in src/

This is pretty much the same as above, except it puts the code into the src dir like GNU asks.

  1. create your src/foo.c
  2. create the src/Makefile.am:
    bin_PROGRAMS = foo
    foo_SOURCES = foo.c
    
  3. create the ./Makefile.am:
    SUBDIRS = src
    
  4. create the configure.ac. It's almost the same as the above, except add "src/Makefile" to the AC_CONFIG_FILES call:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile src/Makefile])
    AC_OUTPUT
    
  5. run "autoreconf --install"
  6. run "./configure"
  7. run "make"
  8. run "./foo"
  9. run "make dist"

Case 2c: src/.c file with include/.h file

This shows you how to inject a local -I into your generated makefile.

  1. create src/foo.c and include/foo.h.
  2. create the src/Makefile.am as above in [2a], but also add AM_CPPFLAGS:
    bin_PROGRAMS = foo
    foo_SOURCES = foo.c
    
  3. create the ./Makefile.am:
    SUBDIRS = src
    AM_CPPFLAGS = -I$(srcdir)/../include
    
  4. create the configure.ac. Same as [2a]:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile src/Makefile])
    AC_OUTPUT
    
  5. run "autoreconf --install"
  6. run "./configure"
  7. run "make"
  8. run "./foo"
  9. run "make dist"

Case 3: one .c file, and a README to install

  1. create your foo.c and your README
  2. create the Makefile.am. It's almost the same as [case 1], except you add dist_doc_DATA:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    dist_doc_DATA = README  # list of files to install to /usr/local/share/doc/foo/
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 4: one .c file, and a .h you want to export

Note that exporting a .h means installing to /usr/local/include

  1. create your foo.c and foo.h
  2. create the Makefile.am. It's almost the same as [case 1], except you add include_HEADERS:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    include_HEADERS = foo.h
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Just to warn about a pitfall: make install copies the header files to $(includedir), which means any local subdirs you're using are essentially removed. If that's bad, use nobase_include_HEADERS instead to preserve the relative directories on install.

Case 5: one .c file, one man file

  1. create your foo.c and foo.2 (or whatever section you want it in)
  2. create the Makefile.am. It's almost the same as [case 1], except you add man_MANS:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    man_MANS = foo.2  # list of files to install to /usr/local/share/man/man(#section)
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Another pitfall warning: man files are not considered 'source' (because they're commonly autogenerated), so they're not included in 'make dist'! If your man page actually is source, use dist_man_MANS instead to force inclusion in 'make dist')

Case 6: one .c file, one .texi file

.texi files are used by the info program, which some rather vocal people think are better than man pages.

  1. create your foo.c and foo.texi
  2. create the Makefile.am. It's almost the same as [case 1], except you add info_TEXINFOS:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    info_TEXINFOS = foo.texi  # produces foo.info, includes it in 'make dist' and installs it to /usr/local/share/info
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 7: multiple .c files

  1. create your foo.c and bar.c files
  2. create the Makefile.am. It's almost the same as [case1], except you set the _SOURCES field to the complete list of your .c files:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c bar.c
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 8: one .c file, and generate config.h

The config.h file contains a bunch of #defines that record lots of configuration info that was detected when the user ran ./configure. For example, it has the version number that you declared in configure.ac, so if you wanted your help message to print the version, you could get it from there instead of hardcoding it in multiple places.

  1. create a foo.c that #include's "config.h"
  2. create the Makefile.am as usual:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    
  3. create the configure.ac as usual, but add AC_CONFIG_HEADERS somewhere before the AC_OUTPUT:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_CONFIG_HEADERS([config.h])         # make it generate the config.h
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 9a: one .c file, becoming an exported static lib

  1. create foo.c as usual
  2. create the Makefile.am for the lib build and export:
    lib_LIBRARIES = libfoo.a
    libfoo_a_SOURCES = foo.c
    
  3. create the configure.ac as usual, but add AC_PROG_RANLIB and AM_PROG_AR somewhere before the AC_OUTPUT:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_PROG_RANLIB                   # check for ranlib
    AM_PROG_AR                       # check for ar
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 9b: one .c file, becoming an unexported static lib

This obviously only makes sense if the unexported static lib is used by some second .c file in this package.

  1. create bar.c to become libbar.a, and foo.c that uses libbar.a
  2. create the Makefile.am:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    noinst_LIBRARIES = libbar.a      # declare need to build libbar.a, without installing
    libbar_a_SOURCES = bar.c         # how to build bar.a
    foo_LDADD = libbar.a             # declare that foo needs libbar.a
    
  3. create the configure.ac just like in [case 9a]:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_PROG_RANLIB                   # check for ranlib
    AM_PROG_AR                       # check for ar
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 9c: one .c file, becoming an exported static lib used internally

  1. create bar.c to become libbar.a, and foo.c that uses libbar.a
  2. create the Makefile.am:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    lib_LIBRARIES = libbar.a         # declare need to build libbar.a, installing to $libdir
    libbar_a_SOURCES = bar.c         # how to build bar.a
    foo_LDADD = libbar.a             # declare that foo needs libbar.a
    
    3. create the configure.ac just like in [case 9a]:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_PROG_RANLIB                   # check for ranlib
    AM_PROG_AR                       # check for ar
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  3. run "autoreconf --install"
  4. run "./configure"
  5. run "make"
  6. run "./foo"
  7. run "make dist"

Note: it's possible all the static libs can be done by the libtool mechanism, making all the case 9's a subset of the case 10's. In fact, let's do [9a] over again, but using libtool:

Case 9a-2: one .c file, with exported static lib, but using libtool

  1. create foo.c as usual
  2. create the Makefile.am like so:
    lib_LIBRARIES = libfoo.a
    libfoo_a_SOURCES = foo.c
    ACLOCAL_AMFLAGS = -I m4
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_PROG_AR
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AM_PROG_LIBTOOL
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_CONFIG_MACRO_DIR([m4])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Yes, even though libtool is meant to replace ar and ranlib, you still need AM_PROG_AR. And, bizarrely, they want it before AM_INIT_AUTOMAKE.

Case 10a: one .c file that turns into an exported dynamic lib

  1. create foo.c as usual
  2. create the Makefile.am:
    lib_LTLIBRARIES = libfoo.la
    libfoo_la_SOURCES = foo.c
    ACLOCAL_AMFLAGS = -I m4
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_PROG_AR
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AM_PROG_LIBTOOL
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_CONFIG_MACRO_DIR([m4])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 10b: one .c file becoming an unexported dynamic lib

Again, this only makes sense if there's a second .c file that's using the dynamic lib. However, this particular situation is highly inadvisable because if you don't export the dynamic lib, then the exported binary won't be able to run. So, I'm not even going to show you how to do it.

Case 10c: one .c file, becoming an exported dynamic lib used internally

  1. create foo.c to become libfoo.so, and bar.c that uses libfoo.so
  2. create the Makefile.am like so:
    bin_PROGRAMS = bar
    bar_SOURCES = bar.c
    bar_LDADD = libfoo.la
    lib_LTLIBRARIES = libfoo.la
    libfoo_la_SOURCES = foo.c
    ACLOCAL_AMFLAGS = -I m4
    
  3. create the configure.ac like so:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_PROG_AR
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AM_PROG_LIBTOOL
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_CONFIG_MACRO_DIR([m4])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Case 11a: one .c, using an external .a

  1. create foo.c as usual
  2. create the Makefile.am as usual, but add an _LDADD:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    foo_LDADD = /usr/local/lib/libsomething.a
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"

Note that you specify the full path to the .a file.

Case 11b: one .c, using an external .so or .dylib

  1. create foo.c as usual
  2. create the Makefile.am as usual, but add an _LDADD:
    bin_PROGRAMS = foo   # list of programs to install to /usr/local/bin/
    foo_SOURCES = foo.c
    foo_LDADD = -lsomething
    
    Note that you could specify -L yourself:
    ..
    foo_LDADD = -L/usr/local/lib/ -lsomething
    
  3. create the configure.ac:
    AC_INIT([foo], [0.1.0], [foo@bar.com])
    AM_INIT_AUTOMAKE([-Wall -Werror foreign])
    AC_PROG_CC
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    
  4. run "autoreconf --install"
  5. run "./configure"
  6. run "make"
  7. run "./foo"
  8. run "make dist"


Chris verBurg
2015-03-08