Build Systems with autoconf, automake and libtool [updated]

Post on 14-May-2015

4.894 views 2 download

description

Talk held at FOSDEM 2010

Transcript of Build Systems with autoconf, automake and libtool [updated]

Build Systems with autoconf and libtool

Benny Siegert ‹bsiegert@gmail.com›The MirOS Project (http://www.mirbsd.org)

FOSDEM 2010

Contents1. Theory

a) Introduction: a history lesson

b) Presentation of components

2. Practice

a) A concrete example

b) Bad practices

3. Conclusion

Make!les‣ since Version 7 AT&T Unix (ca 1977)

‣ Targets = !lenameswith dependencies and compiler commands

‣ “Phony” !lenames like “install”

# extremely simple Makefilefoo: foo.o cc -o foo foo.o

foo.o: foo.c cc -c -o foo.o foo.c

install: foo install -m 555 foo /usr/local/bin

… end of story?

Problem: Options

‣ compiler "ags, conditionals

‣ paths: headers, installation directory?

‣ user must be able to adjust these

‣ manually editing Make!les today?unacceptable!

Other build systems‣ imake (X11): Imake!le + cpp ➝ Make!le

‣ a nightmare in practice

‣ Xorg now uses autotools, they know why

‣ Schily make (J?rg Schilling, cdrtools)

‣ >300 Make!le fragments, one per platform

‣ does not work with BSD make

‣ bsd.prog.mk etc (BSD): platform dependent

The GNU build system‣ almost everywhere today (good!)

‣ easy to use:

% ./configure ; make ; make install

‣ But: few developers understand it

‣ more and more “easier” alternatives (cmake)

‣ Is it really too complicated?

— No, because a portable build system is difficult!

Introducingautotools

con!gure

‣ gets options:target dir, compiler "ags, paths, modules, etc.

‣ searches for libraries, headers, paths

‣ creates con!g.status

‣ con!g.status handles replacement variables(e.g. CC, CFLAGS, LIBS)

con!g.statusMake!le.incon!g.h.in

*.in

Make!lecon!g.h

*

autoconf

‣ you don‘t actually write con!gure yourself

‣ con!gure.ac contains macro calls to be converted into a shell script

‣ Macros that are not part of autoconfcome from aclocal.m4

‣ Documentation: info autoconf (read it!)

m4con!gure.acaclocal.m4

con!gure

autoheader

‣ creates a template for con!g.h

‣ con!g.h contains C preprocessor symbolsfor options, e.g. HAVE_FOO_H

‣ Source code: #include “con!g.h”at the start of each !le

‣ Documentation: info autoheader

con!gure.ac con!g.h.in

automake

‣ creates a portable Make!lefrom a simpli!ed description

‣ Make!le.am is short and simple

‣ Make!le.in looks horrendously complex(don‘t touch it then)

‣ Documentation: info automake

Make!le.am Make!le.in

aclocal

‣ part of automake

‣ tells autoconf about local macros,like those from automake

‣ only those actually used are put into aclocal.m4

‣ acinclude.m4 is for your own macros

‣ Documentation: info aclocal

*.m4 aclocal.m4

pkgcon!g

‣ makes !nding and using shared libs easier

‣ replaces *-con!g scripts (gtk-con!g etc.)

‣ PKG_CHECK_MODULES(FOO, glib2.0 >= 2.8.0)sets FOO_CFLAGS, FOO_LIBS

‣ if you are writing a library, consider distributing a .pc !le for it

‣ Dokumentation: man pkgcon!g

CFLAGS LIBS

*.pc

libtool

‣ shared library support for all platforms

‣ encapsulates platform-speci!c commands

‣ you pretend you are building a static library with .la suffix (so-called libtool library)

‣ you pre!x commands with libtool --mode=foo,libtool executes different, adapted commands

‣ Documentation: info libtool (except Mac OS)

command different command

Others

autom4te: m4 wrapper used by autoconf

‣ Weird problems: delete autom4te.cache

autoscan: automatically create con!gure.ac

libtoolize, gettextize: add !les for libtool or gettext

autoreconf: regenerate necessary !les

Part 2Practice

Example source code#include <stdio.h>#include "hello.h"

void hello(){ printf("Hello World!\n");}

void hello();

#include "hello.h"

int main(int argc, char **argv){ hello(); return 0;}

hello.c

hello.h

main.c

Traditional Make!le

hw: main.o libhello.a cc -o hw main.o libhello.a

libhello.a: hello.o ar cru libhello.a hello.o ranlib libhello.a

.SUFFIXES: .c .o

.c.o: cc -c -o $@ $<

clean: rm -f hw main.o hello.o libhello.a

Make!le using libtoolhw: main.lo libhello.la ./libtool --mode=link cc -o hw main.lo libhello.la

libhello.la: hello.lo ./libtool --mode=link cc -rpath /usr/local/lib \ -o libhello.la hello.lo

.SUFFIXES: .c .lo

.c.lo: ./libtool --mode=compile cc -c -o $@ $<

clean: ./libtool --mode=clean rm -f hw main.lo hello.lo \ libhello.la

autoconf + automake‣ here: source code moved to src/

‣ call autoreconf -v -i

AC_INIT(hw, 0.1)AM_INIT_AUTOMAKE(foreign)

AC_PROG_CCAC_STDC_HEADERS

AC_CONFIG_FILES([Makefile src/Makefile])AC_OUTPUT

bin_PROGRAMS = hwhw_SOURCES = main.c hello.c hello.h

SUBDIRS = src

con!gure.ac

Make!le.am

src/Make!le.am

Using con!g.h

‣ call autoheader (done by autoreconf)

‣ To use con!g.h:add this to the top of every .c !le

AC_INIT(hw, 0.1)AM_INIT_AUTOMAKEAC_CONFIG_HEADERS(config.h)

[…]

con!gure.ac

#ifdef HAVE_CONFIG_H#include "config.h"#endif

Features we have‣ uses user‘s CC and CFLAGS

‣ can be built outside the sources

‣ installation (via make install), uninstall

‣ user can specify target directory

‣ dependency tracking (can be disabled)

‣ create a tarball (make dist)

‣ make clean

libtool!

‣ goal (as before): make libhello a shared library

‣ libtool is created by con!gure (AC_PROG_LIBTOOL macro)

‣ copy libtool.m4 and ltmain.sh from the libtool sources (or use libtoolize --copy)

‣ !nally call autoreconf -v -i

bin_PROGRAMS = hwhw_SOURCES = main.chw_LDADD = libhello.la

lib_LTLIBRARIES = libhello.lalibhello_la_SOURCES = hello.c hello.h

src/Makefile.am

[…]AC_PROG_CCAC_STDC_HEADERSAC_PROG_LIBTOOL[…]

configure.ac

ACLOCAL_AMFLAGS = -I .

SUBDIRS = srcMakefile.am

Replacement variables‣ a variable is set to a value in con!gure

‣ AC_SUBST(variable) replaces @variable@by its value in output !les

‣ automake de!nes variable for us in Make!les

‣ Goal here: version info for libhello should be set by con!gure

‣ add -version-info to libhello‘s LDFLAGS

current:revision:age‣ version info for libtool libraries

‣ three numbers ∈ ℕ

‣ current: current API revision

‣ revision: bug!xes etc. without any API changes

‣ age: to how many API revisions are we backwards compatible?

‣ If you don‘t touch any function prototypes:

increment revision

‣ a function was added:

increment current and age, set revision to 0

‣ function prototype changed or removed:

increment current, set age and revision to 0

[…]LIBHELLO_CURRENT=1LIBHELLO_REVISION=0LIBHELLO_AGE=1

LIBHELLO_VER=$LIBHELLO_CURRENT:\$LIBHELLO_REVISION:$LIBHELLO_AGEAC_SUBST(LIBHELLO_VER)

AC_CONFIG_FILES([Makefile src/Makefile])AC_OUTPUT

con!gure.ac

lib_LTLIBRARIES = libhello.lalibhello_la_SOURCES = hello.clibhello_la_LDFLAGS = \ -version-info ${LIBHELLO_VER}

src/Make!le.am

Advanced stuff

Checking headers‣ AC_CHECK_HEADERS checks if a header !le is

found and if it works

‣ If another header must be included before, use the fourth parameter

‣ ex: sys/mount.h depends on sys/param.h[…]AC_CHECK_HEADERS([sys/param.h sys/mount.h], [], [], [#ifdef HAVE_SYS_PARAM_H#include <sys/param.h>#endif]) […]

con!gure.ac

arguments to con!gure

‣ additional features (--enable-feature) or

use of external packages (--with-package)

‣ AC_ARG_ENABLE and AC_ARG_WITH,work the same way

‣ Arguments: name, help string, and twoshell fragments (if arg given, if omitted)

‣ AS_HELP_STRING formats the help string

Example

‣ AC_DEFINE: sets a preprocessor symbol(in con!g.h)

[…]AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [Enable debugging code (default=no)]), [enable_debug=$enableval], [enable_debug=no])if test "x$enable_debug" = xyes; then AC_DEFINE(DEBUG, 1, [Enable debugging code])fi[…]

con!gure.ac

More complex exampleAC_ARG_WITH([glib2], AS_HELP_STRING([--with-glib2], [Enable support for glib 2.0 @<:@default=auto@:>@]), [with_glib2=$withval], [with_glib2=auto])if test "x$with_glib2" != xno; then PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.8], [have_glib2=yes], [have_glib2=no])else have_glib2=nofiif test "x$with_glib2" = xyes -a "x$have_glib2" = xno; then AC_MSG_ERROR([Library requirements (glib-2.0 >= 2.8) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.])fiif test "x$have_glib2" = xyes; then AC_DEFINE([HAVE_GLIB2], [1], [Define if you have the glib2 library.])fi

Conditional compile‣ No portable “if” statement in Make!les,

use automake conditionals

‣ decision is taken at con!gure timeusing the test from the second argument

[…]AM_CONDITIONAL(DEBUG,

[test "x$enable_debug" = xyes]) […]

con!gure.ac

[…]if DEBUGhw_SOURCES += debugging.celse# something elseendif

src/Make!le.am

How not to do it

‣ automake replacement, used by wxWidgets

‣ pkgsrc has patched wxWidgets Make!les so that they use libtool. The patch is 3 MiB.

‣ example Bake!le, from the tutorial:

Bake!le

<?xml version="1.0"?>

<makefile> <include file="presets/simple.bkl"/>

<exe id="hello" template="simple"> <sources>hello.c</sources> </exe></makefile>

wx-con!g‣ very complex con!g script instead of a .pc !le

‣ has to clean up behind con!gure:# We evidently can't trust people not to duplicate things in# configure, or to keep them in any sort of sane order overall,# so only add unique new fields here even if it takes us a while.[…]# This is not a licence# for sloppy work elsewhere though and @GUI_TK_LIBRARY should# be fixed.[…]# of course, this duplication is bad but I'll leave to somebody else the care# of refactoring this as I don't see any way to do it - VZ.

# This (and the other cruft to support it) should be removed with# reference to the FIXME above when configure stops piping us a slurry# of options that need to be decomposed again for most practical uses - RL.

“List of platforms“‣ autoconf‘s goal is to help you get rid of long

lists of platforms and corresponding options.

‣ Yet, some con!gures manage to do this.case "${host}" in[…] *-*-openbsd*) USE_BSD=1 USE_OPENBSD=1 AC_DEFINE(__OPENBSD__) AC_DEFINE(__BSD__) DEFAULT_DEFAULT_wxUSE_GTK=1 ;;

*) AC_MSG_ERROR(unknown system type ${host}.)esac

a2ps Make!les‣ Last release (2.13b) in 2000

‣ uses autoconf 2.14a that was never officially released

‣ does not use aclocal but:

‣ comes with its own version of automakein 17 (!) m4 !les

## I use special autoconf/make macrosACLOCAL_AMFLAGS = --version >/dev/null && cat m4/*.m4 >aclocal.m4

Conclusion

‣ It is a good thing that so many projects use autotools – only one build system to learn

‣ you cannot write a portable build systemfrom scratch – so don‘t try

‣ writing Make!les with automake is notthat complicated

‣ automake replacements complicate lifefor porters and contributors

‣ a good build system even runs on an “unknown” system

1. Don‘t try to reinvent the wheel.

2. Not everyone uses Linux i386 with gccand GNU make.

3. Don‘t hardcode things that can be tested.

4. The user must be able to regenerateyour autoconf infrastructure.

5. Don‘t change any generated !les.

My golden rules

Thank youfor your attention!

‣ slides on http://www.slideshare.net/bsiegert

‣ info pages

‣ ”autotools mythbuster“:http://www."ameeyes.eu/autotools-mythbuster/

Further Reading