DEV Community

Paul J. Lucas
Paul J. Lucas

Posted on

Autotools Introduction

Introduction

Unlike some modern languages, C doesn’t have a prescribed way to build programs from multiple source files. There’s only cc (or gcc or clang), i.e., just the compiler. Yes, you can do something like:

$ cc -o my_prog *.c
Enter fullscreen mode Exit fullscreen mode

to compile all .c files and it will work, but it doesn’t scale with the number of files since such a command will recompile all source files whether they need it or not. To build non-trivial programs, you need something better and that works across various Unix platforms.

Make

Almost as old as C is a separate program Make that can be used to specify, via a “makefile,” one or more targets (the things you want built), a set of dependencies (the set of files needed to build the targets), and a set of rules (the command(s) needed to transform the dependencies into the targets).

For example, though overkill for a single-file program such as “hello, world,” a minimal makefile would be:

hello: hello.o
        $(CC) -o $@ $<
Enter fullscreen mode Exit fullscreen mode

that says:

  • The target hello (before the :) depends upon hello.o.
  • The rule to convert one or more .o files into an executable is given on the next line.

where:

  • For any variable x, $(x) means expand its value. (Note that this is different from shell variable expansion of either $x or ${x}. A common mistake is to forget the ().) Make has a set of built-in variables: CC is the name of the C compiler (defaults to cc).
  • The special variable $@ means “the current target” (in this case hello). (I remember it because @ kind of looks like a bullseye.)
  • The special variable $< means “all the dependencies.” (I remember it because < is like an arrow feeding the dependencies into the target to the left.)

Note that Make is fussy about tabs vs. spaces: the leading whitespace on the $(CC) line must be a tab.

Make by default looks for a file named either makefile or Makefile in the current directory. Given one, all you need to do is type make and it will do the rest printing the commands it executes on your behalf:

$ make
cc -c -o hello.c hello.c
cc -o hello hello.o
Enter fullscreen mode Exit fullscreen mode

Make also has many built-in rules such as knowing how to convert a .c file into a .o file — which explains the first line of output; the second line is the result of your explicit rule.

In addition to knowing how to build targets and their dependencies, Make also knows when to (re)build only the dependencies that need to be, i.e., if at least one dependency’s file modification date is later than the target’s, rebuild the target. In this example, if hello.c is modified, Make can see that it’s later than hello.o, so it rebuilds it. Subsequently, Make sees that hello.o is later than hello, so it rebuilds it as well.

While Make by itself is a huge leap forward, it’s can become problematic for a couple of reasons:

  1. Maintaining makefiles by hand is tedious and error-prone.
  2. Makefiles are static, that is they can’t easily handle idiosyncrasies of various Unix platforms without hard-coding all possibilities in advance — assuming you want your program to compile on all of Linux, FreeBSD, OpenBSD, macOS, Solaris, AIX, HP-UX, Cygwin, and others.

One of the first build systems to address these issues is GNU Autotools. Autotools is widely used for open-source software. Using Autotools is what this series of articles will be about.

There are newer alternatives such as CMake and Meson.

Autotools Introduction

A minimal set of files for our “hello, world” program to use Autotools would be:

configure.ac
Makefile.am
src/
    Makefile.am
    hello.c
Enter fullscreen mode Exit fullscreen mode

Autotools is actually a set of tools, the primary ones being autoconf and automake. The configure.ac file is the input to autoconf that will generate a cross-platform configure executable shell script. When run, it will “probe” the various idiosyncrasies of the Unix platform it finds itself on and, using automake with Makefile.am files as input, generate a set of cross-platform makefiles. If you’ve ever compiled and installed most any open-source software, you’ve most likely typed the commands:

$ ./configure && make && make install
Enter fullscreen mode Exit fullscreen mode

and it all just works.

configure.ac

A minimal configure.ac file would be:

# Initialize.
AC_PREREQ([2.69])
AC_INIT([hello], [1.0],
  [https://github.com/paul-j-lucas/hello/issues],
  [],
  [https://github.com/paul-j-lucas/hello]
)
AM_INIT_AUTOMAKE([foreign])

# Checks for programs.
AC_LANG(C)
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S

# Generate files.
AC_CONFIG_FILES([
  Makefile
  src/Makefile
])
AC_OUTPUT
Enter fullscreen mode Exit fullscreen mode

where:

  • AC_PREREQ specifies the minimum required version of Autotools your input files require.
  • AC_INIT gives the name of the project, its version number, its issue-reporting URL (if any), tarball name (if different from the project name), and the project’s home page URL.
  • AM_INIT_AUTOMAKE initializes Automake. By default, Autotools requires a specific set of files required by GNU conventions, namely AUTHORS, ChangeLog, NEWS, and README. (We’ll get to these later.) However, if you don’t want to include such files, you can specify that your program’s source is “foreign” (as opposed to being “native” GNU).

Autotools uses m4, a general purpose macro processor. (It’s almost as old as C and you can think of it like the C preprocessor’s big brother in that it’s more fully featured.) In m4, brackets [] are used to enclose arguments that contain whitespace (but can always be used even when arguments don’t contain whitespace) similar to the way most other languages use "" (double quotes) to enclose strings.

  • AC_LANG(C) sets the program’s primary language to C. (While Autotools can be used to build most any software written in any language, they’re not surprisingly optimized for C.)
  • AC_PROG_CC figures out what the local C compiler is called be it cc, gcc, or something else.
  • AC_PROG_INSTALL and AC_PROG_LN check for install and ln that are used for program installation.

Finally:

  • AC_CONFIG_FILES lists all the makefiles that should be created from their respective Makefile.am files (that we’ll get to shortly).
  • AC_OUTPUT actually generates the files.

Makefile.am

The top-level Makefile.am is just:

SUBDIRS = src
Enter fullscreen mode Exit fullscreen mode

where SUBDIRS is a space-separated list of subdirectories into which Autotools should recurse looking for more Makefile.am files.

The src/Makefile.am file is just:

bin_PROGRAMS = hello
hello_SOURCES = hello.c
Enter fullscreen mode Exit fullscreen mode

where:

  • bin_PROGRAMS is a space-separated list of binary executables to be built.
  • hello_SOURCES is a space-separated list of all the source files the hello executable depends upon. In general, for every executable x in bin_PROGRAMS, there needs to be an x_SOURCES.

Configuration & Building

To have Autotools generate configure and the makefiles, do:

$ autoreconf -i
Enter fullscreen mode Exit fullscreen mode

where the -i option copies auxiliary build files it needs to the current directory. I also always include the -f (force) option to force Autotools to regenerate all files since the only time you need to do this is if you’ve made changes to either configure.ac or any of the Makefile.am files.

If everything works, Autotools will have generated configure. At this point you can do:

$ ./configure && make
Enter fullscreen mode Exit fullscreen mode

where configure will probe the system and generate the makefiles, and make will build the program.

Though not required, conventionally, open-source programs include an executable shell script named either bootstrap or autogen.sh that minimally contains:

#! /bin/sh
autoreconf -fi
Enter fullscreen mode Exit fullscreen mode

Then the entire build steps from scratch would be:

$ ./bootstrap && ./configure && make
Enter fullscreen mode Exit fullscreen mode

Personally, I embellish my version of bootstrap a bit.

Other Files

As mentioned, GNU conventions are that the following files should exist:

  • AUTHORS: A plain-text file containing a list of authors and their contributions. For example, this trivial one or this more involved one.
  • ChangeLog: A plain text file containing a pretty-printed version of your version control system’s log, e.g., git log. For example, this one.
  • COPYING: A plain text file containing the software license, such as the GNU General Public License. For example, this one.
  • NEWS: A plain text file containing the user-visible subset of changes. For example, this one.
  • One of README, README.txt, or README.md (Markdown): Contains a general description of the software. For example, this one.

If you provide all of these files, then you can delete the foreign parameter and just do:

AM_INIT_AUTOMAKE
Enter fullscreen mode Exit fullscreen mode

Autotools and Version Control

Autotools generates a bunch of files in addition to those mentioned here. You generally don’t need to care about their contents or purpose. The only reason you care is to tell your version control system to ignore them. For example, a top-level .gitignore file should include at least this:

*.a
*.core
*.gz
*.in
*.log
*.o
*.tmp
*.trs
*~
.deps/
.DS_Store
Makefile
tags

.*.s[a-v][a-z]
.*.sw[a-p]

/*.[ch]
/aclocal.m4
/autom4te.cache/
/compile
/config.*
/configure
/conftest*
/depcomp
/INSTALL
/install-sh
/missing
/test-driver
Enter fullscreen mode Exit fullscreen mode

A src-level .gitignore file should include at least this:

*.dSYM
/config.h
/hello
/stamp-h1
Enter fullscreen mode Exit fullscreen mode

Note that you should replace hello with the name of the target(s) for your software.

Conclusion

Autotools provides a better way to build software (specifically C and C++ programs) in terms of both dependency management and being cross-platform.

Future articles will show additional feature of Autotools including compiling with optional third-party packages, enabling or disabling program features, adding unit tests and test suites, enabling code coverage, and more.

Top comments (0)