by Robert Richter, 2005
see Autobook
Software portability and effective build systems are crucial aspects of modern software engineering practice. It is unlikely that a software project would be started today with the expectation that the software would run on only one platform. Autotools may help creating platform independent software packages and build systems, that make building software easier and less error prone are valuable.
Even if there is no need for creating platform independent software packages or adding Autotools to a project, Autotools are usefull to:
Common open source package installation process:
$ ./configure $ make $ make install
see info and help documentation
Tools available for package creation:
Generate 'aclocal.m4' by scanning 'configure.ac' or 'configure.in'. 'm4' is a powerful macro processor, in the sense that it copies its input to the output, expanding macros as it goes. This is needed by Autoconf for generating configure scripts.
Create a template file of C '#define' statements for 'configure' to use. To this end, scan TEMPLATE-FILE, or 'configure.ac' if present, or else 'configure.in'.
see Libtool manual
Libtool is a platform specific compiler/linker wrapper that provides a consistent interface for building software. Common modes are supported such as:
- Building object files
- Building static and shared libraries - Linking static and shared libraries
- Building executables - Debugging executables
- Installing static and shared libraries - Installing executables
- Removing installed static and shared libraries - Removing installed executables
- Removing libraries, executables, objects and temporary files
Since Libtool is platform dependent, Autoconf is used to generate a 'configure' script that determines platform specific settings and creates Libtool (see create Libtool with Autoconf).
The 'libtoolize' program provides a standard way to add libtool support to your package.
Automake is a tool for automatically generating 'Makefile.in's from files called 'Makefile.am'. Each 'Makefile.am' is basically a series of 'make' variable definitions, with rules being thrown in occasionally. The generated 'Makefile.in's are compliant with the GNU Makefile standards.
Generates a configuration script 'configure' from such as 'configure.ac' or 'configure.in'.
Examines source files in the project directory tree. Searchs the source files for common portability problems, checks for incompleteness of `configure.ac', and creates a file `configure.scan' which is a preliminary `configure.ac' for that package.
Remake the GNU Build System files by scanning 'configure.ac' and running 'autoconf', 'autoheader', 'aclocal', 'automake' and 'libtoolize'.
Updates 'configure.ac' to the syntax of the current version of Autoconf.
There are several ways to configure and build a project using Autotools. For each approach a set of various configuration files are needed:
- configure.ac with Automake and Libtool macros - Makefile.am with make variable definitions
- Makefile.in with project build rules and Makefile substitutions
- configure.ac with Libtool macros and system feature tests - Makefile.in with project build rules and Makefile substitutions
- configure.ac with Libtool macros - Makefile.in with project build rules and Makefile substitutions
- configure.ac with macros that test system features the project needs - Makefile.in with project build rules and Makefile substitutions
- Libtool environment - Plain Makefiles
- scans the project and generates a config.h file during project configuration with platform specific macro definitions, no configuration files needed
see Autobook, Chapter 24.4.1, 24.4.2, 11
Automake provides the easiest way of setting up a GNU Build System. Files needed for setup are a standard configure.ac file with Automake macros and a makefile.am with some project specific make variable definitions. All remaining work such as the generation of Makefile templates and standard files needed for project build is processed by Automake. Since project setup depends on Automake tools, this approach is usefull for configuration of standard projects, but can not provide the flexibility resulting in special project needs such as the addition of special makefile rules.
$ find . ./configure.ac ./hello.c ./hello.h ./main.c ./Makefile.am
$ cat hello.c #if HAVE_CONFIG_H # include <config.h> #endif
#include <stdio.h>
#include "hello.h"
int hello (const char *who) { printf("Hello, %s!\n", who); return 0; }
$ cat hello.h #ifndef HELLO_H #define HELLO_H 1
extern int hello (const char *who);
#endif /* !HELLO_H */
$ cat main.c #if HAVE_CONFIG_H # include <config.h> #endif
#include "hello.h"
int main (int argc, const char *const argv[]) { return hello("World"); }
$ cat configure.ac # Process this file with autoconf to create configure.
AC_INIT(hello.h) AM_CONFIG_HEADER(config.h:config.hin) AM_INIT_AUTOMAKE(hello, 1.0)
AC_PROG_CC AM_PROG_CC_STDC AC_C_CONST AC_LIBTOOL_WIN32_DLL AM_PROG_LIBTOOL
AC_OUTPUT(Makefile)
$ cat Makefile.am ## Process this file with automake to produce Makefile.in.
lib_LTLIBRARIES = libhello.la libhello_la_SOURCES = hello.c libhello_la_LDFLAGS = -no-undefined -avoid-version
include_HEADERS = hello.h
bin_PROGRAMS = hello hello_SOURCES = main.c hello_LDADD = libhello.la
$ aclocal ... $ autoheader ... $ libtoolize --force --copy ... $ automake --add-missing --copy --foreign ... $ autoconf ... $ ./configure ... $ make ... $ make DESTDIR=`pwd`/inst/ install ...
Autoconf creates a 'configure' script from a 'configure.ac' file. See Autoconf manual, chapter 3.1.3 for the standard layout of Autoconf input files. An initial setup of configure.ac could be:
$ cat configure.ac AC_INIT
dnl configure Makefile creation from template file (Makefile.in): AC_CONFIG_FILES(Makefile)
AC_OUTPUT
This example configure.ac file needs a template make file (Makefile.in) for the package, what can be derived from the projects Makefile. (For just setting up the environment it can be empty first.) With both files Autoconf can be run properly:
$ touch Makefile.in $ ls Makefile.in configure.in $ autoconf $ ./configure configure: creating ./config.status config.status: creating Makefile $ ls Makefile autom4te.cache config.status configure.ac Makefile.in config.log configure
Autoscan provides an easy way to create a configure.ac file. Simply call Autoscan in your project and rename the configure.scan file to configure.ac. Also add appropriate build rules to your Makefile.in. Following example uses the same .h and .c files from the Automake example above:
$ rm configure.ac $ ls Makefile.in hello.c hello.h main.c $ cat Makefile.in # Default goal - must be first rule all:
# Project settings TARGETS = hello$(EXEEXT) hello-objs = libhello.la main.o libhello-objs = hello.lo PHONY_OBJS = hello-objs libhello-objs BIN_FILES = hello$(EXEEXT) LIB_FILES = libhello.la INC_FILES = hello.h
# Project dependencies libhello-objs: $(libhello-objs) hello-objs: $(hello-objs)
# Autoconf installation variables bindir = @bindir@ exec_prefix = @exec_prefix@ includedir = @includedir@ libdir = @libdir@ mkdir_p = @mkdir_p@ prefix = @prefix@ CFLAGS = @CFLAGS@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ LDFLAGS = @LDFLAGS@ SHELL = @SHELL@
# Defined to prevent installation in root dir DESTDIR = $(CURDIR)/inst
# General rules .PHONY: all \ clean \ realclean \ install \ uninstall \ $(PHONY_OBJS)
all: $(TARGETS)
clean: $(RM) *.la *.o *.lo *.dll *.a $(TARGETS)
realclean: clean $(RM) -r .libs/ ./inst/ find -name \*~ | xargs $(RM)
INSTALL_BIN_FILES = $(addprefix $(DESTDIR)$(bindir)/, $(BIN_FILES)) INSTALL_LIB_FILES = $(addprefix $(DESTDIR)$(libdir)/, $(LIB_FILES)) INSTALL_INC_FILES = $(addprefix $(DESTDIR)$(includedir)/, $(INC_FILES))
install: $(INSTALL_LIB_FILES) \ $(INSTALL_BIN_FILES) \ $(INSTALL_INC_FILES)
uninstall: $(RM) $(patsubst $(DESTDIR)$(libdir)/lib%.la,$(DESTDIR)$(bindir)/cyg%.dll,$(INSTALL_LIB_FILES)) $(RM) $(patsubst %.la,%.dll.a,$(INSTALL_LIB_FILES)) $(RM) $(patsubst %.la,%.a,$(INSTALL_LIB_FILES)) $(RM) $(INSTALL_BIN_FILES) $(RM) $(INSTALL_INC_FILES)
$(INSTALL_BIN_FILES): $(DESTDIR)$(bindir)/%: % test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" $(INSTALL) "$<" "$@"
$(INSTALL_LIB_FILES): $(DESTDIR)$(libdir)/lib%.la: lib%.la test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" $(INSTALL) "lib$*.dll.a" "$(DESTDIR)$(libdir)/lib$*.dll.a" $(INSTALL) "lib$*.a" "$(DESTDIR)$(libdir)/lib$*.a" test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" $(INSTALL) "cyg$*.dll" "$(DESTDIR)$(bindir)/cyg$*.dll"
$(INSTALL_INC_FILES): $(DESTDIR)$(includedir)/%: % test -z "$(includedir)" || $(mkdir_p) "$(DESTDIR)$(includedir)" $(INSTALL_DATA) "$<" "$@"
# remake configuration automatically $(srcdir)/configure: configure.ac aclocal.m4 cd $(srcdir) && autoconf
# autoheader might not change config.h.in, so touch a stamp file. $(srcdir)/config.h.in: stamp-h.in $(srcdir)/stamp-h.in: configure.ac aclocal.m4 cd $(srcdir) && autoheader echo timestamp > $(srcdir)/stamp-h.in
config.h: stamp-h stamp-h: config.h.in config.status ./config.status
Makefile: Makefile.in config.status ./config.status
config.status: configure ./config.status --recheck
# Build rules %.o: %.c gcc -c $(CFLAGS) -o $@ $<
%.lo: %.c gcc -c $(CFLAGS) -o $*.o $< gcc -c $(CFLAGS) -DPIC -o $*.pic.o $< touch $@
# The phony prerequisite here should be changed since this forces target build. lib%.la: lib%-objs gcc -shared $*.pic.o -o cyg$*.dll \ -Wl,--image-base=0x10000000 \ -Wl,--out-implib,lib$*.dll.a ar cru lib$*.a $*.o ranlib lib$*.a touch $@
# The phony prerequisite here should be changed since this forces target build. %$(EXEEXT): %-objs gcc -g -O -o $@ $(filter %.o, $($*-objs)) \ $(patsubst %.la,%.dll.a,$(filter %.la, $($*-objs))) \ -Wl,--rpath -Wl,${libdir}
$ touch configure.ac $ autoscan ... $ mv configure.scan configure.ac
Autoscan created a new configure.ac file with checks needed for C and configuration header file support:
$ cat configure.ac # -*- Autoconf -*- # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59) AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) AC_CONFIG_SRCDIR([hello.c]) AC_CONFIG_HEADER([config.h])
# Checks for programs. AC_PROG_CC AC_PROG_INSTALL AC_PROG_RANLIB
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics. AC_C_CONST
# Checks for library functions.
AC_CONFIG_FILES([Makefile]) AC_OUTPUT
For creating config.h the autoheader tool is necessary. Also standard files such as install-sh that can be installed easily with Automake are needed. Follow these steps to finalize package generation:
$ autoheader --force $ automake --add-missing --copy ... $ autoconf $ ls Makefile autoscan.log config.log configure.ac install-sh Makefile.in config.h config.status hello.c main.c autom4te.cache config.h.in configure hello.h missing
Add Libtool handling to configure.ac and Makefile.in (see Libtool manual, chapter 5.3.1). Also add Automake initialisation to your configuration and generate a local Autoconf environment since Libtool requires Automake API for configuration.
Create a default configure.ac file by scanning with autoscan (see above). Add this to the appropriate sections of configure.ac:
AM_INIT_AUTOMAKE(hello, 1.0)
# Checks for programs. AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS)
Add Libtool dependencies to Makefile.in and replace compiler and linker commands by Libtool wrapper commands:
# Libtool dependencies LIBTOOL_DEPS = @LIBTOOL_DEPS@ libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status --recheck
After changing the files, create the project environment, rescan it with Autoscan and modify configure.ac accordingly:
$ ls Makefile.in configure.ac hello.c hello.h main.c $ aclocal ... $ autoheader --force $ automake --add-missing --copy ... $ libtoolize --copy ... $ autoscan
The resulting files look like these:
$ cat configure.ac # -*- Autoconf -*- # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59) AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) AM_INIT_AUTOMAKE(hello, 1.0) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h])
# Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS)
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics. AC_C_CONST
# Checks for library functions.
AC_CONFIG_FILES([Makefile]) AC_OUTPUT
$ cat Makefile.in # Default goal - must be first rule all:
# Project settings TARGETS = hello$(EXEEXT) hello-objs = libhello.la main.o libhello-objs = hello.lo PHONY_OBJS = hello-objs libhello-objs BIN_FILES = hello$(EXEEXT) LIB_FILES = libhello.la INC_FILES = hello.h
# Project dependencies libhello-objs: $(libhello-objs) hello-objs: $(hello-objs)
# Autoconf installation variables bindir = @bindir@ exec_prefix = @exec_prefix@ includedir = @includedir@ libdir = @libdir@ mkdir_p = @mkdir_p@ prefix = @prefix@ srcdir = @srcdir@ CFLAGS = @CFLAGS@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ LDFLAGS = @LDFLAGS@ SHELL = @SHELL@
# Defined to prevent installation in root dir DESTDIR = $(CURDIR)/inst
# General rules .PHONY: all \ clean \ realclean \ $(PHONY_OBJS)
all: $(TARGETS)
clean: libtool ./libtool --mode=clean $(RM) *.la *.o *.lo *.exe
realclean: clean $(RM) stamp-h.in find -name \*~ | xargs $(RM)
INSTALL_BIN_FILES = $(addprefix $(DESTDIR)$(bindir)/, $(BIN_FILES)) INSTALL_LIB_FILES = $(addprefix $(DESTDIR)$(libdir)/, $(LIB_FILES)) INSTALL_INC_FILES = $(addprefix $(DESTDIR)$(includedir)/, $(INC_FILES))
# rpath needs to be specified in order to get shared libraries # (see Libtool Manual, 3.2 Linking libraries, Footnote 1) LDFLAGS += -rpath $(libdir) -no-undefined -avoid-version
# Libtool dependencies LIBTOOL_DEPS = @LIBTOOL_DEPS@ libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status --recheck
# remake configuration automatically $(srcdir)/configure: configure.ac aclocal.m4 cd $(srcdir) && autoconf
# autoheader might not change config.h.in, so touch a stamp file. $(srcdir)/config.h.in: stamp-h.in $(srcdir)/stamp-h.in: configure.ac aclocal.m4 cd $(srcdir) && autoheader echo timestamp > $(srcdir)/stamp-h.in
config.h: stamp-h stamp-h: config.h.in config.status ./config.status
Makefile: Makefile.in config.status ./config.status
config.status: configure ./config.status --recheck
# Install rules install: $(INSTALL_LIB_FILES) \ $(INSTALL_BIN_FILES) \ $(INSTALL_INC_FILES)
uninstall: ./libtool --mode=clean $(RM) $(INSTALL_LIB_FILES) ./libtool --mode=clean $(RM) $(INSTALL_BIN_FILES) $(RM) $(INSTALL_INC_FILES)
$(INSTALL_BIN_FILES): $(DESTDIR)$(bindir)/%: % libtool test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" ./libtool --mode=install $(INSTALL) "$<" "$@"
$(INSTALL_LIB_FILES): $(DESTDIR)$(libdir)/lib%.la: lib%.la test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" ./libtool --mode=install $(INSTALL) "$<" "$@"
$(INSTALL_INC_FILES): $(DESTDIR)$(includedir)/%: % test -z "$(includedir)" || $(mkdir_p) "$(DESTDIR)$(includedir)" $(INSTALL_DATA) "$<" "$@"
# Build rules %.lo %.o: %.c libtool ./libtool --mode=compile gcc -g -c -O $(CFLAGS) -o $@ $<
%.la: %-objs libtool ./libtool --mode=link gcc -g -O $(LDFLAGS) -o $@ $($*-objs)
%.exe: %-objs libtool ./libtool --mode=link gcc -g -O $(LDFLAGS) -o $@ $($*-objs)
Now the project is ready to build:
$ aclocal ... $ autoheader --force $ automake --add-missing --copy; true; ... $ libtoolize --copy ... $ autoconf $ ./configure ... $ make ... $ make DESTDIR=`pwd`/inst install ...
see Autoconf Manual, chapter 4.7.3 Build Directories
Each Makefile.in should contain:
srcdir = @srcdir@ VPATH = @srcdir@
Due to VPATH limitations it might be necessary in certain cases to include the '$(srcdir)/' prefix in your Makefile.
see Autoconf, Chapter 4.7.4
Add this to Makefile.in:
$(srcdir)/configure: configure.ac aclocal.m4 cd $(srcdir) && autoconf
# autoheader might not change config.h.in, so touch a stamp file. $(srcdir)/config.h.in: stamp-h.in $(srcdir)/stamp-h.in: configure.ac aclocal.m4 cd $(srcdir) && autoheader echo timestamp > $(srcdir)/stamp-h.in
config.h: stamp-h stamp-h: config.h.in config.status ./config.status
Makefile: Makefile.in config.status ./config.status
config.status: configure ./config.status --recheck
Add this to configure.ac:
AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h])
Add stamp-h.in to your package:
$ touch stamp-h.in
see manuals for Autoconf, Automake, Libtool see Autobook, Chapter 11.1
It is necessary to build Libtool for a specific platform with the use of Autoconf. That is Autoconf is required to create Libtool and basic knowledge about generating Autoconf input files (e.g. configure.ac) is essential. Autoconf generates a 'configure' script that creates Libtool. Once Libtool is available for a platform, it can be used for various software builds and packages without recreation. Follow these steps to create Libtool:
Create this configure.ac file:
$ cat configure.ac # -*- Autoconf -*- # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59) AC_INIT
# Checks for programs. AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS)
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT
Now run Autoconf:
$ ls configure.ac $ aclocal ... $ automake --add-missing --copy ... $ libtoolize --copy ... $ autoconf ... $ ls -t ltmain.sh config.guess install-sh configure configure.ac config.sub missing autom4te.cache aclocal.m4
$ ./configure ... $ ls -t config.log ltmain.sh missing configure config.status config.sub install-sh aclocal.m4 libtool config.guess autom4te.cache configure.ac
The run produced in the end a ./libtool shell script generated by Autoconf for usage in Makefiles on the target platform.
Copy libtool to your project. See 'How to create Libtool with Autoconf for a specific platform' for creating the file. Also add a Makefile as shown to the project containing all necessary libtool build and install rules for the project. Then run make:
$ ls Makefile hello.c hello.h libtool main.c
$ cat Makefile # Autoconf installation variables bindir = ${exec_prefix}/bin exec_prefix = ${prefix} includedir = ${prefix}/include libdir = ${exec_prefix}/lib mkdir_p = mkdir -p -- prefix = /usr/local top_builddir = . EXEEXT = .exe INSTALL = /usr/bin/install -c INSTALL_DATA = ${INSTALL} -m 644 SHELL = /bin/bash
# Defined to prevent installation in root dir DESTDIR = $(CURDIR)/inst
# Libtool LIBTOOL = $(SHELL) $(top_builddir)/libtool
# Default goal - must be first rule all:
# Project settings TARGETS = hello$(EXEEXT) hello-objs = libhello.la main.o libhello-objs = hello.lo PHONY_OBJS = hello-objs libhello-objs BIN_FILES = hello$(EXEEXT) LIB_FILES = libhello.la INC_FILES = hello.h
libhello-objs: $(libhello-objs) hello-objs: $(hello-objs)
# General rules .PHONY: all \ clean \ realclean \ install \ uninstall \ $(PHONY_OBJS)
all: $(TARGETS)
clean: $(LIBTOOL) --mode=clean $(RM) *.la *.o *.lo $(TARGETS)
realclean: clean $(RM) -r .libs/ ./inst/ find -name \*~ | xargs $(RM)
INSTALL_BIN_FILES = $(addprefix $(DESTDIR)$(bindir)/, $(BIN_FILES)) INSTALL_LIB_FILES = $(addprefix $(DESTDIR)$(libdir)/, $(LIB_FILES)) INSTALL_INC_FILES = $(addprefix $(DESTDIR)$(includedir)/, $(INC_FILES))
install: $(INSTALL_LIB_FILES) \ $(INSTALL_BIN_FILES) \ $(INSTALL_INC_FILES)
uninstall: $(LIBTOOL) --mode=uninstall $(RM) $(INSTALL_LIB_FILES) $(LIBTOOL) --mode=uninstall $(RM) $(INSTALL_BIN_FILES) $(RM) $(INSTALL_INC_FILES)
$(INSTALL_BIN_FILES): $(DESTDIR)$(bindir)/%: % test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" $(LIBTOOL) --mode=install $(INSTALL) "$<" "$@"
$(INSTALL_LIB_FILES): $(DESTDIR)$(libdir)/%: % test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" $(LIBTOOL) --mode=install $(INSTALL) "$<" "$@"
$(INSTALL_INC_FILES): $(DESTDIR)$(includedir)/%: % test -z "$(includedir)" || $(mkdir_p) "$(DESTDIR)$(includedir)" $(INSTALL_DATA) "$<" "$@"
# rpath needs to be specified in order to get shared libraries # (see Libtool Manual, 3.2 Linking libraries, Footnote 1) LDFLAGS += -rpath $(libdir) -no-undefined -avoid-version
# Build rules %.lo %.o: %.c $(LIBTOOL) --mode=compile gcc -g -c -O $(CFLAGS) -o $@ $<
%.la: %-objs $(LIBTOOL) --mode=link gcc -g -O $(LDFLAGS) -o $@ $($*-objs)
%$(EXEEXT): %-objs $(LIBTOOL) --mode=link gcc -g -O $(LDFLAGS) -o $@ $($*-objs)
$ make ... $ make DESTDIR=`pwd`/inst install ... $ find inst -type f inst/usr/local/bin/cyghello.dll inst/usr/local/bin/hello.exe inst/usr/local/include/hello.h inst/usr/local/lib/libhello.a inst/usr/local/lib/libhello.dll.a inst/usr/local/lib/libhello.la