Creating Your Own Configuration

In Section 3.1 we saw the work that a tiny two-line, seemingly do-nothing configure.in macro script can do when run through the autoconf tool. In this section we'll see more of the power of autoconf unleashed. The autoconf package is distributed with a huge collection of macros, each of which is prefixed with "AC_". These macros expand into shell script code in the configure file, capable of searching the target system for compilers, external libraries, include files, and even specific functions inside libraries and include files. You can place these macros with their respective parameters in configure.in or even write your own macros from scratch if the standard macros won't do the job. It all ends up as a shell script, so your only limitations are what the shell can do.

Autoconf

The autoconf system is the gatekeeper of all your configuration needs. As the name suggests, autoconf performs automated configuration. The interesting thing about autoconf is that it runs on the developer's machine but doesn't do its real magic until its generated scripts run on the end user's machine. It's like a time-released depth charge.

This setup solves many problems for the developer. Most importantly, it reduces the number of software dependencies that the system administrator needs when installing the software. The developer must have autoconf, automake, and possibly libtool on the development machine, but the output of these tools consists for the most part of generic shell scripts. Thus to compile and install software that has been processed by autoconf, all you need is a command line shell (available on every flavor of UNIX in existence), a compiler, and a recent enough version of make.

The configure.in script should always start with the AC_INIT macro and always end with the AC_OUTPUT macro. AC_INIT processes any command line options you might have given while running configure; it also double-checks the compile directory to make sure your build environment is in good shape. If the file you supply as a parameter to AC_INIT does not exist in the source tree, the generated configure script will bail out with an error message. You should always try to use a uniquely named source file for this check so that configure can quickly detect a dubious compilation environ- ment.

The autoconf system handles automatic file generation with the AC_OUTPUT macro. You supply a space-delimited list of target files you want the configure script to create; configure then searches for a file in the same path with ".in" appended to it and uses that as a template for creating the final output file. Thus if you wanted to perform variable substitutions on a shell script template init-app.sh.in in the root of the source tree, and on a Makefile.in in the src subdirectory, you would use the following command:

AC_OUTPUT(init-app.sh src/Makefile)
        

The Configuration Header

As we saw in the previous section, you can pass a value directly to your makefiles with AC_SUBST. This macro will cause autoconf to insert the variable's value into the text of the makefile. You can then use it however you need to in the makefile, including passing it on to the compiler as part of its command line string.

However, if all you want to do is define some values for your code with #define, autoconf has a special macro for the job, AC_DEFINE. Normally autoconf places these values, formatted for your compile line, into a globally available makefile variable, DEFS. Internally, autoconf calls AC_SUBST(DEFS) to imprint it in your makefiles, so you will still need to put a @DEFS@ declaration somewhere in your Makefile.in file in order to use it (unless you use automake, as described in Section 3.3.2). The prototype for AC_DEFINE looks like this:

AC_DEFINE (VARIABLE [, VALUE [, DESCRIPTION]])
        

The VARIABLE parameter gives autoconf the name of the C constant to define; VARIABLE is the only required parameter. VALUE allows you to specify the contents of VARIABLE and defaults to 1 if you omit it. The DESCRIPTION parameter lets you specify a descriptive comment for the #define statement, but it is used only in conjunction with the AC_CONFIG_HEADER macro, as we'll see in a moment.

Try adding the following lines to configure.in somewhere between AC_INIT and AC_OUTPUT:

AC_DEFINE(USE_FOO)
AC_DEFINE(APPNAME, "myapp")
        

Then add this line anywhere in Makefile.in:

MYDEFS = @DEFS@
        

Run autoconf again-because we've modified configure.in-and then run the freshly created configure script to regenerate the makefile. The makefile has gained the line

MYDEFS =  -DUSE_FOO=1 -DAPPNAME=\"myapp\"
        

The autoconf script added the compiler-specific -D options for us and even took the opportunity to tack on "=1" as a default value to the USE_FOO definition. Unlike AC_SUBST, which puts each declaration into a separate variable, all invocations to AC_DEFINE will concatenate their arguments onto the end of the DEFS variable. If you tried to do this with AC_SUBST, you would end up with code like this in the Makefile.in file:

MYDEFS = "-DUSE_FOO=@USE_FOO@ -DAPPNAME=@APPNAME@"
        

A couple of these substitutions aren't too bad, but once you get into a larger, more complex project with 10 or 20 (or more!) declared variables, you'll be glad that you don't have to maintain this all by hand. You just call AC_DEFINE on each one as you go, and autoconf takes care of the rest.

This brings up a small problem with using AC_DEFINE and DEFS: scalability. If you have 10 -D options in every command line, your compile outputs will become voluminous and hard to read. The important stuff will become lost in a sea of compile parameters. To deal with this situation, autoconf has an alternate mode of handling AC_DEFINE that is much more scalable than using DEFS. autoconf can place all your AC_DEFINE declarations into a header file, which you can then add to your source files with the #include directive. You announce which header file you want to use with the AC_CONFIG_HEADER macro. You'll typically want to call this macro right after AC_INIT, as the second macro in your configure.in file.

Of course, it's not quite as easy as all that. autoconf also needs a template file to tell it how to format the configuration header. The configuration header file is usually named config.h, and the template file is usually the configuration file with the familiar ".in" suffix, config.h.in. As far as autoconf is concerned, it is entirely your job to create this header file, although the autoconf distribution comes with a little tool, autoheader, that automatically generates a config.h.in file for you on the basis of the contents of your configure.in file.

The config.h.in file looks like any normal C header file, except it contains only comments and #define statements, according to what you passed into the AC_DEFINE macro (this is where the DESCRIPTION parameter comes into play). All of these #define statements must either set the value to 0 or undefine it completely with #undef. autoconf uses the config.h.in file as a checklist for the various tests it runs. Each time it hits an AC_DEFINE macro, it searches the config.h.in file for a matching #define statement, uncomments it, and sets the value to 1.

As a final step, autoconf puts the value -DHAVE_CONFIG_H into the DEFS variable. You'll still want to pass the value of DEFS to the compiler in your makefile (as @DEFS@ in Makefile.in), and then check for the HAVE_CONFIG_H macro in your source code. At the top of your source files you can do something like this:

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
        

As we'll see in Section 3.3, automake helps make this process even easier.

Checking for Tools

The autoconf package comes bundled with an exhaustive set of macros for probing target systems. Autoconf makes use of m4, a commonly available macro-processing program. As part of its own distribution, autoconf supplies numerous text files (all of which end in ".m4"), full of various m4 macros. The m4 macro language looks a little odd at first, but it can be very powerful when used correctly.

As we've seen, autoconf expands these macros into shell commands in the configure script. Many people use m4 to create text files and perform a wide variety of other tasks. For example, GTK--, a C++ wrapper around GTK+, uses m4 to automate the creation of a great deal of repetitive source code. The UNIX e-mail transport utility sendmail uses m4 to generate its complex configuration files from an easy-to-read template. m4 is a generic tool with many uses.

You can tap into this power by adding the m4 macros distributed with autoconf to your configure.in file. The configure.in file is in fact a mixture of m4 macros and inline shell script code. In addition to the initialization and variable-passing macros we saw in the previous section, autoconf comes with a broad selection of macros for probing standard UNIX tools. They typically don't require parameters. You just add each macro on its own line, without parentheses. If one of the prepackaged macros doesn't do what you want, you can write your own custom macro. Here are some common tool macros, exactly as they would appear in a configure.in file:

AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_YACC
AC_PROG_LN_S
AM_PROG_LIBTOOL
        

Each one checks for the existence of a tool or program, first by seeing if it is explicitly named by the user in an environment variable, and if not, by searching the hard drive for it (i.e., the hard way). For example, AC_PROG_CC first checks to see if the CC environment variable is set on the end user's system. If it is, the configure script adds the contents of CC to the master list of variable substitutions. You can then reference @CC@ in your Makefile.in files, just like the other AC_SUBST variables. If the user hasn't explicitly set the environment variable CC, AC_PROG_CC will search the directories in the PATH environment variable for the gcc or cc executables. If it finds something, it will call AC_SUBST on that value instead. This makes it very easy for the user to override the default compiler before configuring a software package that has been processed by autoconf. If the configure script fails to find any trace of a compiler, it aborts the configuration process with an error message.

The other AC_PROG_* macros work similarly. See the autoconf documentation for an explanation of each one. If you're feeling adventurous, you can explore autoconf's m4 files to find out what's really going on. You can find these files in the autoconf install directory, usually something like /usr/share/autoconf or /usr/lib/autoconf, depending on your distribution of UNIX or Linux. You may also want to consult the m4 documentation. If you plan on someday writing your own macros, this will be time well spent.

Checking for Header Files

A frequent problem with porting software is keeping track of header files. Different systems have headers in different places. If your software depends on other libraries, it should attempt to verify that those libraries and their accompanying header files are installed before continuing with the compilation of your package. For example, if you're compiling a GNOME application, you'll want to make sure the header files for the gnome-libs package are available.

AC_CHECK_HEADER allows you to check for specific header files. You supply the name of the header file, plus an optional command if the header is found and another optional command if the header is not found.

 AC_CHECK_HEADER (HEADER-FILE, [ACTION-IF-FOUND [,
          ACTION-IF-NOT-FOUND]])
        

Here's an example that aborts the configure process if the header file grump.h isn't found. The autoconf system uses the square brackets-[ and ]- as the default quote characters for its m4 macros, rather than the double quotation mark ("), to make it easier to include double quotation marks in your macros. The AC_MSG_ERROR macro that follows prints out a message and then aborts the configure process. If you just want to print out a nonfatal warning or status message and then keep going, you can use the AC_MSG_RESULT macro instead.

AC_CHECK_HEADER(grump.h, , [AC_MSG_ERROR(
[Couldn't find grump.h...try downloading the source from \
http://www.grumpalot.org] )])
        

This macro will spit out an acknowledgment message while configure is running, either an affirmative if it finds the file:

checking for grump.h... yes
        

or a negative, if it can't find the file:

checking for grump.h... no
configure: error: Couldn't find grump.h...try downloading the
source from http://www.grumpalot.org...
        

Several things can cause this check to fail. The header file must first exist; the user must also have permission to read it. To perform the check, the configure script creates a very simple C program that consists more or less of a single #include statement for the supplied header file, in our case grump.h. The configure script then tries to compile it, adding the contents of the CPPFLAGS environment variable to the compile line. Whoever is compiling the software will have to add any include paths to CPPFLAGS that aren't implicitly checked by the compiler. If the header files are installed in a nonstandard location, such as /opt/include, and CPPFLAGS doesn't refer to that directory-for example, as -I/opt/include-the AC_CHECK_HEADER macro will fail, even though the files do exist on the system. However, this is an issue for the system's administrator. Part of the convenience of autoconf is that you, as the developer, don't need to worry about these details.

If you have a lot of header files to check for, or only want to find one out of a list of multiple header files, you can use AC_CHECK_HEADERS. The format for AC_CHECK_HEADERS is the same as with AC_CHECK_HEADER, except that you can list multiple files, separated by spaces, in the first parameter. The following example will search for three header files. It will call AC_DEFINE to add a #define statement to config.h (or DEFS) for the first one it finds. If it doesn't find any of them, it will produce an error message and bail out of the configure script.

AC_CHECK_HEADERS(grump.h grump-linux.h grump-win32.h, break,
AC_MSG_ERROR([Couldn't find grump.h...try downloading the source \
from http://www.grumpalot.org] ))
        

If configure finds grump-linux.h, it will create a #define statement for HAVE_GRUMP_LINUX_H and so on, which you can check for in your code. If you want to check for all the listed header files, simply omit the break command.

Custom Checks

You can also check for the existence of specific programs, libraries, and even single function calls. Let's start with the program macros.

 AC_CHECK_PROG (VARIABLE, PROG-TO-CHECK-FOR, VALUE-IF-FOUND [,
          VALUE-IF-NOT-FOUND [, PATH, [ REJECT ]]])
 AC_CHECK_PROGS (VARIABLE, PROGS-TO-CHECK-FOR [,
          VALUE-IF-NOT-FOUND [, PATH]])
        

Each program check will set an environment variable, VARIABLE, with AC_SUBST, according to whether the program is found (VALUE-IF-FOUND) or not (VALUE-IF-NOT-FOUND). PROG-TO-CHECK-FOR is the file name of the program. You can use the optional parameter PATH to override the PATH environment variable, and REJECT to ignore specific path-program combinations-for example, if you know that more than one program has the same name, blahblah, and that the version you don't want is always at /usr/bin/blahblah.

The AC_CHECK_PROGS macro does pretty much the same thing, except you can provide a list of programs to search for. AC_CHECK_PROGS places the first program it finds into VARIABLE. Here's an example that checks for the fictitious program zed.

AC_CHECK_PROGS(ZED, zed zed2000 zed-x11, "")
AC_CHECK_PROG(USE_ZED_X11, zed-x11, yes, no)
        

First it looks for zed in the normal PATH-first as zed, then as zed2000 and zed-x11. If it finds any of these versions of zed, it sets the ZED variable to that value and uses AC_SUBST to substitute it into the makefile. In the second macro it checks for the existence of zed-x11 and sets the USE_ZED_X11 environment variable to yes if it does find it and no if it doesn't. The result is that $(ZED) will be set to whichever version of zed the user has installed, but if the user happens to have zed-x11 installed, extra support for the X11 version can be conditionally compiled on the target system, according to the value of $USE_ZED_X11. Note that nothing magic happens in your code because of an autoconf macro. You still have to check for the USE_ZED_X11 in the makefile and add the proper conditional blocks to enable or disable the extra support. In Section 3.3.4 we'll see how to do this with the AM_CONDITIONAL macro.

When checking for libraries, you give the name of the library and an important function name within that library. By allowing you to name a specific function to search for, autoconf lets you check for specific functionality in a library. Often many similar implementations are floating around for the same li- brary. A good example is the standard C library. On different platforms, libc may or may not contain certain common functions, like vsnprintf( ) and strcasecmp( ). The version number of the library isn't much help because there's no universal correspondence between the version number and the functions a library contains. Version 2.1 of libc on platform A might have vsnprintf( ), while version 5.0 of libc on platform B might not. Version number checks would quickly deteriorate into a swampy maze of special cases, and they require intricate knowledge of the histories of each platform. It's so much easier just to check for the function directly.

The AC_CHECK_LIB macro uses a technique similar to that of AC_CHECK_HEADER. AC_CHECK_LIB creates and compiles a simple temporary program that calls the function, passing the library in on the link line. If the program compiles successfully, AC_CHECK_LIB knows that the function exists in that library. If compilation fails, it knows that the function doesn't exist there, even though it might be somewhere else on the system. It's possible to nest successive calls to AC_CHECK_LIB, so you can specify alternate libraries to search for the function if the first try fails (stay tuned for more on that feature).

AC_CHECK_LIB (LIBRARY, FUNCTION [, ACTION-IF-FOUND [,
          ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]])
        

LIBRARY is the base name of the library; if you were checking for libgrump.so, you would pass in grump. The following macro would check for the grump_some( ) function in libgrump.so or libgrump.a:

AC_CHECK_LIB(grump, grump_some)
        

The default action is to add the library to the LIBS variable and create a special #define statement for it with AC_DEFINE. In our grump example, AC_CHECK_LIB would add -lgrump to LIBS and the #define statement HAVE_LIBGRUMP to config.h if it found libgrump.so or libgrump.a. If the li- brary you're searching has additional library dependencies, you may have to add those as the OTHER-LIBRARIES parameter. If libgrump.so required libcrypt, you would do this instead:

AC_CHECK_LIB(grump, grump_some, , , -lcrypt)
        

AC_CHECK_LIB will automatically create the #define HAVE_LIBxxx statement for you to use in your source code, but if you need to perform any special custom actions at configure time, you can add shell script code or other m4 macros to the ACTION-IF-FOUND and ACTION-IF-NOT-FOUND parameters. The NOT-FOUND parameter is a good place to perform nested library checks. In our example, if the grump_some( ) function were instead located in the libgrumpus library on certain platforms, you could invoke a fallback check for it like this:

AC_CHECK_LIB(grump, grump_some, ,
    [ AC_CHECK_LIB(grumpus, grump_some) ],
    -lcrypt)
        

If you need a specific function and don't care which library it resides in, you can check for it with the AC_CHECK_FUNCS macro. Simply provide a list of function names, and for each one it finds, AC_CHECK_FUNCS will define HAVE_FUNCTION.

AC_CHECK_FUNCS (FUNCTION... [, ACTION-IF-FOUND [,
          ACTION-IF-NOT-FOUND]])
        

The following call to AC_CHECK_FUNCS will potentially result in one or more of the following define statements: HAVE_VPRINTF, HAVE_SETENV, and HAVE_STRCHR.

AC_CHECK_FUNCS(vprintf setenv strchr)
        

Helper Tools

Figuring out which macros to include in your configure.in file can be a Herculean task. Without broad experience in porting and cross-compiling, you'll probably end up guessing which functions and libraries you need to test for. Do all platforms have native support for inline functions? Should you bother checking for vsnprintf( )? Who knows?

The autoconf package has a helper utility called autoscan that snoops through your source files looking for common portability problems. It generates a fully formed-but typically quite lean-configure.in file for you. By default it saves this file as configure.scan. You'll still have to do some work to customize the configure.scan file.

Another helpful tool is ifnames, a utility to extract and sort all the preprocessor define statements in your source files. You invoke ifnames with a list of file names (you can also pipe the files to ifnames through stdin), and it spits back a sorted list of all the #if, #ifdef, #elif, and #ifndef statements it finds. This is especially useful if you are adding autoconf support to an existing software package.

If you are using a config.h file, you may want to try out the autoheader tool. autoheader automatically creates the config.h.in file for you on the basis of the macros you call in configure.in. In particular, autoheader looks for any calls to AC_DEFINE, plus any HAVE_* definitions created by the AC_CHECK_* macros. These preprocessor statements must be undefined (by #undef) in a file called acconfig.h (unless you specify the value in the optional second parameter to AC_DEFINE). The undefinitions are inserted into config.h to protect autoconf's declarations from declarations that might coincidentally occur in external header files. Autoconf comes with an acconfig.h of its own that supplies most of the common #undef statements you'll need. If you create new ones, you have to add them to a new acconfig.h file in your source directory. autoheader combines both of these when it creates config.h.in. This tool can save you quite a lot of work in a large project.