Gemeinsame Quellen

Portables Programmieren mit GNU Autoconf und Automake

Wissen | Hintergrund

Den klassischen Installations-Dreisatz kennt jeder Unix-Administrator: ./configure; make; make install. Doch kaum jemand weiß, dass er seinen eigenen Programmen mit wenig Aufwand eine ebenso einfache Installation mitgeben kann.

In jedem Softwareprojekt fallen neben dem eigentlichen Programmieren immer wieder dieselben lästigen Arbeiten an: Basteln an Makefiles, Portierung auf unterschiedliche Systeme, Zusammenstellen der Distribution und Tests. Was liegt näher als diese mühsamen Aufgaben zu automatisieren?

Aus der langjährigen Erfahrung des GNU-Projekts mit portabler Programmierung auf Unix-artigen Systemen sind dafür einige Programmpakete entstanden. Die beiden wichtigsten sind Autoconf und Automake. Die darin enthaltenen Programme erzeugen aus wenigen einfachen Konfigurationsdateien (configure.ac, Makefile.am) automatisch die bekannten configure-Skripte und einige Hilfsdateien. Diese packt der Entwickler einfach unbesehen mit in das Quelltext-Paket.

Allerhand Dateien und Tools sind nötig, damit der Anwender ein Programm einfach per "./configure ; make ; make install" installieren kann. Doch außer seinen Quelltexten muss der Entwickler nur zwei einfache Konfigurationsdateien bearbeiten.

Der Anwender ruft nach dem Entpacken das configure-Skript auf und dieses testet wichtige Systemeigenschaften, etwa den installierten C-Compiler oder die vorhandenen Header-Dateien und Bibliotheksfunktionen. Dabei erzeugt das Skript aus den Hilfsdateien alle für die automatische Anpassung nötigen Files, unter anderem ein maßgeschneidertes Makefile und Zielsystem-spezifische Header-Dateien (u. a. config.h). Darin stehen in Form von Präprozessor-Symbolen (#define) die Ergebnisse der System-Tests.

Beim Programmieren bindet der Entwickler Vorab-Versionen dieser Header ein und fängt die darin enthaltenen Hinweise auf Portierungsprobleme durch bedingt kompilierten Code ab (#ifdef). Bei diesem kompliziert klingenden Ablauf spielen zwar eine ganze Reihe von Programmen und Dateien mit, für den Programmierer ist er dennoch fast so einfach wie für den Anwender; lediglich zwei Dateien muss er während des Entwicklungszyklus bearbeiten und bei einfachen Projekten bestehen diese auch nur aus wenigen Zeilen (siehe Grafik).

Das Listing timer.c zeigt den Quelltext des Beispielprogramms timer. Die ebenfalls zum Projekt gehörende Quelldatei event.c steht zusammen mit allen anderen Beispielen aus diesem Artikel im c't-Listingservice zum Download bereit (siehe Soft-Link). Das Programm wartet bis zu 10 Sekunden auf eine Eingabe und gibt die verstrichene Zeit aus. Erhält es vor Ablauf des Timeouts keine Daten, bricht timer die Eingabe ab. Ein einfaches Makefile genügt zum Übersetzen und Linken:

all:   timer 
timer: timer.o event.o
clean: rm -f timer timer.o event.o

Da das Makefile keine expliziten Aktionen enthält, ruft make die vorgegebenen Befehle auf:

$ make 
cc -c -o timer.o timer.c
cc -c -o event.o event.c
cc timer.o event.o -o timer

Unter GNU/Linux läuft das so problemlos, doch schon dieses einfache Programm enthält einige Fallstricke für die Portierung auf andere Unix-Varianten. So existieren beispielsweise die Funktionen strerror() und strncasecmp() nicht überall. Beim Umschiffen dieser Klippen helfen Autoconf und Automake.

Die meisten GNU/Linux-Distributionen bringen Autoconf und Automake schon mit. Wer die neuesten Versionen benutzen will oder eine andere Unix-Geschmacksrichtung zum Entwickeln benutzt, lädt sie sich über den Soft-Link herunter. Zum Konfigurieren, Kompilieren und zur Installation genügen selbstverständlich die üblichen drei Befehle: configure, make und make install.

Wohlgemerkt: Die Benutzer einer Software, die Automake und Autoconf zur Wartung der Sourcen verwendet, benötigen keins der Pakete (und auch nicht GNU m4 oder Perl). Die erstellten configure-Skripte sollten in jeder Unix-Shell ausgeführt laufen und die meisten make-Programme verstehen die Makefiles.

Unter Windows jedoch fehlt schon die Shell, die das configure-Skript ausführen könnte. Die Open-Source-Projekte Cygwin und MinGW (siehe Soft-Link) bemühen sich jedoch, jeweils eine GNU-kompatible Compiler-Umgebung zur Verfügung zu stellen. Das erklärte Ziel dabei ist, mit Autoconf und Automake erzeugte Pakete auch unter Windows mit den üblichen drei Befehlen zu kompilieren.

Die zentrale Datei für alle auto-Tools heißt configure.ac. Sie enthält die Liste der Tests, die das configure-Skript später auf dem Rechner des Anwenders durchführen soll. Da Autoconf schon eine Vielzahl vordefinierter Tests mitbringt, genügt es meistens, die nötigen einfach in configure.ac aufzuzählen.

Am einfachsten erstellt man eine erste Version mit dem Hilfsprogramm autoscan, das alle Quelltexte im Verzeichnis auf bekannte Portabilitätsprobleme hin überprüft. Damit das korrekt funktioniert, benennt man zunächst das Makefile in Makefile.in um. Seine Ergebnisse schreibt autoscan in die Datei configure.scan, die man einfach umbenennt und etwas nachbearbeitet. Auf besondere Schwierigkeiten weist autoscan in der Datei autoscan.log hin.

Die von autoscan erstellte configure.ac-Datei (siehe Listing) deckt die potenziellen Probleme im Code schon gut ab. Sie enthält in jeder Zeile einen mit # eingeleiteten Kommentar oder einen Test. Der erste heißt AC_PREREQ und prüft die verwendete Autoconf-Version. Die vom aktuellen autoscan angelegte Datei benötigt mindestens Autoconf 2.57.

Die AC_-Funktionen sind Makros des traditionellen Makroprozessors m4. Gemäß dessen Syntax darf zwischen dem Makronamen und der öffnenden Klammer kein Leerzeichen stehen. Strings, die Leerzeichen enthalten, müssen mit [ und ] umschlossen sein.

In der AC_INIT-Zeile definiert der Entwickler Paketname, Paketversion und eine E-Mail-Adresse für Fehlerberichte. Nur diese Zeile bedarf vorerst einer Änderung, zum Beispiel in

AC_INIT(timer,0.0,marcus@gnu.org) 

Das Makro AC_CONFIG_SRCDIR bewirkt, dass das configure-Skript später prüft, ob das aktuelle oder mit --srcdir angegebene Verzeichnis überhaupt die Quelltexte enthält. Als Argument erhält es den Namen einer Datei, die sich als Stichprobe eignet.

Das später automatisch generierte configure-Skript schreibt auf dem Rechner des Anwenders die Ergebnisse der Tests als Definitionen von C-Präprozessor-Symbolen (#define) in eine Header-Datei. Den Dateinamen gibt das Makro AC_CONFIG_HEADER an. Außerdem setzt es bei der weiter unten beschriebenen Anpassung des Makefile das Symbol HAVE_CONFIG_H auf 1.

Den Quelltexten, die configure-Ergebnisse verwenden, stellt der Programmierer daher einige Zeilen voran:

#if HAVE_CONFIG_H  
#include
#endif

Anschließend kann er auf die automatisch definierten Symbole zugreifen, zum Beispiel um ein Banner auszugeben:

printf( "%s Version %s\nreport bugs to %2\n",  
PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_BUGREPORT);

Diese drei Symbole enthalten die in AC_INIT gesetzten Strings.

Die Datei config.h erstellt configure aus der Datei config.h.in, die das Hilfsprogramm autoheader automatisch aus der Datei configure.ac erzeugt. Die Symbole sind in dieser Datei als undefiniert aufgelistet:

#undef PACKAGE_VERSION 

Das Skript configure erstellt daraus die Datei config.h mit der Zeile:

#define PACKAGE_VERSION "0.0" 

Welche Symbole definiert werden, bestimmen die Tests. So prüft

AC_CHECK_HEADERS([stdlib.h string.h  sys/time.h unistd.h]) 

ob das System die genannten Header-Dateien bereitstellt. Wenn dies der Fall ist, schreibt configure Definitionen in config.h, deren Namen es von den Header Dateien ableitet: Alle Buchstaben werden groß geschrieben, Sonderzeichen durch Unterstriche ersetzt, und dann HAVE_ vorangestellt. Wenn stdlib.h existiert, enthält config.h also die Definition von HAVE_STDLIB_H.

Im Quelltext kann der Entwickler die #include-Anweisungen durch Abfrage dieser Symbole schützen, damit sie auf Systemen ohne die Header-Dateien nicht zu Compiler-Fehlern führen:

#ifdef HAVE_STDLIB_H  
#include
#endif

In der anfänglichen Begeisterung sollte der Entwickler nicht unbedingt alle Präprozessor-Symbole auf gut Glück auswerten, sondern lieber probieren, welche wirklich Schwierigkeiten machen. Dazu testet er sein Projekt möglichst auf mehreren Plattformen und gibt es vielleicht auch einem Kollegen, der Zugang zu einer OpenBSD-Maschine hat. Nach einer vernünftigen Testzeit betrachtet er die Software erst mal als Beta-Version, verteilt sie im Netz und wartet auf Bug-Reports.

Manchmal sind komplexere Tests notwendig. So kann man auf manchen alten Systemen die Header-Dateien time.h und sys/time.h nicht gleichzeitig einbinden. In diesem Fall muss man sys/time.h einbinden, wenn es existiert, andernfalls time.h. Dafür gibt es den Standardtest AC_HEADER_TIME, dessen Ergebnis TIME_WITH_SYS_TIME man im Quelltext so abfragt:

#if TIME_WITH_SYS_TIME 
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif

Ein weiteres Portabilitätsproblem sind die verfügbaren Funktionen und die Argumente, die sie erwarten. So fehlen manche Funktionen auf einigen Systemen (strerror) oder die Argumenttypen weichen von den üblichen ab (select). Andere Funktionen gehören erst seit kurzem zum Standard und fehlen daher auf älteren Systemen (strncasecmp).

Analog zu AC_CHECK_HEADERS testet AC_CHECK_FUNCS, ob die Standardbibliothek die genannten Funktionen enthält und setzt entsprechende HAVE_-Symbole. Das Beispielprogramm nutzt in Zeile 11 strerror, um die vom System bestimmte Fehlermeldung auszugeben. Mit einer kleinen Anpassung läuft es auch auf Unix-Varianten, die diese Funktion nicht mitbringen:

#if HAVE_STRERROR 
fprintf (stderr, "Fehler: %s", strerror (ENOMEM));
#else
fprintf (stderr,
"Fehler: Kein freier Speicher mehr");
#endif

Neben den Systembibliotheken können auch eigentümliche Compiler Portabilitätsprobleme hervorrufen. Deshalb gibt es Standardtests für verschiedene Compiler (C, C++) und für diverse andere Werkzeuge, die man bei der Übersetzung von Programmen oft benötigt. Das Beispiel benötigt nur den C-Compiler; aus dem Makro AC_PROG_CC in configure.ac fügt autoconf einen Test auf dessen Version in das configure-Skript ein.

Dieser Test definiert jedoch in der Header Datei config.h keine Symbole, sondern nutzt einen zweiten Mechanismus: In allen bearbeiteten Dateien ersetzt das configure-Skript die Ersetzungsvariable @CC@ durch den gefundenen Compiler-Aufruf. Die Dateien, in denen das Skript solche Variablen ersetzten soll, haben die Endung .in und werden ohne diesen Anhang in AC_CONFIG_FILES aufgelistet. Im Beispiel erzeugt configure also aus Makefile.in das Makefile.

Um statt des vom System vorgegebenen C-Compilers den zu benutzen, den das Skript beim Anwender findet, fügt man in Makefile.in einfach die Zeile

CC = @CC@ 

ein. Autoconf kennt auch geeignete Compiler-Schalter und stellt sie in der Ersetzungsvariable @CFLAGS@ zur Verfügung. Einige wenige Definitionen landen in @DEFS@. Dazu gehört auch HAVE_CONFIG_H, die ja erforderlich ist, damit der geänderte Quelltext die Datei config.h mit den übrigen Definitionen überhaupt einbindet. Damit der Compiler die Datei findet, muss das oberste Quelltextverzeichnis (@top_srcdir@) zum include-Suchpfad gehören. Damit make diese Informationen auch erhält, fügt man eine weitere Zeile am Anfang von Makefile.in ein:

CFLAGS = @CFLAGS@ @DEFS@  -I@top_srcdir@ 

Damit ist alles erledigt, was für eine erste Übersetzung des Quelltextes mit Autoconf notwendig ist. Der Aufruf autoconf erzeugt das configure-Skript, das man anschließend gleich ausprobiert:

$ ./configure 
checking for gcc... gcc
[ ... viele weitere Tests ... ]
checking for strncasecmp... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h

Wie man in der Ausgabe sieht, hat configure die Dateien Makefile und config.h erstellt. Die Datei config.status enthält alle Testergebnisse, auch solche, die sich nicht in den Ausgabedateien wiederfinden. Ferner erzeugt configure eine Datei config.log, die alle Ausgaben der Tests und weitere Informationen enthält.

Ein Testlauf von make liefert jetzt eine ganz andere Ausgabe als der mit dem handgeschriebenen Makefile weiter oben:

$ make 
gcc -g -O2 -DHAVE_CONFIG_H -I. -c -o timer.o timer.c
gcc -g -O2 -DHAVE_CONFIG_H -I. -c -o event.o event.c
gcc timer.o event.o -o timer

Statt cc ruft make den GNU-Kompiler gcc (@CC@) auf und gibt ihm die üblichen Debug- und Optimierungsflags (@CFLAGS@) sowie die Definition von HAVE_CONFIG mit (@DEFS@). Zum Suchpfad für include-Dateien gehört auch das Quell-Verzeichnis (@top_srcdir@).

Viele configure-Skripte untersuchen nicht nur die Systemumgebung, sondern passen die Quelltexte auch an die Wünsche des kompilierenden Anwenders an. Dazu dienen unter anderem die Kommandozeilenoptionen, die mit --enable beginnen. Der Entwickler definiert sie mit einem AC_ARG_ENABLE-Makro in der Datei configure.ac. Das erste Argument ist der Name der Option; um also dem Beispielskript den Parameter --enable-timeout beizubringen, lautet er "timeout". Das zweite Argument ist der Hilfstext, der der Ausgabe von ./configure - -help hinzugefügt wird. Um den Formatierungsvorgaben zu entsprechen, verwendet man dazu am besten das Hilfsmakro AC_HELP_STRING.

Das dritte Makroargument ist ein Shell-Skript, das configure ausführt, wenn der Benutzer die Option gesetzt hat. Die Variable $enableval enthält dann den mit --enable-timeout=Wert übergebenen Wert. Beim Aufruf ohne einen Wert enthält $enableval den String "yes". Hat der Benutzer --disable-timeout eingetippt, enthält enableval den String "no":

def_timeout = -1 
AC_ARG_ENABLE([timeout],
AC_HELP_STRING( [--enable-timeout=SEC],
[timeout in SEC seconds (default is 5)]), [def_timeout=$enableval])
if test "$def_timeout" = yes; then
def_timeout = 5
fi
if test "$def_timeout" = no; then
def_timeout = -1
fi

Außer dem AC_ARG_ENABLE-Makro enthält dieser Abschnitt zwei Passagen Shell-Code, die autoconf unverändert in das configure-Skript übernimmt. Das tut es grundsätzlich mit allen Elementen, die es nicht als m4-Makros erkennt.

Mit dieser Definition ist $def_timeout nur eine Shell-Variable innerhalb des configure-Skripts. Um ein Symbol für config.h zu definieren, verwendet man den Autoconf-Befehl AC_DEFINE oder AC_DEFINE_UNQUOTED. AC_DEFINE überträgt das zweite Argument wörtlich, während AC_DEFINE_UNQUOTED es erst in der Shell auswertet. Das erste Argument ist der Name des neu zu definierenden Symbols und das dritte Argument ein Kommentar.

AC_DEFINE_UNQUOTED( 
DEFAULT_TIMEOUT, $def_timeout, [Default timeout for waiting on an event.])

Mit dem Aufruf von autoheader wird dieses Symbol in config.h.in übertragen

/* Default timeout for waiting on an event. */  
#undef DEFAULT_TIMEOUT

Das beim nächsten autoconf-Lauf erzeugte configure-Skript setzt dieses Präprozessor-Symbol entsprechend der Benutzervorgabe. Im Quelltext (event.c) reichen zwei kleine Änderungen, um sie auszuwerten. DEFAULT_TIMEOUT wird nur gesetzt, wenn es noch keinen Wert aus config.h hat:

#ifndef DEFAULT_TIMEOUT  
#define DEFAULT_TIMEOUT 10
#endif

und das Timeout-Feature wird nur einkompiliert, wenn der Anwender es nicht abgeschaltet hat (DEFAULT_TIMEOUT=-1):

#if DEFAULT_TIMEOUT >= 0 
timeout = &to;
#endif

Nun genügt dem Anwender der Aufruf ./configure -enable-timeout=2 && make um eine ungeduldigere Fassung des Beispielprogramms zu bauen.

So weit, so gut; autoconf generiert aus einer einfachen Eingabedatei ein trickreiches Shell-Skript, das aus config.h.in und Makefile.in die zum Kompilieren nötigen Dateien baut. Das Skript autoheader nimmt ihm die Arbeit an config.h.in ab, aber was ist mit Makefile.in? Auch die lästige Arbeit daran nimmt dem Programmierer ein Programm ab: automake.

Dafür muss der Entwickler lediglich in configure.ac nach AC_INIT die Zeile AM_INIT_AUTOMAKE([foreign]) einfügen. Mit der Option foreign signalisiert man Automake, dass es sich hier nicht um ein vollwertiges GNU-Programm handelt und es somit einige Regeln über die erforderlichen Dokumentations-Dateien lockerer sehen soll. Die Datei Makefile.am hat zwei Zeilen:

bin_PROGRAMS  = timer  
timer_SOURCES = timer.c event.c

Die meisten Funktionen von Automake erreicht man durch die Definition solcher Zuweisungen. Die Variable bin_PROGRAMS ist die Liste von Programmen, die übersetzt und ins bin-Verzeichnis installiert werden sollen. Entsprechend gibt es sbin_PROGRAMS für Programme, die ins sbin-Verzeichnis gehören. Diese Pfadangaben gelten relativ zu dem mit der configure-Option --prefix angegebenen Verzeichnis, standardmäßig /usr/local. Die Namen dienen als Basis zur Definition der Quelltextdateien für jedes einzelne Programm: Die Variable heißt wie das Programm plus dem String "_SOURCES". Ihr weist man einfach die Liste der erforderlichen Quelltexte und lokalen Header-Dateien zu. System-Header-Dateien gehören nicht dazu, denn um deren Abhängigkeiten kümmert sich Automake selbst.

Da Automake eine Ergänzung zu Autoconf ist, müssen weitere Tests in das Programm importiert werden. Dies geschieht automatisch mit aclocal. Danach ruft man automake auf, das aus Makefile.am Makefile.in erzeugt. Die Option -a fügt dem Verzeichnis fehlende Hilfsdateien hinzu.

Auch das Eintippen dieser Kommandozeilen kann sich der Programmierer abnehmen lassen. Das Tool autoreconf ruft alle erforderlichen Utilities in der richtigen Reihenfolge auf. Leider hat es andere Parameter: mit -i statt -a fügt es fehlende Hilfsdateien zum Verzeichnis hinzu.

Aus dem von automake erzeugten Makefile.in generiert configure ein Makefile mit allen Schikanen. Rund 60 make-Targets lassen keine Wünsche offen. Die nahezu automatische Installation mit make && make install ist nur der Anfang: Zum Beispiel löscht make clean die Zwischenprodukte des Compilers, make distclean alles Unnötige und make uninstall entfernt die installierten Dateien wieder aus dem System.

Für eine Testinstallation oder um ein Binärpaket zu erstellen, kann man durch Definition der Make-Variable DESTDIR eine andere Stelle im Dateisystem als virtuelles Wurzelverzeichnis verwenden:

make install DESTDIR=/tmp/test-install 

Automake erstellt auch Regeln, um die Makefiles und andere generierte Dateien automatisch neu zu erzeugen, wenn die Eingabedateien wie Makefile.am sich verändern. Das ist ideal während der Entwicklung, kann aber unangenehm für den Benutzer sein, der vielleicht nur aus Versehen die Timestamps auf den Dateien verändert hat. Darum empfiehlt es sich, in der Datei configure.ac nach AM_INIT_AUTOMAKE das Macro AM_MAINTAINER_MODE aufzurufen. Damit werden diese automatischen Regeln zum Aufruf von automake und anderen Entwicklerwerkzeugen nur aktiviert, wenn man configure mit der Option --enable-maintainer-mode aufruft.

Der Aufruf make dist packt alle Dateien, die der Benutzer benötigt, automatisch in eine tar.gz-Datei. Deren Name besteht aus den in configure.ac per AC_INIT-Makro festgelegten Werten, für das Beispielprojekt lautet er also timer-0.0.tar.gz. Das Paket enthält das configure-Skript und alle wichtigen generierten Dateien, sodass der Benutzer die auto-tools nicht zur Übersetzung des Programms benötigt.

Noch umfangreicher als make dist arbeitet make distcheck: Der Befehl packt das Tar-File, entpackt es in einem separaten Verzeichnis, kompiliert es dort, absolviert einen Testlauf (make check) und installiert es zur Probe in ein temporäres Verzeichnis. Damit kann man viele Fehler vor dem eigentlichen Release abfangen. Und das alles mit nur zwei Zeilen in Makefile.am!

Bei Projekten mit mehreren Programmen empfiehlt es sich, die mehrfach benötigten Hilfsfunktionen zuerst zu kompilieren und in einer statischen Bibliothek zusammenzufassen. Gegen diese lässt man dann die einzelnen Programme linken. Kleine Änderungen an configure.ac und Makefile.am genügen, um das Beispielprojekt auf dieses Verfahren umzustellen.

Manche Systeme brauchen das Programm ranlib, um die Bibliothek zu packen. Ob das so ist und wie das Programm jeweils heißt prüft der Test AC_PROG_RANLIB, den man zu configure.ac hinzufügt.

In Makefile.am definiert der Programmierer Bibliotheken, die nicht auf dem System installiert werden sollen, mit noinst_LIBRARIES. Für die _SOURCES-Variable ersetzt er alle Punkte im Dateinamen durch Unterstriche. Dass die Bibliothek beim Linken des Programms timer hinzugezogen wird, erreicht man mit einer _LDADD-Variable:

noinst_LIBRARIES   = libevent.a 
libevent_a_SOURCES = event.c
bin_PROGRAMS = timer
timer_SOURCES = timer.c
timer_LDADD = libevent.a

Das ist schon alles - nach dem nächsten autoreconf kompiliert sich das Projekt mit einer temporären Bibliothek libevent.a.

Dieser Mechanismus ist auch die Grundlage für "Replacement Functions", eigene Ersatzroutinen für eventuell fehlende Systemfunktionen. Das Beispielprojekt verwendet mit strncasecmp eine Funktion, die in einigen älteren Systemen fehlt. Statt das Programm so umzuschreiben, dass es auch ohne strncasecmp auskommt, packt man die Funktion lieber mit ins Paket und lässt sie bei Bedarf kompilieren. Wenn configure jedoch eine System-eigene Funktion festgestellt, soll das Programm lieber diese benutzen.

Eine Liste solcher Funktionen gibt man mit AC_REPLACE_FUNCS in der configure.ac-Datei an. Dieser Test setzt das Präprozessor-Symbol HAVE_STRNCASECMP, falls die Funktion im System vorhanden ist, und definiert die Ersetzungsvariable LIBOBJS, die dann die fehlenden Funktionen enthält.

AC_REPLACE_FUNCS([strncasecmp]) 

Um diese Funktionen bei der Übersetzung in das Programm einzubinden, fügt man der timer_LDADD Variable in Makefile.am die Liste der fehlenden Objekte hinzu:

timer_LDADD = libevent.a @LIBOBJS@ 

Das so generierte Makefile setzt voraus, dass die Ersatzfunktion strncasecmp in der Datei strncasecmp.c implementiert ist. Darum kann sich der Programmierer selbst kümmern oder auf die gnulib des GNU-Projekts zurückgreifen (siehe Soft-Link). Sie enthält portable Versionen der oft gebrauchten Ersatzfunktionen. Nur ihren Prototyp muss man dem Programm noch zur Verfügung stellen. Dazu fügt man folgende Zeilen der Datei timer.c hinzu:

#if !HAVE_STRNCASECMP 
int strncasecmp (const char *str1, const char *str2, size_t n);
#endif

Autoconf und Automake nehmen dem Programmierer zwar das Programmieren von Workarounds für verschieden Zielsysteme nicht ab, helfen aber sehr, die kritischen Stellen zu identifizieren und je nach System die richtige Quelltext-Version automatisch auszuwählen. Auch wenn es nur ein Zielsystem gibt, lohnt sich der Einsatz von Autoconf und Automake, denn das lästige Basteln an Makefiles gehört damit der Vergangenheit an.

Wer tiefer in die Materie einsteigen will, sollte wissen, dass es einige weitere Portabilitätstools von GNU gibt: libtool erlaubt das Erzeugen von dynamisch und statisch gelinkten Bibliotheken auf vielen Systemen. Dejagnu stellt eine Testumgebung zur Verfügung. Mit diesen reichhaltigen Werkzeugen kann man den Unterschiedenen zwischen den Unix-Systemen mit Gelassenheit begegnen. (es)

[1] Autoconf-Handbuch, www.gnu.org/manual/autoconf-2.57/autoconf.html

[2] Automake-Handbuch, www.gnu.org/manual/automake-1.7.2/automake.html

[3] Online-Buch GNU Automake, Autoconf, and libtool

ct.de/0321208

Das Beispielprogramm timer sieht einfach aus, enthält jedoch schon Portabilitätsprobleme.

  1 int wait_for_event (char **event); 
2
3 int
4 main (int argc, char *argv[])
5 {
6 char *event;
7 int result = wait_for_event (&event);
8
9 if (result < 0)
10 {
11 fprintf (stderr, "Fehler: %s", strerror (ENOMEM));
12 return EXIT_FAILURE;
13 }
14 if (!strncasecmp (event, "fehler:", 7))
15 {
16 fprintf (stderr, "%s", event);
17 return EXIT_FAILURE;
18 }
19
20 printf ("%s\n", event);
21 free (event);
22
23 return EXIT_SUCCESS;
24 }

Die von autoscan erzeugte Vorlage für configure.ac reicht für viele Projekte schon aus.

AC_PREREQ(2.57)
AC_INIT(Timer, 0.0, marcus@gnu.org)
AC_CONFIG_SRCDIR([timer.c])
AC_CONFIG_HEADER([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h string.h sys/time.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_TIME

# Checks for library functions.
AC_FUNC_SELECT_ARGTYPES
AC_CHECK_FUNCS([select strerror strncasecmp])

AC_CONFIG_FILES([Makefile])
AC_OUTPUT