Autoconf

The Problem

A Solution

An obvious solution would be to have a configure script edit the Makefile. The problem with this is that writing such a script by hand would be very time consuming. autoconf solves the problem by providing a framework for generating such scripts.

What autoconf does

autoconf uses a file that you create, called configure.in, and generates a configure script. A related program, autoheader creates a config.h.in file from configure.in The configure script has a substantial amount of functionality even if configure.in is a minimal (two line) file. The configure script produces a Makefile from a file called Makefile.in. It can also produce a file called config.h from the file config.h.in.

A Minimal configure.in

Here is a minimal configure.in You may download the example The file is preprocessed by a macro processor, m4. The macros AC_INIT and AC_OUTPUT are required in any configure.in file. The form of a macro is:
MACRO(argument1, argument2, ... , argument_n)
autoconf is picky about spaces, there should be no space between a macro, and the parentheses containing its arguments.

AC_INIT(configure.in)
AC_OUTPUT(Makefile)

Running autoconf on this file, we get a script configure. If we run ./configure --help, we get the following output:

Usage: configure [options] [host]
Options: [defaults in brackets after descriptions]
Configuration:
  --cache-file=FILE       cache test results in FILE
  --help                  print this message
  --no-create             do not create output files
  --quiet, --silent       do not print `checking...' messages
  --version               print the version of autoconf that created configure
Directory and file names:
  --prefix=PREFIX         install architecture-independent files in PREFIX
                          [/usr/local]
  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                          [same as prefix]
  --bindir=DIR            user executables in DIR [EPREFIX/bin]
  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
  --datadir=DIR           read-only architecture-independent data in DIR
                          [PREFIX/share]
  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
                          [PREFIX/com]
  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
  --includedir=DIR        C header files in DIR [PREFIX/include]
  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
  --infodir=DIR           info documentation in DIR [PREFIX/info]
  --mandir=DIR            man documentation in DIR [PREFIX/man]
  --srcdir=DIR            find the sources in DIR [configure dir or ..]
  --program-prefix=PREFIX prepend PREFIX to installed program names
  --program-suffix=SUFFIX append SUFFIX to installed program names
  --program-transform-name=PROGRAM
                          run sed PROGRAM on installed program names
Host type:
  --build=BUILD           configure for building on BUILD [BUILD=HOST]
  --host=HOST             configure for HOST [guessed]
  --target=TARGET         configure for TARGET [TARGET=HOST]
Features and packages:
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
  --x-includes=DIR        X include files are in DIR
  --x-libraries=DIR       X library files are in DIR

In other words, out minimal configure script already lets the user configure prefixes. However, we typically want to do more with configure. For example, we'd like it to detect a working compiler for us. Here's a more interesting example of a well commented configure.in file. By the time we've dissected this example, we should have some idea of what configure is capable of.

An Advanced Example

here it is: ( and it can be downloaded )



AC_INIT(configure.in)

dnl generate config.h from config.h.in
AC_CONFIG_HEADER(config.h)	

dnl replace @extra_includes@ with the value of $extra_includes deduced
dnl from the configure script.
AC_SUBST(extra_includes)	

dnl allow the user to specify a directory for extra header files
AC_ARG_WITH(extra-includes, 
	[--with-extra-includes=DIR    where the extra includes are located ],
    extra_includes="$withval"
)     

dnl write a message to indicate we're looking for the argument, and
dnl print out the result.
AC_MSG_CHECKING("extra includes")
AC_MSG_RESULT($withval)

dnl use C++ for tests
AC_LANG_CPLUSPLUS	

dnl check for different programs and substitute CC, CXX, INSTALL in Makefile.in
AC_PROG_CXX	dnl C++ compiler
AC_PROG_CC  dnl C compiler
AC_PROG_INSTALL  dnl install program. We must place install-sh in directory.

dnl check sizes of data types and write defines in config.h
AC_CHECK_SIZEOF(int)		
AC_CHECK_SIZEOF(long long)	

dnl look for headers in standard directories
AC_CHECK_HEADERS(unistd.h  dirent.h)	

dnl user defined macro to look for QT header
AC_FIND_FILE("qglobal.h", [/usr/lib/qt/include /usr/lib/qt-2.1.0/include], qtdir)

dnl warn user if header wasn't found
if test $qtdir = NO
then
	AC_WARN([QT header not found])
fi

dnl try to compile and run a program.
dnl this example tests endianness
AC_TRY_RUN([

int main()
{
	short s = 1;
	short* ptr = &s;
	unsigned char c = *((char*)ptr);
	return c;	
}

]
, [ echo "big endian" ]
, [ echo "little endian" ]
)

dnl write a Makefile.

AC_OUTPUT(Makefile)

So let's go through the macros:

Custom Macros

One question that is still unresolved is this: how was AC_FIND_FILE defined ? Here's the answer: to define a macro, use
AC_DEFUN(MACRO_NAME, macro-command)
Typically, macro commands will go for several lines. It is not unusual to wrap all of the macro commands in square brackets to prevent something from being misinterpreted as a macro or a character that has special meaning to m4. Here is the example:


dnl AC_FIND_FILE ( file, dirs, variable )



AC_DEFUN(AC_FIND_FILE, 
[

	$3=NO
	for x in $2
	do
		for y in $1
		do
			if test -r "$x/$y"
			then
				$3=$x
				break 2
			fi
		done
	done

]
)

For anyone who has written shell script before, the above should look and feel familiar. It's basically just a shellscript function. To prevent configure.in from becoming too cluttered, we usually place macro definitions in a seperate file, acinclude.m4. We then have to run aclocal to generate aclocal.m4, where autoconf looks for macros.

Fortunately, there are a lot of macros already written, so you can usually get by borrowing other peoples macros. ( For example, both the KDE and GNOME projects supply several autoconf macros to make it easier to run tests ). Perhaps one of the nicest things about these macros is that they are reusable enough that one doesn't have to write them very often !