Universität Bayreuth Mathematisches Institut Erste Schritte Mail und News KDE LaTeX/TeX Linksammlung Linuxtools Netzwerk Programmieren Windows X Window Anträge Kontakt |
Programmieren
Steuerung der Übersetzung mit MakeMake steuert die Übersetzung und sorgt gerade bei größeren Projekten dafür, dass nur das wirklich Nötige übersetzt wird. Hier werden wir hauptsätzlich die unter Linux gebräuchliche GNU-Variante von make betrachten. Dabei werden wir auch über die Datei Makefile sprechen, da sie von make benötigt wird. Ein Makefile ist eine Make-Datei, also eine Datei, die von dem Make-Werkzeug interpretiert wird.
Alle Beispieldateien befinden sich im TAR-GZ-Archgiv "make_makefile_bsp.tar.gz". Inhaltsverzeichnis
Zweck und Fähigkeiten von MakeBisher waren Ihre Programme so klein, das eine Datei für sie ausreichte. Bald aber werden Sie größere Programme schreiben, bei denen Sie für jede Funktion, Klasse oder Methode eine eigene Datei anlegen müssen. Dann wird die Kompilierung unseres Programms immer komplizierter. Diese Probleme können Sie mit dem Werkzeug Make lösen.Make verwaltet die Kompilierung und das Linken aller Dateien eines Projekts. Dazu vergleicht es das Erstellung-/Änderungsdatum (jede Datei hat in einem Filesystem unter Linux einen Zeitpunkt abgespeichert, bei dem es erstellt/zuletzt modifiziert wurde) der Quelldateien mit dem der Objektdateien; ist die Quelle neuer, sorgt "make" für die Kompilierung; Ähnliches gilt für das Linken. Sonst wird nichts gemacht. Beispiel 1 Sie haben eine Funktion geschrieben, die Sie in vielen Hauptprogrammen brauchen. Möglichkeit wäre, diese Funktion jedes Mal in jeden Programmquelltext zu kopieren. Nehmen wir an, dass Sie 50 geschriebenen Hauptprogramme haben, wo diese Funktion auftaucht und, dass Sie einen Fehler in Ihrer Funktion entdecken. Dann müssen Sie diesen Fehler in allen 50 Hauptprogrammen korrigieren. Sie können auch diese häufig benutzte Funktion in einer eigenen Datei definieren und jedes Mal,dass wir die brauchen, wird ein Objectfile davon und vom Hauptprogramm erzeugt und das ganze dann gelinkt. Das klappt auch. Der Befehl zur Compilierung kann aber sehr lang werden mit viele Optionen und für so viele Hauptprogramme – dazu haben Sie sicher bald keine Lust mehr. Dann kommt die Benutzung eines Makefile in Frage: Make steuert die Übersetzung und dammit können Sie die lange Compilierungen vermeiden. Beim Übersetzung allen 50 Programme wird nur einmal das Objektfile der Funktion erstellt. Dieses wird zu allen 50 Hauptprogrammen dazugelinkt. Wenn Sie die Funktion ändern, wird nur die compiliert (Erzeugung ihres Objektfile) und, wenn die Hauptprogramme nicht geändert worden sind, genügt das Linken. Und wenn nichts geändert ist wird auch vom Make nicht gemacht. Make macht nur was erforderlich ist. Dieser Fall können Sie sich im Beispiel 1 anschauen. Weil wir die Möglichkeit haben im Makefile Betriebssystemsbefehle einzugeben, können wir viel mehr mit dem Make-Werkzeug machen. Beispiel 2 Sie sind Praktikant in einem Unternehmen und haben als Tätigkeit die Bearbeitung des Korrespondenzs. Sie wollen beispielsweise einen Brief an verschiedene Adressen schicken aber mit verschiedene Anrede oder verschiedene Briefstext an die gleiche adresse schicken. Eine Möglichkeit wäre mit dem Make-Werkzeug zu steuern, in dem Sie verschiedene Dateien anlegen (z.B.: Adresse.txt, text.txt, anrede.txt, usw.) und ein Makefile schreiben, die alles als Brief zusammen füdt. Dieser Fall können Sie sich im Beispiel 2 anschauen. Sie können auch, wenn Sie das Funktionieren von Make verstanden haben, die Installierung von Programmen besser verstehen. Beispiel 3 Die Programmsinstallationen werden häufig per Makefiles gemacht. Dies sind automatisch durch andere Dateien erzeugt oder liegen schon fertiggeschrieben da. Das Verständnis des Vorgehenweise hilft nach Wunsch die Installation anzupassen und erleichtert die Benutzung des Programms. Dieser Fall können Sie sich im Beispiel 3 anschauen.
make-Befehlmake können sie in einem Verzeichnis völlig ohne Argumente aufrufen. Es wird nach einer Datei namens makefile oder Makefile gesucht und die wird übersetzt. Sind beide dateinamen makefile und Makefile belegt, dann wird makefile übersetzt.Sie können trotzdem Ihre Makefiles beliebig nennen und mit der Option -f aufrufen: make -f <Dateiname> Nach Wunsch können Sie nur ein beliegiges Ziel Ihres Makefiles erzeugen lassen. Das erfolgt durch die Eingabe: make -f <Dateiname> <Zielname> Bis jetzt haben wir nur die Option -f kennegelernt aber es gibt noch viele andere. Die wichtigen werden wir jetzt noch kennelernen aber den Rest können Sie sich in der Manualseite anschauen. Kommandozeilenoptionen
Fehler bzw. Warnhinweisemake startet beim Aufruf zunächst die Überprüfung, welche Dateien neuer als ihre Ziele sind und führt die zugehörige Regel (etwa den Aufruf eines Compiler) nach Bedürfnis aus.Bricht die Compilierung mit einem Fehler ab, beendet auch make seine arbeit (wenn sie make ohne die Option -k aufgerufen haben): make: [Prog1.o] Error 1 Ein anderer fehler wird gemeldet, wenn Sie ein Fehler im Makefile haben.(Beispielweise einen syntaktischen Fehler): make: No rule to make target `prog.o' needed by `prog.o'. Stop Versuchen Sie immer zunächst die Schreibweise im makefile und die logische Zusammenhänge der Abhängigkeiten zu überprüfen. Diese Fehlermeldung kann auch bedeuten, dass die Datei prog.o sich nicht in Ihrem aktuellen Verzeichnis befindet. Wenn Sie eine Zeile mit einem ausführbaren Befehl (siehe ausführbare Befehle) nicht mit der Tabulatortaste eingerückt erscheint die Fehlermeldung: <Name des Makefiles>: <Zeilennummer>: missing separator. Stop. Eine andere Meldung erscheint, wenn Sie make aufrufen, obwohl sich keine Dateien gegenüber der letzten Erzeugung verändert haben. make: `prog.o' is up to date.
Grundprinzipien bei der Benutzung von Make-DateienEin Makefile ist eine Textdatei, die Sie daher mit jedem Editor bearbeiten können. Es besteht aus Regeln, wie die einzelnen Dateien, die aus Ihrem Projekt hervorgehen, zu erzeugen sind, also auch welche Optionen Compiler und Linker verwenden sollen oder welche Objektdateien für die ausführbare Datei nötig sind.Beim Schreiben des Makefiles müssen Sie Regeln und prinzipien berücksichtigen, wie in jeder Programmiersprache. Die werden Sie hier kennenlernen. besondere Zeichen
AbhängigkeitenEin Makefile ist dazu da, dem Programm make mitzuteilen, was es tun soll (dies ist das "target" oder "Ziel"). Sie können explizit angeben, welche Datei(en) von welchen anderen abhängig sind, also von welchen Dateien ein Ziel abhängt. Meist ist damit eine Beziehung zwischen einer Quelldatei und der daraus erzeugten gemeint.Beispiel:(siehe Beispiel 1 ) Prog: Prog1.o Prog2.o Prog1.o: Prog1.cpp Prog2.o: Prog2.cpp Die erste Zeile bedeutet, dass Prog neu erzeugt werden muss, wenn sich eine der zwei Dateien Prog1.o oder Prog2.o ver ändert hat, sprich: neuer als das Ziel Prog. Und aus den zweiten und dritten Zeilen ergibt sich, dass Prog1.o zu übersetzen ist, wenn sich Prog1.cpp sich verändert hat, bzw. Prog1.o, wenn Prog1.cpp sich verändert hat. Auf diese Weise kann es mehrere Ebenen der Abhängigkeiten geben. Wie Sie sehen, steht vor dem Doppelpunkt das Ziel und danach die Quelle(n). Zwischen Doppelpunkt und Quelle muss mindestens ein Leerzeichen oder Tabulator eingesetzt werden. Das als Erstes im makefile angegebene Ziel gilt als Standardziel. Wenn dieses Ziel von anderen abhängt, werden diese berüsichtigt. Standardname für das erste (=wichtigste) Ziel ist "all". Beispiel: all: Prog Diese Methode mit "all" geeignet sich besonders gut, wenn Sie mehrere Dateien auf eimal erzeugen wollen. Nach "all" listen Sie dann alle Ziele. Beispiel: all: Prog1 Prog2 a.out Die Standardziele legem Sie mit "all" fest, aber es ist in vielen Fällen wünschenswert, noch weitere Ziele zu haben. Einige von den sind in der kommenden Tabelle gesammelt.
Beispiel: clean: rm .o a.out// Es wird alle Objektfiles und das Excecutable a.out gelöscht!!! Genau in solchen Fällen, wo Sie mehrere Ziele erzeugen, haben Sie die Möglichkeit (wie im Punkt make-Befehl schon gesagt) beim Aufruf von make in der Kommandozeile das Ziel anzugeben, das Sie mit diesem Aufruf erzeugen wollen. Beispiel: $ make Prog // Es wird nur das Ziel Prog ausgeführt!!! oder $ make clean // Es werden alle von "make" erzeugten Dateien gelöscht!!! RegelnWenn Sie dann eine Abhängigkeit formuliert haben, sollten Sie noch angeben, wie make es tun soll (dies ist die zum Ziel gehörige Regel), also durch welchen Befehl das Ziel aus der Quelle erzeugt werden kann.Dieses Kommando geben Sie genauso ein, wie Sie es auch in der Shell tun würden. Achten Sie darauf, die Befehlszeilen immer mit einem Tabulatorzeichen zu beginnen!!! Beispiel 1: (aus dem Beispiel 1 ) Prog: Prog1.o Prog2.o g++ -o Prog Prog1.o Prog2.o Prog1.o: Prog1.cpp g++ -c Prog1.cpp Prog2.o: Prog2.cpp g++ -c Prog2.cpp Zu einer Abhängigkeit können mehrere Regeln gehören, beispielweise: Beispiel 2: ( Beispiel 1 umgeschrieben) Prog: Prog1.cpp Prog2.cpp g++ -c Prog1.cpp g++ -c Prog2.cpp g++ -o Prog Prog1.o Prog2.o
Beispiele zur Benutzung von MakefilesBeispiel 1In diesem Fall haben wir eine Funktion, die wir häufig in vielen von uns geschriebenen Hauptprogrammen benutzen. Um diese Funktion nur einmal zu schreiben und nicht jedes Mal in den Quelltext unserer Hauptprogramme kopieren zu müssen, werden wir dieses Problem mit dem Make-Werkzeug umgehen. Dazu definieren wir diese häufig benutzte Funktion in einer eigenen Datei und schreiben ein Makefile, das die Funktion und die Hauptprogramme kompiliert und linkt.Im Beispiel beschreiben wir alles nur mit einem Hauptprogramm "Prog1.cpp" und einer Funktion, die in der Datei "Prog2.cpp" steht. Das sieht dann so aus:
FazitMit make sparen wir uns lange Eingaben beim Compilieren. Die Compilierungseingabe wird nur einmal im Makefile geschrieben und komplizierte Optionen müssen wir nicht jedes Mal eintippen. Es wird nur gemacht, was nötig ist. Das Compilieren verursacht weniger Aufwand, weil ggf. Compilierungsschritte entfallen. In diesem Fall hat make das ganze Übersetzen vereinfacht.Beispiel 2Wir wollen einen Brief an verschiedene Adressen schicken, aber mit verschiedener Anrede oder verschiedene Briefstexte an die gleiche Adresse schicken.Da wir in Makefiles Betriebsystemsbefehle eingeben können, können wir das ganze mit dem make-Werkzeug steuern. Wir legen verschiedene Dateien (z.b.: adresse.txt, text.txt, anrede.txt, usw.) an und schreiben ein Makefile, das alles als Brief zusammenfügt. Die Datei für das Beispiel:
Makefile2 (in grün sind die Kommmentare)
FazitMit make ist die Arbeit erleichtert worden. Wir können nach Wunsch Dateien ändern, ohne darüber nachdenken zu müssen, welche Dateien neu erzeugt werden müssen und welche unverändert geblieben sind. make macht das Nötige, so dass nur bei Bedarf die Datei "kopf.txt" (Briefkopf) bzw. die Datei "brief.txt" (gesamte Brief) erzeugt werden.Beispiel 3In diesem Beispiel werden wir ein Programm auf unser Rechner installieren. Bei dieser Installation werden Makefiles benutzt (das ist bei den meisten Programmen so üblich). Da wir mit Makefile-Dateien umgehen können, können wir auch verstehen, was bei der Installation passiert und nach Wunsch die Installationsvorgehensweise anpassen.
FazitDie allermeisten Public Domain-Programmpakete werden über Makefiles übersetzt und installiert. Kenntnisse über Makefiles helfen, um bei Problemen die Übersetzung und Installation des Programmpakets flexibel zu gestalten.Vorgehenweise:
Makefiles für FortgeschritteneMakrosMakros sind Variablen, die über Zuweisungen Namen von Kommandos, komplette Kommandoaufrufe oder nur Kommando-Optionen, Verzeichnis- oder Dateinamen, ... speichern können.Die Syntax für die Zuweisung eines Makros entspricht der in Bourne-Shell-Skripten, also: MAKRONAME = Inhalt Sie dürfen im Namen nur alphanumerische Zeichen verwenden. Der Name muss zudem in der ersten Spalten einer Zeile beginnen. Als Inhalt gilt dann nicht nur ein Wort, sondern alle Zeichen bis zum Ende der Zeile. Um den Inhalt eines Makros zu erhalten, müssen Sie den Namen in Klammern mit vorangestellten $-Zeichen angeben, also etwa $(MAKRONAME). Makros werden sehr oft bei Definitinen von Compilernamen, Compileroptionen usw. benutzt. So haben Sie alle benötige Optionen an einer zentralen Stelle des Makefiles festgelegt. Ganz praktisch ist es, da Sie nur an einer Stelle den Wert ändern müssen, wenn Sie eine Compileroption für alle Compilierbefehle ändern wollen. Zudem findet man die Stelle schnell, wenn Sie die Makros ganz oben vor den Abhängigkeiten und Regeln blockweise definieren. Beispiel (Makefile1b) (Das ist das umgeschriebene Makefile1)!:#Name des Compilers CXX = g++ #Compileroptionen CFLAGS_FOR_OBJ = -c OUTPUT_OPTION = -o Prog: Prog1.o Prog2.o $(CXX) $(OUTPUT_OPTION) Prog Prog1.o Prog2.o Prog1.o: Prog1.cpp $(CXX) $(CFLAGS_FOR_OBJ) Prog1.cpp Prog2.o: Prog2.cpp $(CXX) $(CFLAGS_FOR_OBJ) Prog2.cpp Ausgabe nach dem Aufruf g++ -c Prog1.cpp g++ -c Prog2.cpp g++ -o Prog Prog1.o Prog2.o Es gibt vordefinierte Makros, die automatische Variablen genannt werden und die sehr praktisch sein können. Die vordefinierten Makros sind in der folgenden Tabelle aufgelistet:
Beispiel (Makefile1c) (Das ist das umgeschriebene Makefile1)!:# Name des Compilers CXX = g++ # Compileroptionen CFLAGS_FOR_OBJ = -c OUTPUT_OPTION = -o $@ # Durch dieses Ziel "Prog" wird ein Executable "Prog" erzeugt, # das Ziel hängt ab von den Objectfiles "Prog1.o" und "Prog2.o". # In der Regel wird "g++" aufgerufen, die beiden Objectfiles gelinkt # und das Executable "Prog" erzeugt. Prog: Prog1.o Prog2.o $(CXX) $(OUTPUT_OPTION) $+ # In der Regel wird ein Objectfile "Prog1.o" erzeugt. # Das zugehörige Ziel hängt vom File "Prog1.cpp" ab. Prog1.o: Prog1.cpp $(CXX) $(CFLAGS_FOR_OBJ) $^ # In der Regel wird ein Objectfile "Prog2.o" erzeugt. # Das zugehörige Ziel hängt vom File "Prog2.cpp" ab. Prog2.o: Prog2.cpp $(CXX) $(CFLAGS_FOR_OBJ) $^ Ausgabe nach dem Aufruf g++ -c Prog1.cpp g++ -c Prog2.cpp g++ -o Prog Prog1.o Prog2.o Aus der tabelle ergibt sich dann im Beispiel: $@ = Prog ( im target Prog) $+ = "Prog1.o Prog2.o" $^ = Prog1.cpp $^ = Prog2.cpp In dem Target "Prog1.o" und "Prog2.o" könnte man auch "$<" verwenden. Es gibt noch eine Reihe von weiteren internen Makros, die aber nur bei komplexen Makefiles gebraucht werden. Sie haben im Makefile auch Zugriff auf alle Umgebungsvariablen, die Sie in der Shell definiert haben. vordefinierte MakrosViele häufig benutzten Makros sind bereits in make als Makros eingebaut. Wenn Sie diese benutzen wollen, müssen Sie nur noch die Abhängigkeit(en) angeben. Die vordefinierten Makros können Sie durch den Auruf von "make -p" ausgeben lassen.
Eine Liste von wichtigen vordefinierten Makros zeigt Ihnen die folgende Tabelle:
Dabei sind die Makros $(CFLAGS), $(CPPFLAGS), $(LDFLAGS), $(TARGET_ARCH), $(LDFLAGS) und $(CXXFLAGS) nicht vordefiniert. Sie können durch das Setzen von gleichnamigen Environment-Variablen (oder Makefile-Makros) eigene Einstellungen einbringen. Beispiel (Makefile1b_1) (Das ist das umgeschriebene Makefile1)!:# Durch dieses Ziel "Prog" wird ein Executable "Prog" erzeugt, # das Ziel hängt ab von den Objectfiles "Prog1.o" und "Prog2.o". # In der Regel wird "g++" aufgerufen, die beiden Objectfiles gelinkt # und das Executable "Prog" erzeugt. Prog: Prog1.o Prog2.o $(CXX) $(OUTPUT_OPTION) Prog1.o Prog2.o # In der Regel wird ein Objectfile "Prog1.o" erzeugt. # Das zugehörige Ziel hängt vom File "Prog1.cpp" ab. Prog1.o: Prog1.cpp $(COMPILE.cc) Prog1.cpp # In der Regel wird ein Objectfile "Prog2.o" erzeugt. # Das zugehörige Ziel hängt vom File "Prog2.cpp" ab. Prog2.o: Prog2.cpp $(COMPILE.cc) Prog2.cpp Ausgabe nach dem Aufruf g++ -c Prog1.cpp g++ -c Prog2.cpp g++ -o Prog Prog1.o Prog2.o vordefinierte RegelnViele Regeln sind immer wieder sehr ähnlich. Aus diesem Grund sind einige bereits in make eingebaut. Praktisch bedeutet das, dass Sie nur noch die Abhängigkeiten angeben müssen; die Regel zum Übersetzen ist bereits bekannt.Die vordefinierten Regeln können Sie durch den Auruf von "make -p" ausgeben lassen. Beispiel 1(Makefile1d) (Das ist das umgeschriebene Makefile1)!:# Durch dieses Ziel "Prog" wird ein Executable "Prog" erzeugt, # das Ziel hängt ab von den Objectfiles "Prog1.o" und "Prog2.o". # In der Regel wird "g++" aufgerufen, die beiden Objectfiles gelinkt # und das Executable "Prog" erzeugt. Prog: Prog1.o Prog2.o g++ -o Prog Prog1.o Prog2.o # In der Regel wird ein Objectfile "Prog1.o" erzeugt. # Das zugehörige Ziel hängt vom File "Prog1.cpp" ab. Prog1.o: Prog1.cpp @echo "### eigene Regel" g++ -c -Wall -pedantic Prog1.cpp # vordefinierte Regel "%.o: %.cpp" wird trotz Deklaration der Abhängigkeit # durchgefuehrt Prog2.o: Prog2.cpp Ausgabe nach dem Aufruf ### eigene Regel g++ -c -Wall -pedantic Prog1.cpp g++ -c -o Prog2.o Prog2.cpp g++ -o Prog Prog1.o Prog2.o Beispiel 2(makefile1e) (Das ist das umgeschriebeneMakefile1)!:# Durch dieses Ziel "Prog" wird ein Executable "Prog" erzeugt, # das Ziel hängt ab von den Objectfiles "Prog1.o" und "Prog2.o". # In der Regel wird "g++" aufgerufen, die beiden Objectfiles gelinkt # und das Executable "Prog" erzeugt. Prog: Prog1.o Prog2.o g++ -o Prog Prog1.o Prog2.o # In der Regel wird ein Objectfile "Prog1.o" erzeugt. # Das zugehörige Ziel hängt vom File "Prog1.cpp" ab. Prog1.o: Prog1.cpp @echo "### eigene Regel" g++ -c Prog1.cpp # zur Erzeugung von "Prog2.o" aus "Prog2.cpp" wird die vordefinierte # Regel "%.o: %.cpp" verwendet Ausgabe nach dem Aufruf ### eigene Regel g++ -c -Wall -pedantic Prog1.cpp g++ -c -o Prog2.o Prog2.cpp g++ -o Prog Prog1.o Prog2.o Beispiel 3(makefile1f) (Das ist das umgeschriebene Makefile1)!:# In der "Regel" mit Wildcards wird ein Objectfile "*.o" erzeugt. # Das zugehoerige Ziel hängt vom zugehoerigen File "*.cpp" ab. # %.o: %.cpp # g++ -c $< # Durch dieses Ziel "Prog" wird ein Executable "Prog" erzeugt, # das Ziel hängt von den Objectfiles "Prog1.o" und "Prog2.o" ab. # In der Regel wird "g++" aufgerufen, die beiden Objectfiles gelinkt # und das Executable "Prog" erzeugt. Prog: Prog1.o Prog2.o g++ -o Prog Prog1.o Prog2.o Ausgabe nach dem Aufruf g++ -c -o Prog1.o Prog1.cpp g++ -c -o Prog2.o Prog2.cpp g++ -o Prog Prog1.o Prog2.o
Links und Bücherempfehlung
|