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.
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.
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.
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:
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 !