top;¬ make 0. 1. 2. 3. ;make.rtfd;0;¬Was ist make ? ;make.rtfd;1;¬Wie funktioniert make ? ;make.rtfd;2;¬Wie kann make benutzt werden ? ;make.rtfd;3;¬Was kann make sonst noch ? 3.1 ;make.rtfd;3.1;¬Implizite Regeln / .SUFFIXES 3.2 ;make.rtfd;3.2;¬Variablen 3.3 ;make.rtfd;3.3;¬Kommandozeilenoptionen 3.4 ;make.rtfd;3.4;¬Miscellaneous 0;¬0. Was ist make ? In einem Programmierprojekt mit mehreren Dateien, kann man leicht die šbersicht daröber verlieren, welche Dateien aus welchen generiert wurden, d.h. welche Dateien aktualisiert werden mössen wenn sich an einigen der Quellen etwas geÙndert hat. Das Programm make stellt einen einfachen Mechanismus zur Verfögung diese AbhÙngigkeiten zu beschreiben, inklusive der Kommandofolgen die nðtig sind, um eine Datei aus ihren Quellen zu generieren. Ein einfacher Aufruf des Kommandos make föhrt dann dazu, dass alle Dateien mit minimalem Aufwand auf den aktuellen Stand gebracht, bzw. erzeugt werden. 1;¬1. Wie funktioniert make ? Im wesentlichen sucht make den Namen einer benðtigten Datei (ein target) aus der Beschreibung der AbhÙngigkeiten (dem makefile) heraus und untersucht, ob alle Dateien von denen das target abhÙngt existieren und auf dem aktuellen Stand sind. Ist das der Fall, wird das target mit Hilfe der im makefile daför angegebenen Kommandofolgen erzeugt, sofern das target Ùlter ist als mindestens eine seiner Quellen (genauer: wenn die modification-time einer Quelle grðsser ist als die des targets). Man kðnnte auch sagen: Das makefile definiert einen Graphen von AbhÙngigkeiten und make macht eine depth-first search (Tiefensuche ?) um herauszufinden was getan werden muss. abb1;¬make.eps ¬ Abb. 1: Beispiel för einen Graphen von AbhÙngigkeiten den ein makefile definieren kðnnte. 2;¬2. Wie kann make benutzt werden ? Man erstellt ein makefile und föhrt dann jedesmal das Kommando make aus, wenn man mðchte, dass eines der im makefile definierten targets auf den neuesten Stand gebracht wird. Das makefile för das Projekt aus ;make.rtfd;abb1;¬Abb. 1 kðnnte z.B. folgendermassen aussehen: # makefile all: Main.class doc.html Main.class: Hello.class Main.java <tab> javac Main.java Hello.class: <tab> Hello.java javac Hello.java doc.html: <tab> <tab> Main.java Hello.java javadoc Main.java Hello.java test -e doc.html || ln -s tree.html doc.html Die 3. Zeile z.B. besagt, dass das target "Main.class" von den Dateien "Main.java" und "Hello.class" abhÙngt. Sind diese auf dem neuesten Stand kann "Main.class" durch den Befehl "javac Main.java" erzeugt werden. (Fussnote) Die "<tab>" dörfen natörlich nicht im makefile stehen, sie sollen nur verdeutlichen, dass es notwendig ist, dass die Zeilen mit den Kommandos mit einem Tab beginnen. För das target "all" sind keine Kommandos angegeben und es hat auch keine korrespondierende Datei, es ist aber "virtuell" bis zum Ende eines einzelnen make vorhanden, sobald "Main.class" und "doc.html" aktualisiert sind. Das Zeichen '#' leitet einen Kommentar ein der bis zum Zeilenende geht. Ist der Dateiname des makefile "makefile" oder "Makefile" so reicht nun ein einfacher Kommandoaufruf um alle för das target "all" erforderlichen Operationen durchzuföhren: plopp stephan 21: make javac Hello.java javac Main.java javadoc Main.java Hello.java Generating package.html Generating documentation for class Main Generating documentation for class Hello Generating index Sorting 3 items...done Generating tree test -e doc.html || ln -s tree.html doc.html plopp stephan 22: make make: Nothing to be done for `all'. plopp stephan 23: rm doc.html plopp stephan 24: make javadoc Main.java Hello.java Generating package.html Generating documentation for class Main Generating documentation for class Hello Generating index Sorting 3 items...done Generating tree test -e doc.html || ln -s tree.html doc.html Ruft man make mit einem oder vielen targets als Argument auf, so werden diese targets und die von denen sie abhÙngen aktualisiert, ohne ein target auf der Kommandozeile nimmt make einfach das erste das im makefile vorkommt (und das nicht mit einem '.' beginnt). Unser makefile wird daher um folgende Zeilen erweitert: clean: rm -f *.class *.html Die Verwendung von '*' und '?' ist auch bei targets und dependents mðglich und hat dort die gleiche Bedeutung wie in einer shell. Hier föhrt der Aufruf "make clean" dazu, dass alle Dateien mit den Endungen .html oder .class gelðscht werden! 3;¬3. Was kann make sonst noch ? 3.1;¬3.1 Implizite Regeln / .SUFFIXES In unserem Beispiel werden .class-Dateien erzeugt, indem die entsprechende .java-Datei durch einen Aufruf von "javac" kompiliert wird. Mit make kann man dies auch als allgemeine, implizite Regel formulieren: # makefile 3.1 # implicit rules, .SUFFIXES # $< .SUFFIXES: .SUFFIXES: .class .java .java.class: javac $< all: Main.class doc.html Main.class: Hello.class doc.html: Main.java Hello.java javadoc Main.java Hello.java test -e doc.html || ln -s tree.html doc.html clean: rm -f *.class *.html Die Zeichenfolge "$<" ist nur göltig bei einer impliziten Regel und steht för den Namen der Datei die die jeweilige Ausföhrung verursacht hat. Die erste Zeile mit ".SUFFIXES" lðscht bereits definierte implizite Regeln (Einige der vordefinierten Regeln findet man hier:;default.rules.txt;;¬). 3.2;¬3.2 Variablen Um die Struktur des makefile noch ein wenig aufgerÙumter zu gestalten und die Wartung zu vereinfachen kann man Variablen (manchmal macros genannt) vereinbaren: # # # # makefile 3.2 macros, $<one-char>/$(<many-chars>) special: $$, $@, $?, $*, $< make JAVAC=jikes targets=Main.class doc.$H JAVAC=javac JCFLAGS= JAVADOC=javadoc JDFLAGS= H=html # Suffix for html-files: htm on Windows, html on Unix .SUFFIXES: .SUFFIXES: .class .java .java.class: # delete current list of suffixes $(JAVAC) $(JCFLAGS) $< all: $(targets) Main.class: Hello.class doc.$H: Main.java Hello.java $(JAVADOC) $(JDFLAGS) Main.java Hello.java test -e $@ || ln -s tree.html $@ clean: rm -f *.class *.html ZusÙtzlich kann man auch Variablen auf der Kommandozeile von make definieren, welche dann im makefile eventuell vorhandene Definitionen öberschreiben. Die Variablen $$, $@, $?, $*, $< haben folgende Sonderbedeutungen: $$: Das Zeichen "$" $@: Das target das gerade 'gemacht' wird $?: Der String von dependents die jönger als das target sind Nur in impliziten Regeln: $*: Das gemeinsame Prefix von target und dependent $<: Der Name des dependent der die Ausföhrung der Regel verursacht hat. Ausserdem sind noch ein paar Variablen in den ;default.rules.txt;;¬default-rules vordefiniert. 3.3;¬3.3 Kommandozeilenoptionen Folgende Kommandozeilenoptionen beeinflussen die Ausföhrung von make -f <datei> <datei> wird statt "makefile" als makefile genommen. -i Ein Fehler in der Ausföhrung eines Kommandos föhrt nicht dazu, dass make beendet wird. Dies ist Ùquivalent zur Angabe von ".IGNORE:" im makefile. Alternativ kann dies auch durch ein föhrendes "-" för einzelne Kommandozeilen festgelegt werden. -k Wie '-i' nur wird nicht versucht targets zu erzeugen die von dem fehlgeschlagenen abhÙngen. -n Die Kommandos werden nicht wirklich ausgeföhrt (Testmodus). -q Es wird nichts ausgeföhrt oder ausgegeben. Ein exitcode von 0 bedeutet, dass die angegebenen targets alle aktuell sind (Question-mode). -j Erzeuge targets 'parallel' wenn sie nicht voneinander abhÙngen. -s Es werden keine Kommandozeilen vor der Ausföhrung ausgegeben. Dies ist Ùquivalent zur Angabe von ".SILENT:" im makefile. Alternativ kann dies auch durch ein föhrendes "@" för einzelne Kommandozeilen festgelegt werden. 3.4;¬3.4 Miscellaneous Ein target kann auch mehrmals angegeben werden. Dabei ist zwischen zwei verschiedenen FÙllen zu unterscheiden: a) Maximal bei einer Angabe dieses targets stehen auch Kommandos dabei, dann werden diese Kommandos ausgeföhrt wenn mindestens einer der dependents in irgendeiner dieser Zeilen jönger als das target ist. b) Stehen hinter jeder Angabe dieses targets zwei Doppelpunkte "::", so kann jede Angabe auch zugehðrige Kommandos besitzen. Diese werden aber nur ausgeföhrt wen einer der zu dieser Angabe gehðrenden dependents jönger ist. Die Angabe von "include <datei>" im makefile föhrt dazu, dass <datei> an dieser Stelle in das makefile eingefögt und mit verarbeitet wird. Das Zeichen "\" direkt am Zeilenende föhrt dazu, dass das Zeilenende ignoriert wird, so dass man diese Zeile in der nÙchsten fortsetzen kann. Disclaimer: Diese kurze Einföhrung in make hat nicht den Anspruch in irgendeiner Weise vollstÙndig zu sein oder ein spezielles make zu beschreiben. Ich garantiere nicht för irgendeine Form von 'Richtigkeit' der gemachten Angaben. Wer es ganz genau wissen will muss die jeweilige Dokumentation schon selbst zu Rate ziehen.