Dateien wie "Makefile" oder "SMakefile" sind wohl schon vielen beim Durchstöbern fremder Quelltextarchive aufgefallen. Und vom Sinn hat man auch schnell etwas gehört: die Dateien beinhalten die Aufrufe von Compilern und anderen Tools, die nötig sind, um ein Programmpaket lauffähig zu machen.
Ich bin vor kurzem auch wieder über so ein Makefile gestolpert und habe mir Gedanken gemacht, ob ich sowas nicht auch nutzbringend einsetzen könnte. Makefiles werden von "make"-Programmen interpretiert, ich habe den GNU-Make (von der Geek Gadgets 2), also bezieht sich dieser Artikel auf ihn. Andere Makes (SAS) sollten aber weitestgehend kompatibel sein.
Was Make leistet
In Makefiles sind Blöcke von Shell-Aufrufen erfaßt, die bestimmte Dateien produzieren. Zum Beispiel kommt beim Linker (hoffentlich) das ausführbare Programm heraus. Gleichzeitig schreibt man im Makefile auf, welche Dateien nötig sind, damit der Linker das Programm produzieren kann. Da man sich nur auf die Dateien beschränkt, die sich durch das Programmieren verändern, gibt man normalerweise alle Objekt-Dateien an (Endung .o).
Wenn sich ein Objekt ändert, sollte das Programm neu gelinkt werden, um auf den neuesten Stand zu sein. Make vergleicht das Dateidatum von Programm und Objektfiles und ruft ggfs. den Linker auf.
Wichtig ist, daß hier Abhängigkeiten definiert werden. Das Programm hängt von den Objektfiles ab.
Die Power von Make kommt daher, daß es eine Vielzahl von Dateien und Abhängigkeiten geschickt verwalten kann. Denn die Objekt-Dateien sind von den Quelltexten und Headern abhängig. So genügt es, nach der Änderung eines Quelltextes einfach "make" auszuführen. Es erkennt, daß ein Quelltext geändert wurde und ruft den Compiler auf, um ein neues Objektfile zu erstellen. Anschließend bemerkt es, daß das Programm von den Objektfiles abhängig ist und ruft den Linker auf.
Die Art der Dateien und der Shell-Aufrufe ist Make völlig gleich. Man kann Make also auch benutzen, um TeX bei veränderten Texten zu starten. Der Anwendungsbereich ist nur durch die Phantasie des Anwenders begrenzt. :-)
Wie ein einfaches Makefile aussieht
Wenn man das Makefile auch "Makefile" nennt, wird Make es automatisch finden, wenn man im richtigen Verzeichnis ist. Das Makefile sieht dann z.B. so aus:
# Kommentare...
# ...laber sülz
Snake: snake.o
ld -o Snake snake.o -lnix -lgcc -lamiga
snake.o: snake.c snake.h
gcc -c -noixemul -s -O2 -msmall-code -o demo.o demo.c
Hier treten zwei Zeilen mit einem Doppelpunkt auf, "Snake:..." und
"snake.o:...". Das sind zwei Regeln (Rules), die Make auswertet und
dann entscheidet, ob die darunterstehenden, mit Tab (!) eingerückten
Zeilen ausgeführt werden soll.
In jeder Zeile wird ein Ziel (Target) definiert, z.B. Snake. Das ist das ausführbare Programm unseres Snake-Spiels, wow, was für ein Demo- Makefile! Hinter dem Doppelpunkt steht, welche (v.a. zum Projekt gehörende, sich ändernde) Dateien benötigt werden, um Snake zu erstellen.
In diesem Fall ist das snake.o, die vom Compiler erzeugte Objektdatei.
Da es der Sinn von Make ist, alles auf den neuesten Stand zu bringen und, in diesem Beispiel, alle Binaries den geänderten Quelltexten anzupassen, will sich Make jetzt vergewissern, daß das für Snake benötigte snake.o noch aktuell ist.
Es wird dabei die zweite Regel finden, die beschreibt, von welchen Dateien snake.o abhängt: nämlich von snake.c und snake.h. Um herauszufinden, ob snake.o noch auf den neuesten Stand ist, vergleicht es das Dateidatum mit den Quelldateien, die rechts neben dem Doppelpunkt angegeben sind.
Angenommen, snake.c ist jünger als snake.o und snake.h ist älter als snake.o. Dann würde seit der letzten Compilierung der Quelltexte ein Quelltext, nämlich snake.c, verändert. Die Änderung auch nur einer Quelldatei unter tausenden treibt Make zur Entscheidung, daß die Zieldatei veraltet ist.
Um nun ein snake.o zu erzeugen, das jünger ist als all seine Quelldateien, führt es die unter der Regel stehende(n) Zeile(n) in der Shell aus. Diese müssen unbedingt mit Tabs eingerückt sein, es kann ein wenig dauern, bis man seinen GoldEd umkonfiguriert hat... Bei mir wird GNU-C (gcc) aufgerufen, mit der "-c"-Option, damit GCC nach dem Compileren aufhört -- und nicht linkt, dafür gibt's ja einen extra Aufruf von "ld".
Make freut sich über die Erzeugung von snake.o, aber es erinnert sich langsam wieder, daß ja eigentlich Snake auf den neuesten Stand gebracht werden sollte. Plötzlich ist snake.o jünger als Snake, und der werte Leser kann raten, was passieren wird... ein neues Snake muß her, natürlich über untenstehendes Shell-Kommando.
Mit Snake ist Make wieder am Anfang angekommen und beendet sich.
Natürlich kann man die Komplexität des Makefiles mit den Regeln beliebig weit treiben, für die meisten Projekte dürfte es aber reichen, ein Haufen #?.o - Ziele mit #?.c - Quellen zu defineren und ein Executable-Ziel mit den #?.o - Quellen. Make kann selbstverständlich jedes Programm mit Shell- Interface aufrufen, also auch brush2c: wenn man für sein MUI-Programm eine Grafik geändert hat, merkt Make das und wandelt die Grafik in C-Source um, anschließend wird das betreffende Objekt, das den Source einbindet, neu gelinkt. Wenn man sich nur genug Gedanken macht, was sich alles mit Make automatisieren läßt, kann man sich leicht einiges an Aufwand ersparen.
Variablen
Variablen (bei anderen Makes evtl. auch Makros genannt) ersetzen häufig wiederkehrende Texte in Makefiles (z.B. Compileroptionen) durch ein übersichtlicheres Schlüsselwort, das man sich selbst generieren kann.
Das geht beispielsweise so:
# Variable deklarieren und initialisieren
COMPILEROPTS = -c -noixemul -s -O2 -msmall-code
Snake: snake.o highscore.o
ld -o Snake snake.o -lnix -lgcc -lamiga
snake.o: snake.c snake.h
gcc $(COMPILEROPTS) -o demo.o demo.c
highscore.o: highscore.c highscore.h
gcc $(COMPILEROPTS) -o highscore.o highscore.c
Man kann mit Variablen noch einige sinnvolle und unnütze Dinge machen,
aber das geht alles zu weit für diesen Kurs. Wer möchte, kann in die
Anleitung von Make einsteigen, bei GNU erstreckt sich das AmigaGuide-
File wieder über 400kB...
Noch was zu den Shell-Aufrufen von Make
Wer das obige Beispiel schon in der harten Wirklichkeit ausprobiert hat, wird gemerkt haben, daß Make jedes Kommando textuell in der Shell ausgibt, bevor es ausgeführt wird. Das kann ganz schön unübersichtlich werden. Plaziert man den Klammeraffen @ vor dem Kommando, wird das "Echoing" unterdrückt:
Snake: snake.o highscore.o
@echo Making Snake
@ld -o Snake snake.o -lnix -lgcc -lamiga
Das sieht gleich viel schöner aus.
Rückgabewerte
Wenn ein Kommando mit einem Wert ungleich 0 abbricht, wird das von Make als Fehlermeldung verstanden. Make steigt dann auch aus. Das läßt sich verhindern, indem man einen Bindestrich - vor das Kommando schreibt.
Oft ist es aber sinnvoll, bei fehlerhaften Übersetzung eines Quelltexts noch die anderen Quelltexte zu übersetzen, auch wenn das Linken sowieso nicht möglich sein wird. Dann kann man im Editor schon die Fehler korrigieren, während Make weiterarbeitet. Dieses Verhalten läßt sich mit dem Flag -k oder --keep-going einstellen, Make macht dann alles, was sich machen läßt...
Ausblick
Make ist auch in der Lage, verschiedene Kommandos gleichzeitig auszuführen. Andere Makefiles lassen sich "includen", Makefiles können in Abhängigkeit von Variablen ausgeführt werden (ähnlich den Preprozessor- Direktiven #ifdef, sogar Schleifen sind möglich), mit Variablen können allerlei Veränderungen angestellt werden (Search-Replace, Verzeichnis- oder Dateinamen abschneiden, Suffixe anhängen). Es gibt haufenweise vordefinierte Variablen, $@ ist der Dateiname des Ziels, $? sind die Namen aller Quellen, die neuer sind als das Ziel... Es gibt spezielle Ziele, die nur durch ihre Definition bestimmte Auswirkungen auf das Verhalten von Make haben (die Definition von .PRECIOUS verhindert, daß Make Zieldateien löscht, wenn ein Kommando gescheitert ist). Hier fangen sicher auch die Grauzonen der Make-Implementationen an, Features gehen über den "Standard" hinaus und werden nicht von allen Makes unterstützt. Es ist wirklich hilfreich, sich einmal die vollständige Dokumentation vom GNU-Make anzusehen.
![]() |
Inhaltsverzeichnis | ![]() |
| ©`98 Der AmZeiger | ||