Aus Linux-Magazin 01/2001

Quellcodebäume

Mit Hilfe der Werkzeuge automake und autoconf lassen sich schnell installierbare und portable Quelltextbäume erstellen.

Und wieder einmal haben Sie ein tolles GNOME-Programm geschrieben – das kann man gar nicht oft genug machen -, aber es hapert doch an einem bestimmten Ende? Nämlich der Programmerstellung? Ihnen bereitet es Kopfschmerzen, die Benutzer Zeilen wie

gcc -c quelle.c `gnome-config libs cflags gnome gnomeui gnomecanvaspixbuf' -o quelle.o

eintippen zu lassen, vielleicht auch noch einmal für jede der Dateien? Und mit zusätzlichen Compilerflags? Um kurz darauf zu verlangen, das Ganze zu linken? Und zu guter Letzt müssen die Benutzer das fertige Binary auch noch von Hand in das Zielverzeichnis kopieren?

Doch Sie wollen viel lieber Komfort, Portabilität und Geschwindigkeit für die User einbauen. Das sind zwar gleich drei große Wünsche auf einmal – aber sie lassen sich selbstverständlich erfüllen.

Listing 1: Ein
simples Makefile für ein GNOME-Programm

1: CC=/usr/bin/gcc
2: CFLAGS=`gnome-config cflags gnome gnomeui`
3: LDFLAGS=`gnome-config libs gnome gnomeui`
4: OBJ=beispiel.o eins.o zwei.o
5: BINARIES=beispiel
6: 
7: all: $(BINARIES)
8: 
9: beispiel: $(OBJ)
10:     $(CC) $(LDFLAGS) -o $<@> $(OBJ)
11: 
12: .c.o:
13:     $(CC) $(CFLAGS) -c $<
14: 
15: clean:
16:     rm -rf $(OBJ) $(BINARIES)

Vorbau

Die obige Szenerie ist etwas übertrieben, denn Makefiles sind nicht schwierig zu schreiben. Es reicht, einfach make im Quelltextverzeichnis einzugeben, und das Programm wird erstellt. Ein simples Makefile für ein kurzes C-Programm in GNOME kann etwa wie in Listing 1 aussehen. Dennoch ist dieser Weg nicht immer und überall gangbar, denn die Annahmen von Orten, an denen sich Programme befinden, Pfadnamen und anderem muss der Benutzer eventuell noch von Hand editieren.

Das ist nicht das, was man haben möchte. Was man haben möchte und was Sie bestimmt schon einmal gesehen haben, manifestiert sich für den Benutzer im Wesentlichen in der Eingabe dreier Befehle:

./configure
make
make install

Der geneigte Leser wird sich eventuell verwundert die Augen reiben, da dieses Thema schon mal in diesem Magazin behandelt worden ist. Allerdings ist erstens nicht jeder interessierte Leser seit dem ersten Artikel dabei und zweitens ist bisher noch nicht auf die GNOME-spezifischen Eigenarten verwiesen worden, die beim Aufbau eines Quelltextbaums beachtet werden müssen. Und genau einen solchen wollen wir jetzt für unser Programm erstellen.

Struktur

Wichtig ist eine Struktur für den Quelltextbaum. Der Quellcode selber sollte von anderen Dingen, etwa der Dokumentation oder den Dateien zur Konfiguration, getrennt liegen, damit man leichter die Übersicht behält.

Wir erstellen uns also als Erstes ein Verzeichnis mit dem Namen beispiel als Platz für unseren Baum, erzeugen darin das Verzeichnis src für unseren Code und werfen dort alles hinein. Um bei den Dateien aus dem Makefile in Listing 1 zu bleiben, sind das die Dateien beispiel.c sowie eins.c, eins.h, zwei.c und zwei.h. Die erste dieser Dateien sieht aus wie in Listing 2, die restlichen vier sind leer und nur des Beispiels wegen mit von der Partie.

Dokumentation: Ein leidiger Schritt für jeden Programmierer, der von Mitentwicklern und Benutzern aber gedankt wird. Folgende Dateien gehören zu einem Quellverzeichnis:

Das sind die wichtigsten Dateien, es gibt noch andere. Woher bekommt man diese Dateien? Die ersten drei legt man natürlich selber an, wohingegen die letzten beiden am besten aus dem Verzeichnis von automake kopiert werden.

automake

Bei automake und autoconf handelt es sich um zwei kleine GNU-Tools, von [4] zu beziehen. Sie erzeugen unsere Konfigurationsdateien, die dann nur noch vom kompilierwilligen User auszuführen sind. Dafür muss er die Programme nicht selbst installiert haben, es genügt das Quelltextpaket unserer Anwendung. Wie die beiden Programme installiert werden können, wenn Sie sie nicht einfach aus der Entwicklersektion Ihrer Distributions-CDs herausziehen, können Sie sich inzwischen wahrscheinlich schon denken.

Bevor wir aber automake und autoconf auf unsere Quellen ansetzen, ist noch etwas Vorarbeit zu leisten. Wir benötigen noch zwei Dateien, die angelegt werden müssen, namentlich configure.in und Makefile.am, zu sehen in den Listings 3 und 4.

Die beiden Dateien sind im Grunde leicht erklärt, wir beginnen mit configure.in: In der ersten Zeile wird autoconf initialisiert. In Klammern muss dabei eine beliebige existente Datei genannt werden. Dafür eignet sich die Hauptquelldatei sehr gut. AM_CONFIG_HEADER gibt eine Headerdatei an, die später die für das konfigurierte Paket spezifischen Informationen tragen soll und die auch im Quelltext eingebunden werden muss – doch dazu später.

Manchem Leser sind vielleicht schon die verschiedenen Präfixe aufgefallen, die die Makronamen tragen: AC_ bezeichnet ein Makro für autoconf, und mit AM_ wird – wenig überraschend – automake angesprochen, denn auch automake wird sich noch um den Inhalt dieser Datei kümmern. Doch auch dazu später mehr. In Zeile 5 wird es jedenfalls erst einmal initialisiert, und zwar mit dem Namen des Pakets sowie seiner Versionsnummer. Wer der Meinung ist, dass sich im Laufe der Entwicklung genug getan hat um diese Nummer nach oben zu schrauben, sollte das in dieser Datei nicht vergessen!

Zeile 7 ist dann interessant, wenn man die Quellen schließlich weitergibt. Generell werden für das Makefile auch Targets erstellt, die zum Beispiel eine Neugenerierung von configure erlauben. Das Einfügen dieser Zeile schaltet die Erzeugung dieser Dinge ab, der Benutzer ist also vor bösen Pannen geschützt.

In der neunten Zeile wird das Verzeichnis macros dem Suchpfad von aclocal hinzugefügt. Noch ein zusätzliches Programm! Ja, und es wird nicht das letzte sein. Dieses Programm kümmert sich um die Verwaltung der ganzen Makros, die in configure.in aufgerufen werden. Das Verzeichnis macros kann etwa aus dem Quellpaket der gnome-libs kopiert werden. Es folgt die Initialisierung von GNOME und diverser Standardmakros für Suche und Test eines C-Compilers, diverser Header-Dateien, POSIX-Konformität des Systems und so weiter. Auch die Kompilierwarnungen werden eingeschaltet.

Zeile 21 kümmert sich um die vorhandenen Lokalisierungen des Programms; hier wird das Vorhandensein deutscher Übersetzungen vorausgesetzt. In der Zeile danach wird das ebenfalls für diesen Zweck notwendige gettext initialisiert. Wie man GNOME-Programme internationalisiert und lokalisiert, habe ich in einer anderen Ausgabe beschrieben [6]. Auf diesen Teil der Codeerstellung werde ich also nicht mehr eingehen. Die Zeilen 24, 25 und 26 exportieren die ganzen Variablen, Compiler- und Linkerflags, die während der Abarbeitung der Datei festgestellt worden sind, nach außen, so dass sie tatsächlich im Programm benutzt werden können.

Die letzten Zeilen um AC_OUTPUT geben schließlich an, an welcher Stelle Makefiles erstellt werden sollen. Zeile 33 ist dabei kein Tippfehler; siehe auch [6].

Listing 2:
beispiel.c

1: #include <gnome.h>
2: #include "eins.h"
3: #include "zwei.h"
4: 
5: int main (int argc, char *argv [])
6: {
7:      GtkWidget *app;
8: 
9:      gnome_init ("beispiel", "0.0.1", argc, argv);
10:     app = gnome_app_new ("beispiel", "Beispiel");
11:  
12:     gtk_widget_show_all (app);
13:     gtk_main ();
14:     return TRUE;    
15: }

Listing 3:
configure.in

1: AC_INIT(src/beispiel.c)
2: 
3: AM_CONFIG_HEADER(config.h)
4: 
5: AM_INIT_AUTOMAKE(Beispiel, 0.1.0)
6: 
7: AM_MAINTAINER_MODE
8: 
9: AM_ACLOCAL_INCLUDE(macros)
10: 
11: GNOME_INIT
12: 
13: AC_PROG_CC
14: AC_ISC_POSIX
15: AC_HEADER_STDC
16: AC_ARG_PROGRAM
17: AM_PROG_LIBTOOL
18: 
19: GNOME_COMPILE_WARNINGS
20: 
21: ALL_LINGUAS="de"
22: AM_GNU_GETTEXT
23: 
24: AC_SUBST(CFLAGS)
25: AC_SUBST(CPPFLAGS)
26: AC_SUBST(LDFLAGS)
27: 
28: AC_OUTPUT(
29: Makefile,
30: macros/Makefile,
31: src/Makefile,
32: intl/Makefile,
33: po/Makefile.in
34: )

Listing 4:
Makefiles.am

1: SUBDIRS=macros po intl src
2: 
3: Applicationsdir=$(datadir)/gnome/apps/Applications
4: Applications_DATA=beispiel.desktop

Makefile.am

Dies ist eine Templatedatei, um – über einen Zwischenschritt mit dem Namen Makefile.in – ein einzelnes oder alle fertigen Makefiles zu erstellen. In der ersten Zeile stehen alle Unterverzeichnisse, in denen sich weitere Templates befinden beziehungsweise in denen Makefiles erstellt werden sollen. Die Zeilen drei und vier geben das Verzeichnis an, in dem unser Programm seine Desktop-Datei platzieren soll, anhand derer es später in den GNOME-Menüs auftauchen soll. Wichtig ist jetzt noch das Makefile.am im Unterverzeichnis src, das die eigentlichen Quellen des Programms enthält. Ein solches ist in Listing 5 zu sehen.

Dieses Beispiel habe ich aus Havoc Penningtons “Gtk+/GNOME Application Development” übernommen. Zuerst werden die Include-Verzeichnisse definiert und dann für das fertige Programm die Quelldateien genannt. Schließlich werden noch die Flags für den Linker gesetzt, der die kompilierten Objektdateien zusammenfügen soll.

Et voilà

Und nun ist es schon fast vollbracht! Die folgenden Aufrufe erledigen jetzt die Erstellung unserer Konfigurationsskripte, Makefiles und so weiter:

frosch@verlaine:~/code/beispiel    # libtoolize copy force
frosch@verlaine:~/code/beispiel    # gettextize copy force
frosch@verlaine:~/code/beispiel    # aclocal
frosch@verlaine:~/code/beispiel    # autoheader
frosch@verlaine:~/code/beispiel    # automake add-missing copy
frosch@verlaine:~/code/beispiel    # autoconf

Der erste Befehl ist vor allem für das Erstellen von Bibliotheken notwendig, kopiert aber Skripte in das Verzeichnis, die auch anderweitig benötigt werden. -copy erbittet ein Kopieren statt des Anlegens von Symlinks – das ist die Standardeinstellung – und -force legt die Dateien neu an, auch dann, wenn sie schon vorhanden sind. gettextize gibt dem Paket noch die nötigen Dateien für Internationalisierung und Lokalisierung mit. aclocal bearbeitet die Makros und autoheader legt die Datei config.h.in an, die dann durch automake und autoconf erzeugt wird.

Wir haben nun ein fertiges Paket, dass auf die Homepage gestellt werden kann. Eben für die Paketerzeugung haben wir nun das komfortable Target dist: nach einem Aufruf von configure erstellt ein make dist ein fertig geschnürtes Päckchen, in unserem Beispielfall mit dem Namen beispiel-0.1.0.tar.gz. Im zu Beginn kopierten Verzeichnis macros befindet sich übrigens ein kleines Skript mit dem Namen autogen.sh, das diese Aufrufe für Sie übernehmen kann. Sie müssen diese nicht ständig von Hand ausführen, nachdem Sie eine Quelltextdatei hinzugefügt haben.

Listing 5:
src/Makefile.am

1: INCLUDES=$(top_srcdir) -I$(includedir) $(GNOME_INCLUDEDIR) 
2:      -DG_LOG_DOMAIN="Beispiel" 
3:      -DGNOMELOCALEDIR=""$(datadir)/locale"" 
4:      -I../intl -I$(top_srcdir)/intl
5: 
6: bin_PROGRAMS=beispiel
7: 
8: beispiel_SOURCES=beispiel.c 
9:      eins.h 
10:     zwei.h 
11:     eins.c 
12:     zwei.c
13: 
14: beispiel_LDADD=$(GNOMEUILIBS) $(GNOME_LIBDIR) $(INTLLIBS)

Änderungen in den Quellen

Durch das Entstehen der Datei config.h ergeben sich eventuell noch Änderungen für den Quelltext. Besonders Angaben über den Namen des Pakets und die Versionsnummer lassen sich nun komfortabler benutzen. Listing 6 zeigt den Code der Beispieldatei nach den Änderungen. Das Makro _() in der zehnten Zeile ist dem Willen geschuldet, unser Paket zu internationalisieren.

Die Desktop-Datei

In unserem Hauptquellverzeichnis lungert immer noch die leere Datei beispiel.desktop herum. Füllt man sie mit dem Inhalt, wie er aus Listing 7 zu entnehmen ist, und trägt in seiner Haupt- Makefile.am noch die Zeile

EXTRA_DIST = beispiel.desktop

ein, so wird bei der Installation des fertigen Programms auch ein Desktopeintrag in der Menühierarchie von GNOME eingefügt. Dieser Eintrag enthält noch eventuell lokalisierte Namen – wie hier zum Beispiel auf Deutsch -, einen Kommentar – ebenfalls lokalisiert – und den Namen der auszuführenden Datei, den Typ und die Tatsache, dass diese Anwendung nicht in einem Terminal ausgeführt werden soll.

Listing 6:
src/beispiel.c

1: #include <gnome.h>
2: #include <config.h>
3: #include "eins.h"
4: #include "zwei.h"
5: int main (int argc, char *argv [])
6: {
7:      GtkWidget *app;
8: 
9:      gnome_init (PACKAGE, VERSION, argc, argv);
10:     app = gnome_app_new (PACKAGE, _("Beispiel"));
11:  
12:     gtk_widget_show_all (app);
13:     gtk_main ();
14:     return TRUE;    
15: }

Glade

Natürlich schreibe ich das erst am Ende: Das Programm Glade erstellt auf Knopfdruck auch komplette Quelltextbäume, die sich dann wie gewohnt installieren lassen. Warum sollte man sich also den Aufwand machen und von Hand vorgehen, wie in diesem Artikel beschrieben? Weil Glade nur einen rudimentären Baum erstellt. Sobald man mehr haben möchte, als Glade es bieten kann, muss man sowieso selber Hand anlegen, und das ergibt nur dann einen Sinn, wenn man es zuvor verstanden hat. Wer sich schon einmal – etwa nach dieser Anleitung – einen eigenen Quellbaum erstellt hat, kann sich beruhigt schlafen legen und davon ausgehen, dass er im Zweifelsfall eigene Änderungen vornehmen kann, ohne auf die Gnade einer grafischen Oberfläche angewiesen zu sein. Fröhliches GNOMEn.

Listing 7:
beispiel.desktop

1: [Desktop Entry]
2: Name=Example program
3: Name[de]=Beispielprogramm
4: Comment=An example
5: Comment[de]=Ein Beispiel
6: Exec=beispiel
7: Terminal=0
8: Type=Application

Richtigstellung

Ein kurzes Wort in eigener Sache: In meinem letzten Artikel über GNOME-Canvas [2] habe ich zwei Behauptungen aufgestellt: erstens, dass ich ein Python-Modul zum Lesen von Shapefiles erstellt hätte, und zweitens, dass es sich um proprietären, nicht zu veröffentlichenden Code handeln würde. Die erste ist die reine Wahrheit, die zweite völliger Mumpitz. Wenn Personen Mist bauen, die Schuld aber nicht auf sich laden wollen, dann reden sie meist von einem Missverständnis. Auch ich tue das jetzt. Sowohl ich als auch meine arbeitgebende Firma achten das Prinzip und die Idee freier Software so hoch, dass es seltsam ist, dass sich ein solcher Satz in den Text verirrt hat. So zeigt mit dem Finger auf mich und lacht – doch behauptet nicht, dass ich meinen Code nicht freigäbe. (uwo)

Infos

[1] GNOME Website: http://www.gnome.org/

[2] Thorsten Fischer: Punkt, Punkt, Komma, Polygon, Linux-Magazin 11/2000

[3] Python-Modul für Shapefiles: http://www.derfrosch.de/weichewaren/

[4] GNU FTP-Spiegel: ftp://ftp.cs.tu-berlin.de/pub/gnu/

[5] Glade Homepage: http://glade.pn.org/

[6] Thorsten Fischer: Die Internationale, Linux-Magazin 10/2000

Der
Autor

Thorsten Fischer studiert Informatik und/oder Medienberatung an der Technischen Universität Berlin, sein Buch “GUI-Programmierung mit GTK+” wurde im Oktober pünktlich zur Buchmesse bei SuSE-Press veröffentlicht. Er arbeitet außerdem als Entwickler bei Mapmedia in Berlin.

Copyright © 2002 Linux New Media AG

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben