Wenn Sie an einem einfachen Programm mit nur einer oder zwei Quelltextdateien arbeiten, ist die Eingabe von
%
cc file1.c file2.c
zwar nicht aufwendig, wird aber mit zunehmender Anzahl der Quelltextdateien sehr lästig—und auch das Kompilieren kann eine Weile dauern.
Eine Möglichkeit dies zu umgehen besteht in der Verwendung von Objektdateien, wobei man nur die Quelltextdateien neu kompiliert, die verändert wurden. So könnten wir etwa folgendes erhalten:
%
cc file1.o file2.o
… file37.c
…falls wir seit dem letzten Kompiliervorgang nur die Datei
file37.c
verändert haben. Dadurch
könnte der Kompiliervorgang um einiges beschleunigt
werden, es muß jedoch immer noch alles von Hand
eingegeben werden.
Oder wir könnten uns ein Shell Skript schreiben. Dieses würde jedoch alles immer wieder neu kompilieren, was bei einem großen Projekt sehr ineffizient wäre.
Was ist, wenn wir hunderte von Quelltextdateien hätten? Was ist, wenn wir in einem Team mit anderen Leuten arbeiten würden, die vergessen uns Bescheid zu sagen, falls sie eine der Quelltextdateien verändert haben, die wir ebenfalls benutzen?
Vielleicht könnten wir beide Lösungen kombinieren und etwas wie ein Shell Skript schreiben, welches eine Art magische Regel enthalten würde, die feststellt, welche Quelltextdateien neu kompiliert werden müssten. Alles was wir bräuchten wäre ein Programm, das diese Regeln verstehen könnte, da diese Aufgabe etwas zu kompliziert für eine Shell ist.
Dieses Programm heißt make
. Es
liest eine Datei namens makefile,
welche ihm sagt, wie unterschiedliche Dateien voneinander
abhängen, und berechnet, welche Dateien neu kompiliert
werden müssen und welche nicht. Zum Beispiel könnte
eine Regel etwas sagen wie „wenn
fromboz.o
älter als
fromboz.c
ist, bedeutet dies, daß
jemand die Datei fromboz.c
verändert
haben muß, und diese daher neu kompiliert werden
muß.“ Das makefile enthält außerdem
Regeln die make sagen, wie die
Quelltextdatei neu kompiliert werden muß, was dieses
Tool noch sehr viel mächtiger macht.
Makefiles werden normalerweise im selben Verzeichnis
wie die Quelltextdateien abgelegt, zu denen sie gehören,
und kann makefile
,
Makefile
oder
MAKEFILE
heißen. Die meisten
Programmierer verwenden den Namen
Makefile
, da diese Schreibweise
dafür sorgt, daß die Datei gut lesbar ganz oben in
der Verzeichnisliste aufgeführt wird.
[6]
Hier ist eine sehr einfache make Datei:
Sie besteht aus zwei Zeilen, einer Abhängigkeitszeile und einer Erzeugungszeile.
Die Abhängigkeitszeile hier besteht aus dem Namen
des Programms (auch Ziel genannt),
gefolgt von einem Doppelpunkt und einem Leerzeichen, und
anschließend dem Namen der Quelltextdatei. Wenn
make
diese Zeile liest überprüft
es die Existenz von foo
; falls diese
Datei existiert vergleicht es das Datum der letzten
Änderung von foo
mit der von
foo.c
. Falls foo
nicht existiert, oder älter als
foo.c
ist, liest es die Erzeugungszeile
um herauszufinden, was zu tun ist. Mit anderen Worten, dies
ist die Regel die festlegt, wann foo.c
neu kompiliert werden muß.
Die Erzeugungszeile beginnt mit einem tab
(drücken Sie dazu die tab-Taste) gefolgt
von dem Befehl, mit dem Sie foo
manuell
erzeugen würden. Wenn foo
veraltet
ist, oder nicht existiert, führt make
diesen Befehl aus, um die Datei zu erzeugen. Mit anderen
Worten, dies ist die Regel die make sagt, wie
foo.c
kompiliert werden muß.
Wenn Sie also make
eingeben wird
dieses sicherstellen, daß foo
bzgl.
Ihrer letzten Änderungen an foo.c
auf dem neuesten Stand ist. Dieses Prinzip kann auf
Makefile
s mit hunderten von
Zielen—es ist bei FreeBSD praktisch möglich, das
gesamte Betriebssystem zu kompilieren, indem man nur
make world
im richtigen Verzeichnis
eingibt!
Eine weitere nützliche Eigenschaft der makefiles ist, daß die Ziele keine Programme sein müssen. Wir könnten zum Beispiel eine make Datei haben, die wie folgt aussieht:
Wir können make sagen welches Ziel wir erzeugt haben wollen, indem wir etwas wie folgt eingeben:
%
make target
make
wird dann nur dieses Ziel
beachten und alle anderen ignorieren. Wenn wir zum Beispiel
make foo
mit dem obigen makefile
eingeben, dann wird make das Ziel
install
ignorieren.
Wenn wir nur make
eingeben wird
make immer nur nach dem ersten Ziel suchen und danach mit dem
Suchen aufhören. Wenn wir hier also nur
make
eingegeben hätten, würde
es nur zu dem Ziel foo
gehen,
gegebenenfalls foo
neu kompilieren, und
danach einfach aufhören, ohne das Ziel
install
zu beachten.
Beachten Sie, daß das install
-Ziel
von nichts anderem abhängt! Dies bedeutet, daß der
Befehl in der nachfolgenden Zeile immer ausgeführt wird,
wenn wir dieses Ziel mittels make
install
aufrufen. In diesem Fall wird die Datei
foo
in das Heimatverzeichnis des
Benutzers kopiert. Diese Vorgehensweise wird häufig bei
makefiles von Anwendungen benutzt, damit die Anwendung nach
erfolgreicher Kompilierung in das richtige Verzeichnis
installiert werden kann.
Dieser Teil ist etwas schwierig zu erklären. Wenn
Sie immer noch nicht so richtig verstanden haben, wie
make
funktioniert, wäre es das Beste,
sie erstellen sich selber ein einfaches Programm wie
„hello world“ und eine make Datei wie die weiter
oben angegebene, und experimentieren damit selber ein bißchen
herum. Als nächstes könnten Sie mehrere
Quelltextdateien verwenden, oder in Ihrer Quelltextdatei eine
Header-Datei includen. Der Befehl touch
ist
an dieser Stelle ganz hilfreich—er verändert das
Datum einer Datei, ohne das Sie diese extra editieren
müssen.
C-Code beginnt häufig mit einer Liste von Dateien, die included werden sollen, zum Beispiel stdio.h. Manche dieser Dateien sind include-Dateien des Systems, andere gehören zum aktuellen Projekt, an dem Sie gerade arbeiten:
Um sicherzustellen, daß diese Datei neu kompiliert
wird, wenn foo.h
verändert wurde,
müssen Sie diese Datei Ihrem
Makefile
hinzufügen:
Sobald Ihr Projekt größer wird und Sie mehr
und mehr eigene include-Dateien verwalten müssen wird es
nur noch sehr schwer möglich sein, die Übersicht
über alle include-Dateien und Dateien, die von diesen
abhängen, beizubehalten. Falls Sie eine include-Datei
verändern, jedoch das erneute Kompilieren aller Dateien,
die von dieser Datei abhängen, vergessen, werden die
Folgen verheerend sein. Der gcc
besitzt
eine Option, bei der er Ihre Dateien analysiert und eine Liste
aller include-Dateien und deren Abhängigkeiten erstellt:
-MM
.
Wenn Sie das Folgende zu Ihrem Makefile hinzufügen:
und make depend
ausführen,
wird die Datei .depend
mit einer Liste
von Objekt-Dateien, C-Dateien und den include-Dateien
auftauchen:
Falls Sie foo.h
verändern
werden beim nächsten Aufruf von make
alle Dateien, die von foo.h
abhängen, neu kompiliert.
Vergessen Sie nicht jedes mal
make depend
aufzurufen, wenn Sie eine
include-Datei zu einer Ihrer Dateien hinzugefügt
haben.
Makefiles können eher schwierig zu schreiben sein.
Glücklicherweise kommen BSD-basierende Systeme wie
FreeBSD mit einigen sehr mächtigen solcher Dateien als
Teil des Systems daher. Ein sehr gutes Beispiel dafür ist
das FreeBSD Portssystem. Hier ist der grundlegende Teil eines
typischen Makefile
s des
Portssystems:
Wenn wir jetzt in das Verzeichnis dieses Ports wechseln
und make
aufrufen, passiert das
Folgende:
Es wird überprüft, ob sich der Quelltext für diesen Port bereits auf Ihrem System befindet.
Falls dies nicht der Fall ist wird eine FTP-Verbindung zu der URL in MASTER_SITES aufgebaut und der Quelltext heruntergeladen.
Die Checksumme für den Quelltext wird berechnet und mit der schon bekannten und für sicher und gut empfundenen verglichen. Damit wird sichergestellt, daß der Quelltext bei der Übertragung nicht beschädigt wurde.
Sämtliche Anpassungen, die nötig sind, damit der Quelltext unter FreeBSD funktioniert, werden vorgenommen—dieser Vorgang wird auch patchen genannt.
Alle speziellen Konfigurationen, die am Quelltext nötig sind, werden vorgenommen. (Viele UNIX® Programmdistributionen versuchen herauszufinden, auf welcher UNIX®-Version sie kompiliert werden sollen und welche optionalen UNIX®-Features vorhanden sind—an dieser Stelle erhalten sie die Informationen im FreeBSD Ports Szenario).
Der Quelltext für das Programm wird kompiliert.
Im Endeffekt wechseln wir in das Verzeichnis, in das der
Quelltext entpackt wurde, und rufen
make
auf—die eigene make-Datei
des Programms besitzt die nötigen Informationen um
dieses zu bauen.
Wir haben jetzt eine kompilierte Version des
Programmes. Wenn wir wollen können wir dieses jetzt
testen; wenn wir überzeugt vom Programm sind,
können wir make install
eingeben. Dadurch werden das Programm sowie alle
zugehörigen Dateien an die richtige Stelle kopiert;
es wird auch ein Eintrag in der
Paketdatenbank erzeugt, sodaß
der Port sehr einfach wieder deinstalliert werden kann,
falls wir unsere Meinung über dieses geändert
haben.
Ich glaube jetzt werden Sie mit mir übereinstimmen, daß dies ziemlich eindrucksvoll für ein Skript mit vier Zeilen ist!
Das Geheimnis liegt in der letzten Zeile, die
make
anweist, in das makefile des Systems
mit dem Namen bsd.port.mk
zu sehen. Man
kann diese Zeile zwar leicht übersehen, aber hierher
kommt all das klevere Zeugs—jemand hat ein makefile
geschrieben, welches make
anweist, alle
weiter oben beschriebenen Schritte durchzuführen (neben
vielen weiteren Dingen, die ich nicht angesprochen habe,
einschließlich der Behandlung sämtlicher Fehler,
die auftreten könnten) und jeder kann darauf
zurückgreifen, indem er eine einzige Zeile in seine
eigene make-Datei einfügt!
Falls Sie einen Blick in die makefiles des Systems werfen
möchten, finden Sie diese in
/usr/share/mk
. Es ist aber wahrscheinlich
besser, wenn Sie damit noch warten, bis Sie ein bißchen mehr
Praxiserfahrung mit makefiles gesammelt haben, da die dortigen
makefiles sehr kompliziert sind (und wenn Sie sich diese
ansehen sollten Sie besser eine Kanne starken Kaffee
griffbereit haben!)
Make
ist ein sehr mächtiges
Werkzeug und kann noch sehr viel mehr als die gezeigten
einfachen Beispiele weiter oben. Bedauerlicherweise gibt es
mehrere verschiedene Versionen von make
,
und sie alle unterscheiden sich beträchtlich voneinander.
Der beste Weg herauszufinden was sie können ist
wahrscheinlich deren Dokumentation zu lesen—hoffentlich
hat diese Einführung Ihnen genügend Grundkenntnisse
vermitteln können, damit Sie dies tun können.
Die Version von make, die in FreeBSD enthalten ist, ist
Berkeley make; es gibt eine
Anleitung dazu in
/usr/share/doc/psd/12.make
. Um sich diese
anzusehen, müssen Sie
%
zmore paper.ascii.gz
in diesem Verzeichnis ausführen.
Viele Anwendungen in den Ports verwenden
GNU make, welches einen sehr guten
Satz an „info“-Seiten mitbringt. Falls Sie
irgendeinen dieser Ports installiert haben wurde
GNU make automatisch als
gmake
mit installiert. Es ist auch als
eigenständiger Port und Paket verfügbar.
Um sich die Info-Seiten für
GNU make anzusehen müssen Sie
die Datei dir
in
/usr/local/info
um einen entsprechenden
Eintrag erweitern. Dies beinhaltet das Einfügen einer
Zeile wie
in die Datei. Nachdem Sie dies getan haben können
Sie info
eingeben und dann den
Menüeintrag
auswählen (oder Sie können in
Emacs die Tastenkombination
C-h i
verwenden).
[6] Verwenden Sie nicht MAKEFILE
mit
lauter Großbuchstaben, da diese Schreibweise
häufig für Dokumentationsdateien wie
README
benutzt wird.
Wenn Sie Fragen zu FreeBSD haben, schicken Sie eine E-Mail an
<de-bsd-questions@de.FreeBSD.org>.
Wenn Sie Fragen zu dieser Dokumentation haben, schicken Sie eine E-Mail an
<de-bsd-translators@de.FreeBSD.org>.