5.11. Autoconf og automake

Makefiles er en stor hjælp til selv små projekter. Et problem der hurtigt opstår er at projektet begynder at udvide sig og blive mere og mere komplekst. Det får så din Makefile til at blive voldsomt stor, fyldt med fejl og uoverskuelig. Mange af de operationer der er i en Makefile, er trivielle gentagelser fra projekt til projekt. Når der skal ske det samme så mange gange, ville det være en fordel at indbygge dette i make eller lave noget udenom make der kan håndtere dette. Resultatet blev autoconf.

Formålet med autoconf er få oprettet en "make"-fil der passer til slutbrugerens linux-version og eventuelt med brugerens specielle ønsker. Hele autoconf-systemet er lidt af en rodebunke og ret komplekst. Der findes flere mindre gode instruktioner til autoconf, og dette er nok endnu et eksempel – vi forsøger alligevel.

Følgende programpakker skal være installeret inden man kan gå i gang: make, autoconf og automake.

For at gøre det hele så simpelt som muligt, laver vi det klassiske "Hello, world!" eksempel og bygger videre på dette. I et tomt underkatalog har vi så det lille simple program hello.c med følgende indhold.


/* Filnavn: hello.c */
#include <stdio.h>
int main(void)
{
  printf("Hello, world!\n");
  return 0;
}

For at komme i gang med selve autoconf mangler vi configure.in og Makefile.am. Makefile.am er en fil til programmet automake, og den vil så oprette Makefile.in. Makefile.am er ofte en lille fil og ser således ud i en minimal udgave for et C-program.


## Filnavn: Makefile.am
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = hello
hello_SOURCES = hello.c

Næste fil er configure.in hvilket er en fil der kan gå hen og blive ret stor. I sin minimale udgave ser den således ud:


# Filnavn: configure.in
AC_INIT(hello.c)
AM_INIT_AUTOMAKE( demo-hello, 1.0 )
AC_PROG_CC
AC_STDC_HEADERS
AC_OUTPUT(Makefile)

Med filerne hello.c, Makefile.am og configure.in kan vi gå i gang med at oprette de filer vi senere skal have distribueret til brugeren.

aclocal.m4 er en samling af alle de shell-macroer som bruges i configure.in. Filen aclocal.m4 dannes af programmet aclocal ved at aclocal skanner configure.in for hvilke macroer der bliver brugt. Macroerne finder den i .m4-filer som dem i /usr/share/aclocal/ (Den kigger i nuværende bibliotek og /usr/share/aclocal/ automatisk, man kan angive yderligere biblioteker). aclocal skal køres hvis der anvendes nye makroer i configure.in. Når man da kalder autoconf for at danne configure ud fra configure.in, vil autoconf erstatte brug af macroer med koden fra aclocal.m4.


hven% aclocal

For at få oprettet Makefile.in – det er den der senere skal læses af configure – køres kommandoen automake -a.


hven% automake -a

Der manglede nogle filer til automake hvilket vi automatisk får med, med option '-a' (--add-missing).


hven% autoconf
hven% ./configure
hven% make

Ved oversættelse ses at der er brugt flaget '-g' til 'gcc', det betyder at der kommer debug-information med i programmet. Dette kan slås fra ved at sætte CFLAGS i configure.in til for eksempel "-O2". Dertil kommer at der er to oversætterdirektiver, PACKAGE og VERSION, med på kommandolinjen. Der kan komme flere direktiver, så det vil være smart hvis disse blev lagt i en fil for sig, så kommandolinjen ikke bliver for lang. Dette kan gøres ved at tilføje AM_CONFIG_HEADER(config.h) i configure.in.


# Filnavn: configure.in
AC_INIT(hello.c)
AM_INIT_AUTOMAKE( demo-hello, 1.0 )
AM_CONFIG_HEADER(config.h)
CFLAGS="-O2"
AC_PROG_CC
AC_STDC_HEADERS
AC_OUTPUT(Makefile)

Efter tilføjelse af AM_CONFIG_HEADER kan systemet drille lidt ved at komme med nogle sære fejlmeddelelser. Dette skyldes at der er nogle afhængigheder i aclocal.m4, så denne fil skal opdateres med kommandoen aclocal. For at få oprettet header-filen config.h.in køres autoheader. ./configure vil så oprette filen config.h som evt. kan tilføjes i hello.c. Dernæst kan ./configure køres igen, og der oprettes en Makefile.


hven% aclocal
hven% autoheader ; autoconf
hven% ./configure
hven% make clean ; make

Bemærk at der nu under oversættelse tilføjes -DHAVE_CONFIG_H på kommandolinjen. Vi kan nu teste i hello.c om der er en config.h og inkludere denne. Dernæst tjekker vi om PACKAGE og VERSION er defineret, og udskriver disse værdier. Tag et kig på config.h, og se hvad den indeholder.


/* Filnavn: hello.c */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>

int main(void)
{
#ifdef PACKAGE
#ifdef VERSION
  printf("Bygget med pakken: %s-%s\n\n", PACKAGE, VERSION);
#endif
#endif
  printf("Hello, world!\n");
  return 0;
}

Der er en del filer i et autoconf-system og som nybegynder kan det være noget svært at få et overblik. Listen herunder giver en beskrivelse af de mest almindelige filer.

Tabel 5-1. Filer i et autoconf system

FilnavnSkrives afLæses af
hello.c*dig*gcc
Makefile.am*dig*automake
configure.in*dig*automake autoconf aclocal
config.h.inautoheaderautomake configure
aclocal.m4aclocalautoconf
Makefile.inautomakeconfigure
missingautomakemake??
install-shautomake"make install"
mkinstalldirsautomakeconfigure
stamp-h.inautomakeconfigure
configureautoconf*script*
Makefileconfiguremake
config.hconfigurehello.c(gcc)
stamp-hconfigureconfigure
config.cacheconfigureconfigure
config.statusconfigureconfigure
config.logconfigure*dig*

I figuren herunder ses en skematisk tegning af de vigtigste filer i et autoconf system.

Figur 5-10. Autoconf road map

Du har nu fået oprettet de to mest basale filer Makefile.am og configure.in som fint kan danne basis for et projekt. Lad os lige stoppe op og kikke på hvad der er oprettet af filer og hvorfor. Makefile.am er den mest overordnede fil og også den mest avancerede m.h.t. til brug af makroer. Heldigvis for forståelsen er der ikke så mange kommandoer i denne fil, de er bare ret avancerede. Makefile.am indeholder informationer om hvordan projektet overordnet ser ud. Hvilke er selve programmerne, hvor skal disse installeres, hvilke kildefiler kræver disse programmer osv. En Makefile.am skal oprettes i hvert underkatalog, så dem kan man nemt få nogle stykker af.

Den Makefile.am der ligger i roden af projektet, har kommandoen AUTOMAKE_OPTIONS som overordnet bestemmer hvilken type af projekt man køre. En option man kan bruge til frie programmer er fx. gnu som er default. Med gnu-optionen opretter automake en del ekstra filer såsom COPYING og INSTALL. Dertil forventer automake at der er oprettet fire ekstra filer (NEWS README AUTHORS ChangeLog). Den nye Makefile.am ser således ud:


## Filnavn: Makefile.am
AUTOMAKE_OPTIONS = gnu
bin_PROGRAMS = hello
hello_SOURCES = hello.c

Inden automake køres igen, skal vi lige have tilføjet de ekstra filer som en gnu-installation kræver, og så kan automake køres. En ny Makefile.in oprettes, og den indeholder ny oplysninger om flere filer der skal med i en distribution når man senere kører kommandoen make dist.


hven% touch NEWS README AUTHORS ChangeLog
hven% automake

Kommandoen bin_PROGRAMS er en noget mere avanceret makro der fortæller hvilke binære filer der skal oversættes, og hvor disse skal installeres. Flere programmer kan indeholdes i en pakke, og de skrives på samme linje med mellemrum imellem. bin betyder at programmerne installeres i det underkatalog der er defineret i variablen bindir. Slutbrugeren kan så senere under installation bestemme hvor binære filer skal installeres. Default vil programmer blive installeret i /usr/local/bin/, men for nogle programmer gælder at disse skal installeres i /bin/. Vi kan på forhånd selv bestemme bindir, hvilket vi kommer tilbage til. bin_PROGRAMS har en fætter der hedder sbin_PROGRAMS, og den har default /usr/local/sbin/.

hello_SOURCES er en noget speciel variable der fortæller hvilke kildefiler der anvendes til et enkelt program. automake tager hvert program der er listet i fx. bin_PROGRAMS og leder efter variablen ???_SOURCES. automake forventer altså at denne variabel er oprettet, og der kommer selvfølgelig en fejl hvis den ikke er.

configure.in er filen der fortæller hvordan en konfiguration skal foregå. Her kikkes der ikke på kildetekster etc., men alene på hvordan konfigurationen skal forløbe for slutbrugeren. Til et projekt er der kun én configure.in, og denne er placeret i roden af projektet.

Der kan komme mange underlige fejl undervejs, hvilket kan afhjælpes ved at køre de forskellige programmer igen. For alle programmer gælder at det ikke skader at køre dem igen, og det tager heller ikke ret lang tid at gøre det. Så går noget galt, så kør denne stribe programmer igen:


hven% aclocal; automake -a; autoheader; autoconf; ./configure

Læs mere: http://sources.redhat.com/automake/automake.html, http://sources.redhat.com/autoconf/autoconf_toc.html, http://www.andamooka.org/reader.pl?section=autobook og "info automake" lokalt på dit system. Se også autoconf-filerne fra distribution fileutils.

5.11.1. Autoconf, underkataloger

Til større projekter er det en fordel at lægge afgrænsede dele af projektet i underkataloger. Som tidligere nævnt består et autoconf-system af en configure.in og evt. flere Makefile.am. I roden af projektet lægges en Makefile.am der fortæller hvilke underkataloger der skal med, og i hvert katalog en Makefile.am der beskriver hvilke kildetekster der skal med i dette katalog. I dette eksempel er der et katalog med en C-fil og et katalog med en man-fil. Her er listen af filerne før vi går i gang med at køre autoconf-programmerne.


./Makefile.am
./configure.in
man/Makefile.am
man/hello.1
src/Makefile.am
src/hello.c

De enkelte filer ser således ud:

Filnavn: ./Makefile.am


AUTOMAKE_OPTIONS = foreign
SUBDIRS = man src

Filnavn: ./configure.in


AC_INIT(src/hello.c)
AM_INIT_AUTOMAKE( katalog-hello, 1.0 )
AC_PROG_CC
AC_STDC_HEADERS
AC_OUTPUT(Makefile man/Makefile src/Makefile)

Filnavn: man/Makefile.am


man_MANS = hello.1
man_aux = $(man_MANS:.1=.x)
EXTRA_DIST = $(man_MANS)

Filnavn: man/hello.1


.TH HELLO 1 "1 August 2001" "Hello" "Friheden til at programmere"
.SH NAME
hello \- en hilsen
.SH SYNOPSIS
hello
.SH DESCRIPTION
.B hello
skriver "Hello, world!" til standard out.
.SH USAGE
hello
.SH COPYRIGHT
Copyright \(co 2001 Friheden til at programmere.
.SH SEE ALSO
goodbye(1)

Filnavn: src/Makefile.am


bin_PROGRAMS = hello
hello_SOURCES = hello.c

Filnavn: src/hello.c


#include <stdio.h>
int main(void)
{
  printf("Hello, world!\n");
  return 0;
}

Med alle filer på plads er det blot at køre autoconf-programmerne:


hven% aclocal; automake -a; autoconf
hven% ./configure
hven% make

Anvendelse af underkataloger kræver altså ikke meget mere end at have alle filer i samme katalog. ./Makefile.am har AUTOMAKE_OPTIONS som så ikke er i de andre Makefile.am-filer, SUBDIRS angiver i hvilke underkataloger der ligger flere Makefile.am's. Specielt for ./configure.in skal det bemærkes at alle Makefiler skal angives i AC_OUTPUT().

5.11.2. Autoconf/automake eksempel

I eksemplet i Afsnit 5.10 er vist hvordan man manuelt opbygger en Makefile. Skal samme eksempel laves med autoconf/automake, skal der skrives to filer:


## Filnavn: Makefile.am
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = ceks2
ceks2_SOURCES = ceks2.c cfkt.c

# Filnavn: configure.in
AC_INIT(ceks2.c)
AM_INIT_AUTOMAKE( demo-ceks , 1.0 )
AC_PROG_CC
AC_STDC_HEADERS
AC_OUTPUT(Makefile)

De to filer kan se meget anderledes ud, men dette er noget af det simpleste man kan have til et C-program. Herefter er det blot at køre nogle kommandoer:


hven% automake -a
hven% aclocal
hven% autoconf
hven% ./configure
hven% make
hven% make install
hven% make uninstall
hven% make dist # o.s.v...

Det er lidt mere besværligt end det oprindelige eksempel, men vi fik også en make install plus et par andre gode ting for besværet.

5.11.3. Installation af andre filer

I de andre eksempler er vist hvordan C-programmer kan oversættes og installeres i /usr/local/bin ved at bruge makroen bin_PROGRAMS. Har man programmer der skal lægges i /usr/local/sbin er det makroen sbin_PROGRAMS man skal bruge. HTML-filer, kommandofortolker og Perl-programmer skal håndteres anderledes. Her er et lille eksempel på hvad der skal tilføjes for at klare disse fil-typer.


## Filnavn: Makefile.am
...
bin_SCRIPTS = hello.pl hello.sh
sbin_SCRIPTS = minbackup.sh
sysconfdir_DATA = hello.conf
htmldir = /var/www/html/hello
html_DATA = index.html
...

I ovennævnte eksempel er de første tre makroer prædefinerede. Til bin_PROGRAMS er allerede defineret bindir, hvilket kan ændres af den bruger der installere programmet. Hvis bin-filerne skal ned i /bin vil brugeren skrive:


hven% ./configure --bindir=/bin

Listen over prædefinerede kataloger fås nemmest med:


hven% ./configure --help | grep DIR

I førnævnte eksempel er opfundet et nyt katalog med navnet htmldir. Skal brugeren selv have lov til at bestemme hvilket katalog det er, skal der lidt mere til, og vi skal over og rette i configure.in. Der skal ske det at der kommer en ny option som bliver listet sammen med ./configure --help, og en variabel der kan sættes.


# Filnavn: configure.in
...
htmldir=/var/www/html/hello
AC_MSG_CHECKING(htmldir)
AC_ARG_WITH(htmldir,
[  --with-htmldir=DIR      Where to put html/php files],
[ case "$withval" in
  yes|no)
  ;;
  * )
    htmldir="$withval"
    ;;
  esac])
AC_SUBST(htmldir)
AC_MSG_RESULT(${htmldir})
...

Prøv herefter følgende kommandoer:


hven% ./configure --help
hven% ./configure --htmldir=/home/www
hven% make -n install | more

Eksemplet med htmldir er inspireret af automake-manualen og configure.in fra Samba.

5.11.4. Tjek at en funktion er til stede

En god ting at have med i sin configure.in er at tjekke om de funktioner man bruger i C-programmerne er til stede i det pågældende system hvor ./configure køres. I hello.c bruges kun printf() og den tjekkes således:


# Filnavn: configure.in
...
AC_CHECK_FUNCS(printf)
...

Kør autoconf og ./configure for at se resultatet. Flere funktioner kan skrives som parameter med mellemrum imellem. Eksemplet er fundet i Sambas configure.in.

5.11.5. Variable i config.h

En spændende ting man kan lave med configure.in, er at tilføje variable til config.h med dynamisk input på det tidspunkt ./configure køres. For ikke at kommandolinjen skal blive for lang, er det bedst at have AM_CONFIG_HEADER sat i confgiure.in. I følgende eksempel hentes den aktuelle dato og tid når ./configure køres. Den kan herefter findes i config.h og bruges i for eksempel hello.c.


# Filnavn: configure.in
...
CONFDATO=`date "+%Y-%m-%d %H:%M:%S"`
AC_DEFINE_UNQUOTED(CONFDATO,"$CONFDATO",[Dato for kørsel af ./configure])
...

For at få hele systemet opdateret kræves at autoheader køres for at få opdateret config.h.in, autoconf skal køres for at få opdateret configure, og ./configure skal køres for at opdatere config.h. Herefter kan man se resultatet i config.h.


hven% autoheader ; autoconf ; ./configure
hven% cat config.h

Oversætterdirektivet kan nu ses i config.h, og det kan så bruges i hello.c.


/* Filnavn: hello.c */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>

int main(void)
{
#ifdef CONFDATO
  printf("./configure er kørt d.: %s\n", CONFDATO );
#endif
  printf("Hello, world!\n");
  return 0;
}

Herefter er det blot at køre make og ./hello, så vises dato og tid for hvornår ./configure er kørt. Alternativt til dato kunne være kommandoen uname -a for at få oplysninger om det system programmet blev oversat på, eller en option som brugeren der kører ./configure, selv kan definere. Eksemplet er fundet i PHP's configure.in.

5.11.6. Autoconf og andre sprog

Hidtil er sproget C blevet brugt i eksemplerne, men autoconf/automake kan også bruges til både C++ og Fortran. Det er blot et spørgmål om at AC_PROG_CC udskiftes med henholdsvis AC_PROG_CXX eller AC_PROG_F77. Se følgende eksempel:


# Filnavn: configure.in
AC_INIT(hello.c++)
AM_INIT_AUTOMAKE( demo-hello, 1.0 )
AC_PROG_CXX
AC_OUTPUT(Makefile)