--title autoconf und automake --author Andreas Krennmair --date today Praktisch jede Software unter Linux wird mit ./configure ; make ; make install konfiguriert, compiliert und installiert. Ich möchte heute zeigen, wie man selbst mit Autoconf und Automake den Konfigurations- und Übersetzungsprozess automatisieren kann, und was das für portable Software bringen kann. --newpage agenda --heading Agenda * Einführung * Makefiles * Autoconf alleine * Autoconf und Automake --newpage intro --heading Einführung: Geschichte der automatisierten Compilierung unter Unix * Anfangs: Shellskripte namens make im Sourceverzeichnis (bis Unix V6) * Ab Unix V7: Programm make, Information aus Datei makefile * makefile enthält Informationen, welchen Dateien (Targets) aus welchen Dateien (Dependencies) erzeugt werden, und welche Kommandos dazu aufgerufen werden müssen. * Makefiles funktionierten anfangs ganz gut, bis die ersten Unix- Varianten erschienen, die subtil anders als die bisherigen Unixe waren. Software sollte aber portabel bleiben. * Einfache Lösung: ein Target pro Zielplattform. * Nachteil: bei mehr Plattformen ansteigender Wartungsaufwand --newpage intro-conf --heading Einführung: erste automatisierte Konfiguration * Makefile-Lösung hörte auf zu skalieren, als immer mehr und immer obskurere Unix-Varianten auftauchten, auf die die Entwickler von Software noch dazu keinen Zugriff mehr hatten. * Erste Lösung Mitte bis Ender der 80er Jahre: Configure * Larry Wall wollte, dass seine Software (insbesondere Perl) portabel auf möglichst vielen Unix-Plattformen läuft. * Schreib Shellskript Configure, das Informationen über das System sammelte, und aus *.SH-Dateien dementsprechende Dateien generierte (Makefile.SH -> Makefile) * Vorteil: Perl konnte ohne grossen Portierungsaufwand auf vielen, tewilweise recht obskuren Unix-Systemen betrieben werden. --newpage intro-gnu --heading Einführung: Konfiguration für GNU * GNU-Software sollte möglichst portabel sein * GNU-Projekt griff Larry Wall's Idee auf, und realisierte im wesentlichen zwei Frameworks, um das Konfigurieren und Übersetzen von GNU-Software möglichst portabel und einfach wartbar zu halten. * Konfiguration: autoconf * Übersetzung: automake * Status heute: Autoconf und Automake sind ein Quasi-Standard bei Freier Software/Open Source --newpage makefiles --heading Einfache Makefiles Makefiles bestehen im wesentlichen aus zwei Bereichen: * Variablendefinitionen * Zieldefinitionen Variablen werden verwendet, um gleiche "Textbausteine", die öfters im Makefile vorkommen, zusammenzufassen und parametrisierbar zu machen, z.B. Compilerkommandos, Compilerflags, Ausgabedatei, ... Zieldefinitionen geben an, welche Datei erzeugt werden soll, von welchen Dateien diese Datei abhängig ist, und mit welchem Kommando die Datei aus diesen Abhängigkeiten erzeugt wird. Diese Zieldefinition definiert ein sog. "Target". Wird ein Target aufgerufen, so wird das Kommando nur ausgeführt, wenn die zu generierende Datei noch nicht existiert, oder wenn eine der Abhängigkeiten erst generiert werden muss, oder wenn eine der Abhängigkeiten neuer ist als die bestehende Datei So werden unnötige Compile-Vorgänge vermieden. --newpage example1 --heading Beispiel 1: einfaches Makefile --beginoutput # Kommentar LATEX=pdflatex # Variablendefinition ac-am.pdf: ac-am.tex # Zieldefinition $(LATEX) ac-am.tex # Kommando --endoutput --newpage example2 --heading Beispiel 2: gleichartige Targets zusammenfassen --beginoutput LATEX=pdflatex RM=rm -f PDFFILES=ac-am.pdf all: $(PDFFILES) %.pdf: %.tex $(LATEX) $< clean: $(RM) $(PDFFILES) *.aux *.log --endoutput --newpage --heading Beispiel 3: modulares C-Programm übersetzen --beginoutput CC=gcc CFLAGS=-Os -Wall OBJS=foo.o bar.o baz.o quux.o OUTPUT=xyzzy all: $(OUTPUT) $(OUTPUT): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o $(OUTPUT) $(OJBS) $(LIBS) %.o: %.c $(CC) $(CFLAGS) $(DEFINES) -c $< clean: $(RM) $(OBJS) $(OUTPUT) core *.core .PHONY: all clean --endoutput --newpage limitations --heading Limtationen von Makefiles Makefiles funktionieren zwar bei kleineren, einfachen Programmen, wer jedoch größere, portable Software schreiben will, stößt mit make und Makefiles schnell an Grenzen. Die Unterschied zwischen den einzelnen Unix-Systemen sind z.B. folgende: * Strukturen unterscheiden sich * Funktionen sind unterschiedlich deklariert * #defines sind anders benannt oder existieren nicht * Manche Funktionen sind nicht mehr in der libc, sondern in externe Libraries ausgelagert (z.B. Sockets nach libsocket). Auf diese Unterschiede kann make nicht eingehen. Deswegen muss man einen Konfigurationsmechanismus einführen, der dies kann. --newpage autoconf1 --heading Autoconf, Schritt 1: configure-Skript erzeugen Autoconf bietet die Möglichkeit, auf eine große Anzahl von Kommandos und Tests zurückzugreifen, um möglichst alle relevanten Systemparameter abzurufen. Diese Tests werden in einer Datei configure.in abgelegt, aus dem dann mit dem Kommando autoconf die Datei configure erzeugt wird. Mit dem Kommando autoheader wird die Datei config.h.in erzeugt. Ruft man ./configure auf, so sammelt das configure-Skript die Konfigurationsinformationen, und generiert aus config.h.in die Datei config.h sowie alle in configure.in angegebenen zu konfigurierenden Dateien, das ist meistens Makefile.in, aus der Makefile erzeugt wird. Die configure.in-Datei lässt sich übrigens erzeugen, indem man autoscan aufruft, und die resultierende Datei configure.scan in configure.in umbenennt. --newpage autoconf2 --heading Autoconf, Schritt 2: Makefile.in erstellen Die Datei Makefile.in wird wie ein normales Makefile geschrieben, mit dem Unterschied, dass für bestimmte Variablen, deren Wert vom configure-Skript bestimmt werden, spezielle Platzhalter eingefügt werden. Das sieht dann z.B. so aus: --beginoutput CC=@CC@ CFLAGS=@CFLAGS@ @DEFS@ LDFLAGS=@LDFLAGS@ LIBS=@LIBS@ --endoutput Der Rest des Makefile sieht wie ein normales Makefile aus. Um auf sämtliche ermittelten Parameter zugreifen zu können, müssen die einzelnen C-Sourcefiles nur noch die Datei config.h inkludieren. Damit ist Autoconf vollständig integriert und das Buildsystem darauf angepasst. --newpage autoconf3 --heading Autoconf, Zusammenfassung In einer Minute zur Sourcekonfiguration mit Autoconf: --beginshelloutput $ autoscan && mv configure.scan configure.in --- $ $EDITOR configure.in --- $ autoconf --- $ autoheader --- $ $EDITOR Makefile.in --endshelloutput Fertig! --newpage confex1 --heading Beispiel für configure.in (1) --beginoutput AC_PREREQ(2.57) AC_INIT(akpop3d, 0.7.7, ak@synflood.at) AC_CONFIG_SRCDIR([authenticate.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC AC_PROG_INSTALL --endoutput --newpage confex2 --heading Beispiel für configure.in (2) --beginoutput # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h shadow.h stdlib.h string.h sys/file.h sys/socket.h sys/time.h syslog.h unistd.h]) # Checks for typedefs, structures, and compiler # characteristics. AC_C_CONST AC_TYPE_UID_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_HEADER_TIME --endoutput --newpage confex3 --heading Beispiel für configure.in (3) --beginoutput # Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_FORK AC_FUNC_REALLOC AC_FUNC_SELECT_ARGTYPES AC_FUNC_STAT AC_CHECK_FUNCS([atexit dup2 getspnam inet_ntoa memchr memset select socket strchr strerror strncasecmp strrchr]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT --endoutput --newpage functions --heading Weitere nützliche Autoconf-Funktionen * AC_CHECK_LIB(library,symbol): wenn symbol in Library library gefunden wird, wird -llibrary zu den LDFLAGS hinzugefügt und HAVE_LIBLIBRARY=1 in config.h definiert. * AC_DEFINE([KEY],[VALUE]): in config.h wird #define KEY VALUE eingetragen. * AC_ARG_WITH(option,[beschreibung]): das configure-Skript um eine --with-option Option erweitern. * AC_ARG_ENABLE(option,[beschreibung]): das configure-Skript um eine --enable-option Option erweitern. --newpage funcex1 --heading Beispiele zu nützlichen Autoconf-Funktionen --beginoutput AC_ARG_WITH(openssl, [ --with-openssl use OpenSSL]) if test "$with_openssl" != "no" ; then AC_CHECK_LIB(crypto,BIO_new) AC_CHECK_LIB(ssl,SSL_new) fi AC_ARG_ENABLE(rfc2449, [ --enable-rfc2449 enable RFC 2449 support]) if test "$enable_rfc2449" != "no" ; then AC_DEFINE([ENABLE_RFC2449],[1],[rfc2449]) fi --endoutput --newpage funcauto --heading Funktionsweise von Autoconf * Vorgefertige Tests in Form von m4-Makros verfügbar * Autoconf lässt configure.in durch m4 laufen, daraus entsteht configure-Skript, was nicht anders als ein Shellskript ist. * -> man kann durch Einfügen von eigenem Shellcode eigene Tests durchführen. * -> oder man greift auf http://ac-archive.sourceforge.net/ zurück, einem umfangreichen Archiv von hunderten Autoconf-Macros. --beginoutput if test x`uname -s` = "xDarwin" ; then AC_DEFINE([HAVE_DARWIN],[1],[define whether we have Darwin]) fi --endoutput --newpage macroself --heading Autoconf-Makros selbst schreiben Autoconf-Makros sind ein Mischmasch aus m4-Skript und Shellskript. --beginoutput AC_DEFUN([AC_C_LONG_LONG], [AC_CACHE_CHECK(for long long int, ac_cv_c_long_long, [if test "$GCC" = yes; then ac_cv_c_long_long=yes else AC_TRY_COMPILE(,[long long int i;], ac_cv_c_long_long=yes, ac_cv_c_long_long=no) fi]) if test $ac_cv_c_long_long = yes; then AC_DEFINE(HAVE_LONG_LONG) fi ]) --endoutput --newpage automake1 --heading Automake: Einführung Automake ist dafür gedacht, den eigentlichen Übersetzungsprozess so weit wie möglich zu vereinfachen, und dem User das Schreiben von eigenen Makefile.in's abzunehmen. Automake setzt Autoconf voraus. Die Makefile.am-Datei besteht wie ein Makefile aus Targets, die quasi beliebig benannt werden können, und alle Kommandos enthalten wie auch ein Target in einem Makefile oder Makefile.in. Zusätzlich existieren eine Reihe von speziellen Variablen, mit denen das Übersetzen von Software einfacher wird. --beginoutput bin_PROGRAMS = hello hello_SOURCES = hello.c main.c EXTRA_DIST = hello.h --endoutput --newpage autocmd --heading Automake: mehr Kommandos --beginshelloutput $ $EDITOR Makefile.am --- $ autoscan && mv configure.scan configure.in --- $ autoheader --- $ aclocal --- AM_INIT_AUTOMAKE(programname,version) in configure.in eintragen. --- $ automake -a --- $ autoconf --- $ ls -l Makefile.in configure -rw-r--r-- 1 ak staff 16048 16 Mar 20:03 Makefile.in -rwxr-xr-x 1 ak staff 123354 16 Mar 20:03 configure $ --endshelloutput --newpage primaries --heading Automake: Primaries Die _PROGRAMS; _SOURCES, etc. Suffixe, die vorher gesehen haben, nennen sich übrigen "Primaries". Weitere Primaries sind z.B.: * DATA: gibt Datendateien an, die 1:1 mitinstalliert, ansonsten aber ignoriert werden. * HEADER: damit werden Headerfiles spezifiziert, die zusammen mit Libraries installiert werden sollen. * SCRIPTS: ausführbare Skripte, die ebenfalls installiert werden, jedoch nicht compiliert oder gestripped werden. * MANS: gibt Manpages an, die ebenfalls mitinstalliert werden. Die Grundbedürfnisse für einfache und problemlose Konfigurations-, Übersetzungs- und Installationsroutinen wäre damit gedeckt. --newpage recam --heading Rekursives Automake Um den Inhalt von Unterverzeichnissen in den Automake-Vorgang miteinzubeziehen, muss man lediglich alle relevanten Unterverzeichnisse über die SUBDIRS-Variable angeben. --beginoutput SUBDIRS = m4 src doc --endoutput In jedem Unterverzeichnis muss natürlich wiederum eine Makefile.am angelegt und daraus eine Makefile.in erzeugt werden. Ausserdem muss das dann zu erzeugende Makefile in der configure.in angegeben werden, und zwar via AC_CONFIG_FILES. --newpage resumee --heading Resümee * make mag veraltet und eingerostet wirken (wird seit Ende der 1970er eingesetzt), bietet aber ein mächtiges System, um Abhängigkeiten zu überprüfen, und unnötige Compilevorgänge zu minimieren. * Autoconf bietet ein mächtiges System, um vielerlei systemabhängige Konfigurationspunkte in Erfahrung zu bringen, was wiederum einen Eckpfeiler für systemnahe und portable Programmierung bildet. * Automake macht es für Entwickler besonders einfach, Softwarepakete in eine Form zu bringen, dass sie übersetzt und installiert werden können. * Autoconf und Automake mögen suboptimale Lösungen sein (./configure dauert lange, configure und Makefile.in sind bei Automake extrem gross), stellen jedoch eine frei verfügbare, einfach anzuwendende und vor allem weit verbreitete Lösung dar. --newpage literacy --heading Literaturempfehlungen * Das "Autobook": Autoconf, Automake and Libtool http://sources.redhat.com/autobook/autobook/autobook_toc.html * Autoconf Dokumentation: http://www.delorie.com/gnu/docs/autoconf/autoconf_toc.html * Automake Dokumentation: http://www.delorie.com/gnu/docs/automake/automake_toc.html --newpage end --heading Und jetzt... --huge Zum praktischen Teil!