![]() |
|
Linux - Wegweiser zur Installation & Konfiguration, 3. AuflageOnline-VersionBitte denken Sie daran: Sie dürfen zwar die Online-Version ausdrucken, aber diesen Druck nicht fotokopieren oder verkaufen. Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen. Wünschen Sie mehr Informationen zu der gedruckten Version des Buches Linux - Wegweiser zur Installation & Konfiguration oder wollen Sie es bestellen, dann klicken Sie bitte hier.
|
Irgendwann in Ihrem Zusammenleben mit Linux werden Sie wahrscheinlich auf make stoßen - selbst wenn Sie gar nicht programmieren möchten. Sicherlich werden Sie einmal den Kernel ändern und neu erstellen, und dazu brauchen Sie make. Wenn Sie Glück haben, müssen Sie sich nicht durch die Makefiles wühlen - wir wollten uns mit diesem Buch allerdings auch an die Pechvögel wenden. In diesem Abschnitt wollen wir Ihnen deshalb so viel von der feinsinnigen make-Syntax vermitteln, daß ein Makefile Sie nicht mehr einschüchtern kann.
[55] make |
In einigen unserer Beispiele werden wir das aktuelle Makefile für den Linux-Kernel benutzen. Darin sind viele der mächtigen Erweiterungen enthalten, die zur GNU-Version von make gehören; das gibt uns Gelegenheit, sowohl einige dieser Erweiterungen als auch das Standard-make zu beschreiben. In Managing Projects with make von Andrew Oram und Steve Talbott finden Sie eine gute Einführung in make. |
[56] GNU make |
Die GNU-Erweiterungen werden in der Manpage zu GNU-make ausführlich beschrieben. |
Die meisten Benutzer betrachten make als ein Hilfsmittel, um aus Quelltexten Objektdateien und Bibliotheken zu erzeugen sowie Objektdateien zu ausführbaren Dateien zu machen. Wenn man es abstrakter betrachtet, ist make ein vielseitiges Programm, das aus gewissen Abhängigkeiten (prerequisites) bestimmte Ziele (targets) erzeugt. Das Ziel kann eine ausführbare Datei sein, ein PostScript-Dokument oder was auch immer. Die Abhängigkeiten können C-Code, eine Textdatei im -Format usw. sein.
Es ist nicht schwierig, einfache Shell-Skripten zu schreiben, die gcc-Befehle aufrufen, um ausführbare Programme zu erzeugen. make dagegen kann noch mehr: Es weiß, welche Ziele neu erstellt werden müssen und welche nicht. So muß beispielsweise eine Objektdatei nur dann erneut kompiliert werden, wenn die zugrundeliegende Quelldatei geändert wurde.
Ein Beispiel: Nehmen wir an, daß ein Programm aus drei C-Quelltexten besteht. Sie könnten nach jeder Änderung in einer der Quelldateien mit dem Befehl
alle drei Dateien erneut kompilieren, um eine neue ausführbare Datei zu erzeugen. Das wäre allerdings eine große Zeitverschwendung, da Sie ja nur eine Quelldatei geändert haben. (Dies gilt ganz besonders dann, wenn das fragliche Programm aus mehr als nur einer Handvoll von Quelldateien besteht.) Was Sie wirklich erreichen möchten, ist die Neukompilierung der einen geänderten Datei zu einer Objektdatei sowie das erneute Binden aller Objektdateien zur ausführbaren Datei. make kann diesen Vorgang für Sie automatisieren.
Grundsätzlich unterstützt make Sie dabei, eine Zieldatei in kleinen Schritten zu erzeugen. Wenn ein Programm aus vielen Quellcodedateien besteht, können Sie eine davon ändern und eine neue ausführbare Datei erzeugen, ohne alle Quelldateien neu kompilieren zu müssen. make erreicht diese Flexibilität, indem es sich merkt, welche Dateien zu Ihrem Ziel gehören.
Hier zeigen wir Ihnen ein triviales Makefile. Nennen Sie es makefile oder Makefile, und speichern Sie die Datei in dem Verzeichnis ab, in dem auch die Quellcodedateien stehen.
Dieses Makefile erzeugt aus den beiden Quelldateien main.c und edit.c ein Programm namens edimh. Sie können ein Makefile nicht nur für die C-Programmierung einsetzen; es können beliebige Befehle enthalten sein.
Das Makefile besteht aus drei Einträgen. Jeder davon enthält eine Abhängigkeiten-Zeile, aus der hervorgeht, wie eine Datei erzeugt wird. Die erste Zeile besagt also, daß edimh (der Name vor dem Doppelpunkt) aus den beiden Objektdateien main.o und edit.o (den Namen hinter dem Doppelpunkt) erzeugt wird. Für make bedeutet das, daß es die folgende gcc-Zeile immer dann ausführen soll, wenn eine dieser beiden Objektdateien geändert wurde. Die Zeilen, in denen Befehle stehen, müssen mit einem Tabulator beginnen (und nicht mit Leerzeichen).
führt die gcc-Zeile aus, wenn derzeit keine Datei namens edimh vorhanden ist. Diese Zeile wird aber auch dann ausgeführt, wenn edimh existiert, aber eine der Objektdateien neuer ist. In diesem Fall ist edimh das Ziel. Die Dateien hinter dem Doppelpunkt nennt man entweder Abhängigkeiten oder Vorbedingungen.
Die nächsten beiden Einträge erfüllen denselben Zweck für die Objektdateien. Die Datei main.o wird erzeugt, wenn sie noch nicht existiert oder wenn die zugehörige Quelldatei main.c neuer ist. edit.o wird aus edit.c erzeugt.
Woher weiß make, ob eine Datei neu ist? Es liest den Zeitstempel, den das Dateisystem jeder Datei zuordnet. Mit dem Befehl ls -l können Sie sich den Zeitstempel anzeigen lassen. Da diese Zeiten auf eine Sekunde genau sind, kann make zuverlässig ablesen, ob Sie eine Quelldatei nach dem letzten Compiler-Lauf geändert haben oder ob Sie eine Objektdatei kompiliert haben, nachdem die letzte Version der ausführbaren Datei erzeugt wurde.
Lassen Sie uns dieses Makefile testen und beobachten, was passiert:
Wenn wir jetzt main.c editieren und den Befehl noch einmal aufrufen, werden nur die notwendigen Dateien neu erzeugt, und wir sparen etwas Zeit:
Es spielt keine Rolle, in welcher Reihenfolge die drei Einträge im Makefile stehen. make findet heraus, welche Dateien von welchen anderen Dateien abhängig sind, und führt die Befehle in der richtigen Reihenfolge aus. Es ist bequemer, den Eintrag für edimh an die erste Stelle zu setzen, weil das die Datei ist, die per Voreinstellung erzeugt wird. Mit anderen Worten: Wenn Sie make aufrufen, erhalten Sie dasselbe Ergebnis wie mit make edimh.
Sehen wir uns ein etwas längeres Makefile an. Versuchen Sie herauszubekommen, was darin passiert:
Als erstes sehen wir das Ziel install. Daraus wird nie eine Datei erzeugt werden; man nennt das ein unechtes Ziel (phony target), weil dieser Eintrag nur existiert, damit die Befehle darunter ausgeführt werden können. Bevor install ausgeführt wird, muß zuerst all aufgerufen werden, weil install von all abhängig ist. (Erinnern Sie sich, daß die Reihenfolge der Einträge keine Rolle spielt.)
Als nächstes wendet sich make also dem Ziel all zu. Dahinter stehen keine Befehle (das ist in Ordnung so), aber all ist abhängig von edimh und readimh. Dabei handelt es sich um echte Dateien; es sind zwei ausführbare Programme. make geht also die Liste der Abhängigkeiten so lange durch, bis es zu den .c-Dateien kommt, die von nichts weiter abhängig sind. Anschließend wird make jedes einzelne Ziel gewissenhaft neu erzeugen.
Lassen Sie uns einen Testlauf starten (eventuell brauchen Sie root-Berechtigung, um die Dateien im Verzeichnis /usr/local installieren zu können):
Dieses Makefile bewältigt also einen kompletten Durchlauf zur Erzeugung und Installation aller Dateien. Es erzeugt zuerst die Dateien, die für edimh benötigt werden. Dann erzeugt es die Objektdatei, die zusätzlich für die Erzeugung von readimh gebraucht wird. Wenn diese beiden ausführbaren Dateien erstellt sind, ist das Ziel all erfüllt. Anschließend kann make mit dem Ziel install weitermachen, also die ausführbaren Dateien an ihren endgültigen Speicherort verschieben.
Viele Makefiles - darunter auch die, die Linux erzeugen - enthalten eine ganze Reihe unechter Ziele, mit denen Routineaufgaben erledigt werden. Das Makefile für den Linux-Kernel enthält zum Beispiel Befehle, die temporäre Dateien löschen:
Dort finden Sie auch Befehle, um eine Liste mit Objektdateien samt den Header-Dateien, von denen sie abhängig sind, zu erzeugen. (Dies ist eine komplizierte und wichtige Aufgabe - wenn eine Header-Datei geändert wird, soll sichergestellt werden, daß die Objektdateien, die davon abhängig sind, neu kompiliert werden.)
Einige dieser Shell-Befehle werden ziemlich komplex; wir werden uns im Abschnitt »Mehrfachbefehle« später in diesem Kapitel noch mit den Befehlen in Makefiles befassen.
Die größten Probleme rund um die Makefiles verursacht immer die Syntax - jedenfalls für Neulinge. Also gut, wir sagen es geradeheraus: Die Syntax von make ist ganz einfach idiotisch. Wenn Sie Leerstellen einfügen, wo Tabulatoren stehen sollten (oder umgekehrt), geht das Ding in die Hose; die Fehlermeldungen dazu sind eher verwirrend.
Setzen Sie vor eine Befehlszeile immer einen Tabulator - keine Leerstellen. Setzen Sie niemals einen Tabulator vor irgendeine der anderen Zeilen. |
Sie können an beliebiger Stelle in einer Zeile ein Doppelkreuz (#) schreiben, um einen Kommentar einzuleiten. Alles hinter dem Zeichen wird ignoriert.
Wenn Sie ein Backslash an das Ende einer Zeile setzen, wird die nächste Zeile eine Fortsetzungszeile. Das funktioniert mit besonders langen Befehlen ebenso wie für alle anderen Zeilen in Makefiles.
Lassen Sie uns einige der mächtigen Fähigkeiten von make betrachten, die insgesamt eine Art Programmiersprache darstellen.
Wenn Programmierer einen Dateinamen oder sonstigen String innerhalb eines Makefiles mehr als einmal benutzen, tendieren sie dazu, daraus ein Makro zu machen. Das ist einfach eine Zeichenfolge, die mit Hilfe von make zu einer anderen Zeichenfolge expandiert. Sie könnten zum Beispiel den Anfang unseres trivialen Makefiles so formulieren:
Wenn make loslegt, wird es einfach überall da main.o edit.o
einsetzen, wo Sie $(OBJECTS)
angegeben haben. Wenn Sie dem Projekt eine weitere Objektdatei hinzufügen möchten, brauchen Sie dies nur in der ersten Zeile der Datei anzugeben. Die Zeile mit den Abhängigkeiten und die Befehle werden dementsprechend angepaßt.
Vergessen Sie die Klammern nicht, wenn Sie $(OBJECTS)
ansprechen. Makros sehen vielleicht ein wenig wie Shell-Variablen aus (etwa $HOME
oder $PATH
), aber sie sind doch anders.
Sie können ein Makro innerhalb einer anderen Makrodefinition einsetzen. Ein Beispiel:
In diesem Fall wird HEADERS
zum Verzeichnis /usr/local/include expandiert, und SOURCES
wird zu /usr/local/src. Falls Sie ein Programmpaket auf Ihrem System installieren, das nicht in /usr/local stehen soll, müssen Sie nur ein anderes Verzeichnis finden und dieses in der ROOT
-Zeile eintragen.
Sie müssen übrigens für die Namen von Makros keine Großbuchstaben verwenden, aber diese Konvention hat sich überall durchgesetzt.
Ein Zusatz zum GNU-make gibt Ihnen die Möglichkeit, eine Makrodefinition zu erweitern. Benutzen Sie dazu den String :=
statt des Gleichheitszeichens:
Die erste Zeile enthält eine normale Makrodefinition, die dem Makro DRIVERS
den Wert drivers/block/block.a
zuweist. Die nächste Definition erweitert das Makro um die Datei drivers/scsi/scsi.a
; diese Erweiterung findet allerdings nur dann statt, wenn auch CONFIG_SCSI
definiert ist. Die komplette Zuweisung lautet in diesem Fall:
Wie aber definieren Sie CONFIG_SCSI
? Fügen Sie es einfach in das Makefile ein, und weisen Sie ihm einen beliebigen Wert zu:
Wahrscheinlich ist es aber einfacher, die Definition auf der Befehlszeile von make vorzunehmen - und so wird's gemacht:
Eine subtile Anwendung von Makros bietet das undefinierte Makro. Wenn ein Makro nicht definiert wird, bekommt es eine leere Zeichenfolge zugewiesen (das heißt, es steht nichts an der Stelle, an der das Makro stehen sollte). Damit haben Sie aber auch die Möglichkeit, ein Makro als Umgebungsvariable zu definieren. Wenn Sie also CONFIG_SCSI
nicht im Makefile definieren, könnten Sie folgendes in Ihre Datei .bashrc einfügen (wenn Sie bash benutzen):
Für die csh oder tcsh schreiben Sie folgendes in die Datei .cshrc:
Damit haben Sie für alle Compiler-Läufe das Makro CONFIG_SCSI
definiert.
Natürlich wollen Sie bei solchen Routineaufgaben wie der Erzeugung einer Objektdatei aus einer Quelldatei in Ihrem Makefile nicht jede Abhängigkeit einzeln definieren. Dazu besteht auch kein Grund. Die Unix-Compiler bestehen auf einer einfachen Vereinbarung (eine Datei mit der Endung .c wird kompiliert, um eine Datei mit der Endung .o zu erzeugen); make benutzt Suffixregeln, um alle vorkommenden Dateien zu erfassen.
Sie könnten folgende einfache Suffixregel in Ihr Makefile schreiben, um eine C-Quelldatei zu kompilieren:
Die Zeile .c.o: bedeutet: »Mache aus einer .c-Datei eine .o-Datei.« Das Makro CFLAGS
besteht aus den gewünschten Compiler-Optionen - etwa -g zum Debuggen oder -O für die Optimierung. Die Zeichenfolge $<
steht für »die Eingabedatei«. An dieser Stelle wird also der Name Ihrer .c-Datei eingesetzt, wenn make diese Anweisung ausführt.
Lassen Sie uns diese Suffixregel testen. Die Befehlszeile übergibt die beiden Optionen -g und -O:
In Wirklichkeit brauchen Sie diese Suffixregel gar nicht in Ihr Makefile einzubauen, weil make bereits etwas ganz Ähnliches enthält. Es benutzt außerdem die CFLAGS
, so daß Sie die Compiler-Optionen bestimmen können, indem Sie einfach diese Variable definieren. Das Makefile, das bei der Kompilierung des Kernels benutzt wird, enthält derzeit eine ganze Reihe von Optionen für den gcc:
Da wir uns gerade mit Compiler-Optionen beschäftigen, wollen wir eine besonders erwähnen, die man sehr häufig zu sehen bekommt - das ist die Option -D, mit der Symbole im Quellcode definiert werden. Es kann sein, daß Sie eine ganze Menge solcher Optionen an Ihr Makefile übergeben müssen, weil einige davon in den #ifdef-Zeilen recht häufig auftauchen; zum Beispiel -DDEBUG oder -DBSD. Wenn Sie diese Optionen in der Befehlszeile übergeben, sollten Sie auf jeden Fall die komplette Reihe der Optionen mit Anführungszeichen oder Apostrophen klammern; daraufhin wird Ihre Shell alle Optionen als ein einziges Argument an Ihr Makefile übergeben:
Das make von GNU benutzt außerdem etwas, das man Pattern-Regeln (Musterregeln) nennt. Damit haben Sie weitergehende Möglichkeiten als mit den Suffixregeln. In einer Pattern-Regel ist das Prozentzeichen ein Platzhalter für »einen beliebigen String«. Mit folgender Regel können Sie also C-Quellen kompilieren:
In diesem Fall steht die Ergebnisdatei an erster Stelle, und die Eingabedatei erscheint hinter dem Doppelpunkt. Eine Pattern-Regel sieht also aus wie eine normale Abhängigkeiten-Zeile, enthält aber Prozentzeichen statt der Dateinamen.
Der String $<
ist der Platzhalter für die Eingabedatei, und das $@
steht für die zu erzeugende Datei; hier wird also der Name der .o-Datei eingesetzt. Beide sind interne Makros, die make jedesmal definiert, wenn es eine Anweisung ausführt.
Ein anderes internes Makro ist $*
, das den Namen der Eingabedatei ohne das Suffix erzeugt. Wenn die Eingabedatei also edit.c heißt, wird der String $*.s
zu edit.s expandiert (eine Assembler-Codedatei).
Ein Beispiel für eine nützliche Anwendung, das mit der Pattern-Regel, aber nicht mit einer Suffixregel funktioniert: Hängen Sie die Zeichenfolge _dbg an den Namen der Ausgabedatei an, damit Sie später noch wissen, daß dieses Programm mit Debugging-Informationen kompiliert wurde:
Jetzt können Sie alle Objektdateien einmal mit und einmal ohne Debugging-Informationen kompilieren. Weil die Dateinamen unterschiedlich sind, können Sie alle Versionen in einem Verzeichnis halten.
In einem Makefile können beliebige Shell-Befehle ausgeführt werden. Die Lage wird allerdings dadurch kompliziert, daß make jeden Befehl in einer anderen Shell ausführt. So gelangen Sie also nicht ans Ziel:
Noch eine Änderung: Wenn Sie innerhalb des Befehls eine Shell-Variable definieren und benutzen, müssen Sie zwei Dollarzeichen voranstellen. make erkennt daran, daß hier eine Shell-Variable und nicht ein Makro gemeint ist.
Vielleicht ist die Datei einfacher zu lesen, wenn Sie die einzelnen Bestandteile eines Mehrfachbefehls jeweils in eine neue Zeile schreiben. Schließen Sie dann jede Zeile mit einem Backslash ab, damit make das Ganze als eine zusammenhängende Zeile betrachtet:
Manche Makefiles enthalten wiederum einen make-Befehl; das nennt man rekursives make. So sieht es aus:
Das Makro $(MAKE)
ruft make auf. Es gibt mehrere Anwendungen für geschachtelte makes. Unser Beispiel zeigt eine Möglichkeit - einen Compiler-Lauf, der sich auf verschiedene Verzeichnisse erstreckt (dabei muß jedes dieser Verzeichnisse ein eigenes Makefile enthalten). Eine andere Anwendung ist die Definition von Makros in der Befehlszeile, so daß Compiler-Läufe mit unterschiedlichen Makrodefinitionen stattfinden können.
GNU-make bietet als Erweiterung ein weiteres mächtiges Interface mit der Befehlszeile. Sie können einen Shell-Befehl aufrufen und einem Makro das Ergebnis des Aufrufs zuweisen. Im Makefile zum Linux-Kernel wird diese Methode benutzt, aber wir wollen Ihnen hier ein einfaches Beispiel zeigen:
Damit weisen Sie dem Makro HOST_NAME
den Namen Ihres Rechners im Netzwerk zu (nämlich die Ausgabe des Befehls uname -n).
make befolgt ein paar Konventionen, die manchmal ganz praktisch sind. So erreichen Sie zum Beispiel mit dem Klammeraffen (@) vor einem Befehl, daß make bei der Ausführung den Befehl selbst anzeigt:
Eine andere Möglichkeit besteht darin, einen Bindestrich vor einen Befehl zu setzen; damit weisen Sie make an, selbst dann fortzufahren, wenn der Befehl nicht ausgeführt werden konnte. Das kann zum Beispiel dann nützlich sein, wenn make auch nach einem fehlgeschlagenen mv oder cp weiterarbeiten soll:
Bei großen Projekten hat man es oft mit vielen Makefiles zu tun. Das erleichtert zum Beispiel die gemeinsame Nutzung von Makrodefinitionen durch etliche Makefiles in verschiedenen Verzeichnissen. Mit der Anweisung
lesen Sie den Inhalt von dateiname ein. Auch dies wird im Makefile des Linux-Kernels benutzt. Ein Beispiel:
Wenn Sie sich die Datei .depend ansehen, finden Sie dort einige Makefile-Einträge; genauer gesagt Zeilen, in denen erklärt wird, daß Objektdateien von Header-Dateien abhängen. (Es könnte übrigens sein, daß .depend noch nicht existiert - diese Datei wird durch einen weiteren Eintrag im Makefile erzeugt.)
Manchmal beziehen sich include-Zeilen auf Makros statt auf Dateinamen, etwa so:
In diesem Fall muß INC_FILE
entweder als Umgebungsvariable oder als ein Makro definiert sein. Auf diese Weise können Sie noch genauer bestimmen, welche Datei benutzt wird.
Das Schreiben von Makefiles für ein größeres Projekt ist normalerweise eine langweilige und langwierige Aufgabe, insbesondere, wenn die Programme auf mehreren Plattformen kompiliert werden sollen. Aus dem GNU-Projekt stammen zwei Werkzeuge namens Autoconf und Automake, die sehr schwer zu erlernen sind, aber die Erzeugung portabler Makefiles drastisch vereinfachen, wenn man sie erst einmal beherrscht. Dazu gibt es noch libtool, das die Erzeugung von Shared Libraries sehr viel einfacher macht. Es würde den Rahmen dieses Buches sprengen, die Benutzung dieser Programme zu beschreiben. Sie finden sie unter ftp://ftp.gnu.org/pub/gnu .
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Weitere Informationen zum Linux - Wegweiser zur Installation & Konfiguration
Weitere Online-Bücher & Probekapitel finden Sie in unserem Online Book Center
© 2000, O'Reilly Verlag