|
Stellen Sie sich vor, Sie möchten ein kleines Programm übersetzen und
müssen dazu zu Ihrem Rechner lediglich ein mach mal sagen und Ihr
Rechner würde Sie verstehen. Genau zu diesem Zweck gibt es das
make-Kommando. Doch ein
wird vom Rechner leider mit einem
don't know how to make 'love'
|
quittiert. (das GNU-make gibt leider keine so schöne Fehlermeldung
aus). Im Verlauf dieses Kapitels werden wir etwas Aufklärungsarbeit
für unser Unix-System betreiben, damit es unseren Wunsch erfüllt.
|
Basis für unsere Aufklärungsbemühungen ist ein Makefile, aus dem
das make-Kommando die nötigen Informationen bezieht, um love zu machen:
#
# Makefile for making love
#
love : love.c
gcc love.c -o love
|
Jetzt weiss das make-Kommando, dass es nach einem love.c Ausschau
halten muss, um dann mit Hilfe des Kommandos gcc love.c -o
love (Aufruf des GNU-C-Compilers) ein love zu generieren.
Wenn Sie das Ganze ausprobieren wollen, achten Sie bitte darauf, dass
vor gcc ... ein Tabulator-Zeichen steht. Für love.c können Sie
folgende C-Quelle verwenden:
love.c
|
main()
{
printf("Hallo Schatz!\n");
return 0;
}
|
Jetzt haben wir alle Hilfsmittel beisammen, um den Rechner zu einem
make love aufzufordern:
user@linux $
make love
gcc love.c -o love
|
Und tatsächlich: der Rechner erwidert unseren Wunsch mit dem Aufruf
des GNU-Compilers gcc.... Sollten Sie eine Fehlermeldung
(zum Beispiel Makefile:6: *** missing separator...) erhalten,
überprüfen Sie bitte nochmal das Makefile: die Zeile mit gcc...
muss mit einem Tabulator-Zeichen beginnen! Wenn Sie das Verzeichnis
auflisten (zum Beispiel mit ls -l), werden Sie feststellen, dass
das make-Kommando tatsächlich ein love erzeugt hat, das sich
aufrufen lässt:
user@linux $
./love
Hallo Schatz!
|
Die beiden Schritte, Kompilation und Programmaufruf, lassen sich
natürlich auch zusammen im Makefile unterbringen:
love : love.c
gcc love.c -o love
./love
|
Damit wird nach erfolgreicher Kompilation das love-Programm
aufgerufen.
|
Ein Makefile enthält Regeln, die die Abhängigkeiten der Quellen
zum Programm, zu Objekt-Dateien, zu Bibliotheken oder auch anderen
Dateien definiert. Eine Regel hat dabei typischerweise folgendes
Aussehen:
target: dependencies
actions
|
Bei target handelt es sich um das Ziel, das gebaut werden soll (im
obigen Beispiel war es love). Die dependencies geben an, wovon das
target abhängt (im Beispiel hängt love von love.c ab). Hinter
actions verbirgt sich das (oder die) Kommando(s), das/die zum Bau des
Targets notwendig ist/sind. Achten Sie bitte darauf, dass die Zeile mit den
actions mit einem Tabulator-Zeichen beginnen muss. actions darf
dabei aus mehreren Zeilen (= mehrere Kommandos) bestehen, aber alle
diese Zeilen müssen mit dem Tabulator-Zeichen beginnen.
Achtung! Leider unterscheidet sich ein Tabulator-Zeichen optisch nicht
von 8 Leerzeichen. Das make-Kommando sieht das leider etwas enger und
gibt stattdessen eine wenig hilfreiche Fehlermeldung (wie ...missing
separator... oder Ähnliches) aus.
Wenn Sie Kommentare in einem Makefile verwenden wollen, leiten Sie
diese mit dem #-Zeichen ein. Ein Kommentar erstreckt sich von #
bis Zeilenende.
|
Wie kann make wissen, wann es ein Ziel zu bauen hat? Wenn Sie
erneut ein make love versuchen, werden Sie von make einen Korb
bekommen:
user@linux $
make love
make: 'love' is up to date.
|
Das Geheimnis liegt in den Abhängigkeiten: immer, wenn das Ziel
(love) jünger als seine Abhängigkeiten (love.c) ist, geht make
davon aus, dass es nichts zu tun gibt. Wenn Sie jetzt love.c
editieren und abspeichern, ändert sich dies: jetzt ist love.c
jünger. Ein make love wird daraufhin wieder den C-Compiler
aufrufen.
Sie werden sich vielleicht fragen: "Wozu der ganze Aufwand? Ich kann
doch den C-Compiler manuell aufrufen, wenn ich "love.c" editiere". Aber
bei größeren Programmen hat man nicht nur eine C-Quelle, sondern
mehrere. Hier hilft das make-Kommando, den Aufruf des Compilers zu
automatisieren.
#
# Makefile mit mehreren Abhängigkeiten
#
children : mummy.o daddy.o
gcc mummy.o daddy.o -o children
mummy.o : mummy.c
gcc -c mummy.c
daddy.o : daddy.c
gcc -c daddy.c
|
Wie man an diesem Beispiel sieht, können Abhängigkeiten auch
mehrstufig sein. children hängt von mummy.o und daddy.o
ab. mummy.o und daddy.o sind Objekt-Dateien, die wiederum von
mummy.c und daddy.c abhängen. Wenn Sie das erste Mal ein make
children eingeben, werden zuerst mummy.o und daddy.o gebaut, ehe
daraus dann children erzeugt wird:
user@linux $
make children
gcc -c mummy.c gcc -c daddy.c gcc mummy.o daddy.o -o children
|
Wenn Sie jetzt eine der beiden C-Quellen verändern und erneut das
make-Kommando aufrufen, wird nur die Objekt-Datei neu erzeugt, die
veraltet ist.
|
Dies sind Ziele, die keine Abhängigkeiten besitzen. Damit können Sie
aber nie up-to-date werden mit dem gewünschten Nebeneffekt, dass
von make immer die Aktionen ausgeführt werden, wenn solch ein
Pseudo-Ziel aufgerufen wird:
Jedes Mal, wenn der Benutzer ein make clean eingibt, wird vom
make-Kommando ein rm *.o core aufgerufen, d.h. es werden sämtliche
Objekt-Dateien (*.o) und core-Dateien (diese werden bei einem
Programm-Absturz angelegt) gelöscht.
Weil das Ziel clean nie erzeugt wird, wird es als Pseudo-Ziel
bezeichnet.
|
|
|