Autor : Oliver Böhm $Id: make.txt,v 1.2 2007/12/28 18:23:52 oliver Exp $ Lizenz: GFDL ToDo : Suffix-Regeln, make für Fortgeschrittene 1 make 1.1 Einführung 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 % make love 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 Linux-System betreiben, damit es unseren Wunsch erfüllt. 1.1.1 Ein einfaches Makefile 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 weiß das make-Kommando, dass es nach einem "love.c" Ausschau halten muss, um dann damit mit Hilfe des Kommandos "gcc love.c -o love" (Aufruf des GNU-C-Compiers) 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-Source verwenden: main() { printf("Hallo Schatz!\n"); } Jetzt haben wir alle Hilfsmittel beisammen, um den Rechner zu einem "make love" aufzufordern: % 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 (z.B. "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 (z.B. mit "ls -l"), werden Sie feststellen, dass das make-Kommando tatsächlich ein "love" erzeugt hat, dass sich aufrufen lässt: % ./love Hallo Schatz! 1.1.1 Regel-Werk Ein Makefile enthält eine Regeln, die die Abhängigkeiten der Sourcen 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 zum Bau des Targets notwendig ist. 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..." o.ä.) aus. Wenn Sie Kommentare in einem Makefile verwenden wollen, leiten Sie diese mit dem "#"-Zeichen ein. Ein Kommentar erstreckt sich von "#" bis Zeilenende. 1.1.1 Abhängigkeiten Wie kann 'make' wissen, wann es ein Target zu bauen hat? Wenn Sie erneut ein 'make love' versuchen, werden Sie von make einen Korb bekommen: % make love make: `love' is up to date. Das Geheimnis liegt in den Abhängigkeiten: immer, wenn das Target ("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-Source, 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: % make children gcc -c mummy.c gcc -c daddy.c gcc mummy.o daddy.o -o children Wenn Sie jetzt eines der beiden C-Sourcen verändern und erneut das make-Kommando aufrufen, wird nur die Objekt-Datei neu erzeugt, die "veraltet" ist. 1.1.1 Pseudo-Targets Dies sind Targets, die keine Abhängigkeiten besitzen. Damit können Sie aber nie "up-to-date" werden mit dem gewünschten Nebeneffekt, dass sie von make immer die Aktionen ausgeführt werden, wenn solch ein Pseudo-Target aufgerufen wird: clean : rm *.o core Jedesmal, 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 Target "clean" nie erzeugt wird, wird es als Pseudo-Target bezeichnet. 1.1 Makros 1.1.1 Syntax Man kann auch Variablen innerhalb von Makefiles verwenden. Sie werden dort als Makros bezeichnet und meistens groß geschrieben. Mit CC = cc wird ein Makro "CC" mit dem Wert "cc" angelegt. Der Wert darf dabei auch aus mehreren Wörtern bestehen (Beispiel: CC = gcc -O). Die Definition darf sich auch über mehrere Zeilen erstrecken, wenn die Zeile mit "\" aufhört (Achtung: bitte darauf achten, dass das "\" tatsächlich das letzte Zeichen in der Zeile ist und kein Tabulator- oder Leerzeichen dahintersteht!). Der Aufruf des Makros "CC" kann mit $CC $(CC) ${CC} erfolgen. 1.1.1 Vordefinierte Makros make kennt schon eine Reihe von vordefinierten Makros. So ist z.B. die Makrodefinition "CC = cc" dem make-Commando bereits bekannt. Mit make -p -f /dev/null können die internen Makros und Regeln abgefragt werden. Ist man nur an den Makros interessiert, kann man diese mit Hilfe des grep- und sort-Kommandos ausfiltern und sortieren: make -p -f /dev/null | grep " = " | sort 1.1.1 Makro-Übergabe Makros können schon vordefiniert sein, Makros können im Makefile gesetzt werden, Makros können aber auch beim Aufruf von make übergeben werden: make love CC=gcc Soll das Makro aus mehreren Wörtern bestehen, so muss es beim Aufruf gequotet (d.h. mit einfachen oder doppelten Anführungszeichen umgeben) werden: make love CFLAGS="-O -DNDEBUG" 1.1.1 Umgebungs-Variablen Umgebungs-Variablen werden innerhalb eines Makefiles genauso wie ein Makro angesprochen. Damit kann ich z.B. eine Variable CXX setzen, die den GNU-C++-Compiler enhalten soll: Bash: export CXX=g++ C-Shell: setenv CXX g++ Die Umgebungs-Variable CXX steht mir damit im Makefile als Makro zur Verfüugung, was ich in der folgenden Regel ausnutze: hello : hello.cpp $(CXX) hello.cpp -o hello 1.1.1 Prioritäts-Regeln Wenn man dasselbe Makro auf verschiedene Weise definieren kann, ist es wichtig zu wissen, welches Makro im Falle eines Konfliktes gewinnt. Makros werden nach folgende Reihenfolge aufgelöst: 1. interne Variablen 2. Umgebungs-Variablen 3. Variablen über die Kommandozeile Die Priorität ist aufsteigend, d.h. Makros, die über die Kommandozeile gestzt werden, gewinnen immer. 1.1.1 String-Substitution Makros dürfen sich auf andere Makros beziehen. Betrachten wir dazu folgendes Beispiel: C_FILES = daddy.c mummy.c H_FILES = children.h SRC_FILES = $(C_FILES) $(H_FILES) OBJ_FILES = dadyy.o mummy.o Das Mako "SRC_FILES" bezieht sich dabei auf die beiden Makros "C_FILES" und "H_FILES". Vergleichen wir jetzt das Makro "C_FILES" mit "OBJ_FILES", so stellen wir fest, dass sie bis auf die Endung (.c bzw. .o) identisch sind. Damit wir nicht jedesmal die OBJ_FILES anpassen müssen, wenn sich C_FILES ändert, gibt es die String-Substitution: OBJ_FILES = $(C_FILES:.c=.o) Damit hängt OBJ_FILES direkt von C_FILES ab, d.h. wenn in C_FILES eine neue .c-Datei aufgenommen wird, müssen wir OBJ_FILES nichts ändern. 1.1.1 Interne Makros Neben den vordefinierten Makros gibt es eine Reihe von internen Makros, die manche Regel vereinfachen können: $@ Name des aktuellen Targets $$@ Name des aktuellen Targets (nur auf der rechten Seite einer Regel, in den Abhängigkeiten, zulässig) $? Name der abhängigen Dateien, die neuer als das Target sind $< Name der abhängigen Dateien, die neuer als das Target sind (mit Endung) $* Name der abhängigen Dateien, die neuer als das Target sind (ohne Endung) $% Name der entsprechenden Objekt-Datei (.o), falls das Target eine Bibliothek ist. Beispiel: children: $(OBJ_FILES) cc $(OBJ_FILES) -o $@ In diesem Beispiel wird von make "$@" durch den Namen des Targets ("children") ersetzt. Will man auf den Dateinamen oder Verzeichnisname zugreifen, kann man dies (außer bei $?) über die Modifier "F" (wie File) oder "D" (wie Directory). Beispiel: $(*D), $(@F)