[Logo der Universität Bayreuth]
Universität Bayreuth

Mathematisches
Institut



 Einleitung

 Erste Schritte

 Mail und News

 Drucken

 KDE

 LaTeX/TeX

 Linksammlung

 Linuxtools

 Netzwerk

 Programmieren

 Windows

 X Window

 Anträge

 Kontakt

Programmieren <-

Steuerung der Übersetzung mit Make

Die einzigen Werkzeuge, die wir bisher beim Programmieren benutzt haben, waren der Compiler und der Debugger. Diese sind aber nicht die einzigen, die zur Verfügung stehen. Unter Linux (aber auch unter anderen Betriebssystemen) gibt es noch weitere mächtige Werkzeuge, die uns beim Programmieren und bei der Softwareentwicklung unterstützen. Eine der wichtigsten ist make und dieses Tool wollen wir hier kennenlernen.
Make 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".
Das Auspacken erfolgt mit dem Befehl "tar xzvf make_makefile_bsp.tar.gz" (es entsteht ein Unterverzeichnis "make" mit den Dateien).






Inhaltsverzeichnis

  1. Zweck und Fähigkeiten von Make

  2. make-Befehl

  3. Grundprinzipien bei der Benutzung von Make-Dateien

    1. besondere Zeichen
    2. Abhängigkeiten
    3. Regeln

  4. Beispiele zur Benutzung von Makefiles

    1. Beispiel 1
    2. Beispiel 2
    3. Beispiel 3

  5. Makefiles für Fortgeschrittene

    1. Makros
    2. vordefinierte Makros
    3. eingebaute Regeln

  6. Übersetzungsvorgang

  7. Links und Bücherempfehlung








top top

Zweck und Fähigkeiten von Make

Bisher 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.•••••••••




top top

make-Befehl

make 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

   -f    Wollen Sie ein Makefile verwenden, das einen anderen Namen trägt, geben Sie diesen hinter der Option -f an, beispielsweise:  make -f <Dateiname>.
-n Wollen Sie zunächst einmal Ihr Makefile testen, starten Sie make mit der Option -n. Dann werden alle Befehle nur ausgedruckt, aber nicht ausgeführt.
-k Normalerweise bricht make ab, wenn ein aufgerufenes Programm mit einem Fehler endet. Mit der Option -k veranlassen Sie, trotzdem mit der abarbeitung fortzufahren.
-i Entspricht der makefile-Anweizung .IGNORE und sorgt dafür, dass make nicht abbricht, wenn ein Kommando einen Fehler meldet(Exit-Status )
-d (debug) Für Testzwecke werden ausführliche Information zu den betrachteten dateien und deren Datumsangaben ausgegeben
-t (touch) Das Zielobjekt (oder Zielobjekte) erhält ein neues Erstellungsdatum, ohne dass eine Generierung erfolgt.
-s Entspricht dem Eintrag ".Silent" und unterdrückt die Ausgabe der ausgeführten Kommandos.
-v (version)Die Version des make-Werkzeugs wird ausgegeben.


Fehler bzw. Warnhinweise

make 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.



top top

Grundprinzipien bei der Benutzung von Make-Dateien

Ein 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

  • Kommentare
    In Makefiles sollen und können Sie auch Kommentare einfügen. Diese Beginnen mit einen Doppelkreuz "#". Damit kommentieren sie alles bis zum Ende der aktuellen Zeile aus. Hier haben Sie auch die Möglichkeit einen Backslash am Zeilenende zu setzen (siehe Fortsetzung einer Zeile). Diese Vorgehenweise kann aber gefährlich sein, wenn Sie den backslash am Ende eines Kommentar gesetzt haben und in der nachkommenden zeile kein Kommentar stehen haben. In diesem Fall wird dann eine Regel nicht ausgeführt oder eine Abhängigkeit nicht berücksichtigt..

    Beispiel
    #Hier ist ein kommentar\Das Ziel "brief.txt" hängt ab von den Dateien "kopf.txt" und "text.txt".\
    # In der Regel werden die Dateien "kopf.txt" und "text.txt" mit dem Unix-Befehl \
    # "cat" zusammengefügt, um die Datei "brief.txt" zu erzeugen.\
                  g++ -c prog.cpp



    //  Dieser Befehl wird nicht ausgeführt !!!



  • Fortsetzung einer Zeile
    In einem Makefile steht pro Zeile nur eine Regel, Definition oder Befehl. Reicht Ihnen die Zeile nicht aus, weil Sie beispielsweise eine große Zahl von Compileroptionen übergeben, können Sie an das Ende der Zeile eine Backslash "\" anhängen. Das ist ein spezielles Fortsetzungszeichen, welches make anweist, die nachfolgende Zeile noch mit der aktuellen zusammenzufassen.

  • ausführbare Befehle
    Zeilen mit ausführbare befehlen müssen Sie mit einem Tabulatorzeichen einrücken, damit diese als solche erkannt werden. Leerzeichen sind anstelle davon nicht erlaubt. Wenn etwas mit der Erzeugung nicht funktioniert, liegt es meistens am fehlenden Tab. Sie erhalten dann Die Meldung:

    <Name des Makefiles>:<Zeilennummer>: ••• missing separator. Stop.

top top

Abhängigkeiten

Ein 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.

install
installiert das Programm auf dem Rechner
clean
löscht Dateien (ursprünglicher Zustand)
dist
erzeugt ein Quellpacket um es weiterzugeben
check / test
führt Tests durch, um die Ordnungsgemäße Erzeugung des programms zu überprüfen
depend
berechnet die Abhängigkeiten unter den einzeilnen Dateien neu und speichert diese (meist im Makefile)




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!!!

top top

Regeln

Wenn 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




top top

Beispiele zur Benutzung von Makefiles


Beispiel 1

In 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:
Prog1.cpp
#include <iostream>
#include <iomanip>
using namespace std;
double endpreis_berechnung(double preis);
int main()
{
   double preis ;
  cout<<"Diese Programm rechnet: Preis + Mehrwertsteuer"
  cout<<endl;
  cout<<"Geben Sie den Preis(ohne Mehrwertsteuer) ein: ";
  cin>>preis;
  endpreis_berechnung( preis);  //  Funktionsaufruf 
  return 0;
}
Prog2.cpp
#include <iostream>
#include <iomanip>
using namespace std;
double endpreis_berechnung(double preis)
{
  double endpreis;
  endpreis= preis*1.19;
  cout<<" Endpreis(Preis + Mehrwertsteuer)= "<<endpreis; 
  cout<<endl;
}
Erklärung
Dies ist ein von unsere Hauptprogrammen. Die Funktion endpreis_berechnung() befindet sich nicht in diesem Programm aber ihren Prototyp muss trotzdem im Programm sein(wichtig fürs Erzeugen des Objektfile:Prog1.o).
Erklärung
Dies ist die häufig gebrauchte Funktion. Wir haben hier keine Main-Funtion. Die zwei Programme Prog1.cpp und Prog2.cpp sind von einander abhängig.



makefile1 (in grün sind die Kommmentare)
# Hier wird ein Executable Prog erzeugt und dies hängt von den Objektfiles Prog1.o und Prog2.o ab.
Prog: Prog1.o Prog2.o
            g++ -o Prog Prog1.o Prog2.o
# Hier wird ein Objektfile Prog1.0 erzeugt und dies hängt vom und Prog1.cpp ab.
Prog1.o: Prog1.cpp
            g++ -c Prog1.cpp
# Hier wird ein Objektfile Prog2.0 erzeugt und dies hängt vom und Prog2.cpp ab
Prog2.o: Prog2.cpp
            g++ -c Prog2.cpp

  • erste Compilerung:

    hiwicip2@btwapmatx10:../make > make -f makefile1
    // make-Aufruf
    g++ -c Prog1.cpp
    // Es wird das Objektfile Prog1.o anhand von Prog1.cpp erzeugt.
    g++ -c Prog2.cpp
    // Es wird das Objektfile Prog2.o anhand von Prog2.cpp erzeugt.
    g++ -o Prog Prog1.o Prog2.o
    // Es wird das Executable Prog durch das Linken von Prog1.o und Prog2.o erzeugt.

  • Wir ändern Prog1.cpp

    hiwicip2@btwapmatx10:../make > make -f makefile1
    // make-Aufruf
    g++ -c Prog1.cpp
    // Es wird nur das Objektfile Prog1.o anhand von Prog1.cpp erzeugt. Prog2.cpp ist nicht geändert worden. Make erzeugt nicht mehr Prog2.o (ist up to date)
    g++ -o Prog Prog1.o Prog2.o
    // Es wird das Executable Prog durch das Linken von Prog1.o und Prog2.o erzeugt.

  • Doppelter Aufruf von make

    hiwicip2@btwapmatx10:../make > make -f makefile1
    // make-Aufruf (erste Compilierung)
    g++ -c Prog1.cpp
    // Es wird das Objektfile Prog1.o anhand von Prog1.cpp erzeugt.
    g++ -c Prog2.cpp
    // Es wird das Objektfile Prog2.o anhand von Prog2.cpp erzeugt.
    g++ -o Prog Prog1.o Prog2.o
    // Es wird das Executable Prog durch das Linken von Prog1.o und Prog2.o erzeugt.
    hiwicip2@btwapmatx10:../make > make -f makefile1
    // make-Aufruf (zweite Compilierung)
    make: `Prog' is up to date. // Es wurde nichts an den Quelldateien geändert. Make merkt es und tut nichts.

Fazit

Mit 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.

top top

Beispiel 2

Wir 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:
adresse.txt    datum.txt    anrede.txt    kopf.txt    text.txt    brief.txt

Makefile2 (in grün sind die Kommmentare)

# Das Ziel "all" hängt ab von den Dateien "brief.txt" und "kopf.txt".
all: brief.txt kopf.txt

#Das Ziel "brief.txt" hängt ab von den Dateien "kopf.txt" und "text.txt".
# In der Regel werden die Dateien "kopf.txt" und "text.txt" mit dem Unix-Befehl
# "cat" zusammengefügt, um die Datei "brief.txt" zu erzeugen.
# Der Klammeraffe "@" vor den Unix-Kommandos verhindert die zusä,tzliche
# Ausgabe der Unix-Kommandos vor ihrem Ablauf.


brief.txt: kopf.txt text.txt
            @echo "kompletter Brief \"brief.txt\" erzeugen"
            @cat kopf.txt text.txt > brief.txt

# Das Ziel "kopf.txt" hängt ab von den Dateien "adresse.txt", "datum.txt"
# und "anrede.txt".
# In der Regel werden die Dateien "adresse.txt", "datum.txt" und "anrede.txt"
# mit "cat" zusammengefügt, um die Datei "kopf.txt" zu erzeugen.

kopf.txt: adresse.txt datum.txt anrede.txt
            @echo "Kopf \"kopf.txt\" erzeugen"
            @cat adresse.txt datum.txt anrede.txt > kopf.txt

  • erster Aufruf

    hiwicip2@btwapmatx10:~../make > make -f makefile2
    // make-Aufruf
    Kopf "kopf.txt" erzeugen
    // Die Datei kopf.txt wird erzeugt.
    kompletter Brief "brief.txt" erzeugen
    // Jetzt wird brief.txt erzeugt.

  • Wir ändern die Datei text.txt

    hiwicip2@btwapmatx10:~../make > make -f makefile2
    // make-Aufruf
    kompletter Brief "brief.txt" erzeugen
    // Es wird nur brief.txt erzeugt, weil kopf.txt up to date ist (hat sich nicht geändert).

  • Wir ändern eine Datei, von der kopf.txt abhängt, beispielsweise: adresse.txt

    hiwicip2@btwapmatx10:~../make > make -f makefile2
    // make-Aufruf
    Kopf "kopf.txt" erzeugen
    // Die Datei kopf.txt wird neu erzeugt.
    kompletter Brief "brief.txt" erzeugen
    // Und brief.txt wird erzeugt.

  • Wir ändern keine Datei (doppelter Aufruf)

    hiwicip2@btwapmatx10:~../make > make -f makefile2
    // erster make-Aufruf
    Kopf "kopf.txt" erzeugen
    // Die Datei kopf.txt wird erzeugt.
    kompletter Brief "brief.txt" erzeugen
    // Jetzt wird brief.txt erzeugt.
    hiwicip2@btwapmatx10:~../make > make -f makefile2
    // zweiter make-Aufruf
    make: `all' is up to date. // Es wurde nichts an den Quelldateien geändert. Make merkt es und tut nichts.

Fazit

Mit 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.

top top

Beispiel 3

In 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.
  • Wir holen uns das Programmspacket units unter http://www.gnu.org/software/units/units.html und speichern es in einem Arbeitsverzeichnis (z.B. work). Wir erzeugen ein Verzeichnis local im Homeverzeichnis ($HOME), unter dem wir die installierten Programme speichern wollen.
  • Im Arbeitsverzeichnis "$HOME/work" packen wir das Programmpaket aus mit "tar xzvf units-1.80.tar.gz" und gehen mit "cd units-1.80" in das Verzeichnis units-1.80.
  • In diesem Verzeichnis (units-1.80) gibt es keine Datei Makefile, die Datei (Makefile) können wir aber mit dem Befehl "./configure --prefix=$HOME/local" automatisch erstellen.
    In diesem Makefile bemerken wir das Ziel "all: units units.1 units.info". Es wird also nur das Executable units sowie die Dateien units.1 und units.info erzeugt, wenn wir make aufrufen.
    Wir rufen dann make auf. Das Executable units wird erzeugt.
    Dann Rufen wir "make install" auf. Mit "make install" wird das Programm units auf dem Rechner installiert und alle benöntige Dateien werden gesichert. Dannach können wir das ganze Verzeichnis units-1.80 mit dem befehl:" rm -rf units-180 " löschen.

Fazit

Die 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:
  1. "./configure" bzw. "./configure --prefix=$HOME/local"
  2. make
  3. make install
  4. rm -rf <Packetsname>




top top

Makefiles für Fortgeschrittene

Makros

Makros 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:


Makros


Bedeutung

$@
voller Name des Ziels

$•
Name des Ziels ohne eine Endung

$<
Name der ersten abhängigen Datei

$?
Namen aller abhängigen Dateien, die neuer als das Ziel sind, getrennt durch Leerzeichnen

$+
Namen aller abhängigen Dateien, getrennt durch Leerzeichnen

$^
Namen aller abhängigen Dateien, getrennt durch Leerzeichnen; doppelt vorkommende werden dabei weggelassen



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.


top top

vordefinierte Makros

Viele 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.
implizite Regel Beispiel zugehörige Regel ausgeführtes Kommando
%: %.o Prog1: Prog1.o $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ cc Prog1.o -o Prog1
%.o: %.c Prog1.o: Prog1.c $(COMPILE.c) $(OUTPUT_OPTION) $< cc -c -o Prog1.o Prog1.c
%.o: %.cpp Prog1.o: Prog1.cpp $(COMPILE.cpp) $(OUTPUT_OPTION) $< g++ -c -o Prog1.o Prog1.cpp

Eine Liste von wichtigen vordefinierten Makros zeigt Ihnen die folgende Tabelle:


Regeln


definiert als

CC
cc

CXX
g++

OUTPUT_OPTION
-o $@

COMPILE.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

COMPILE.cc
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

LINK.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

LINK.cc
$(CXX) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

AR
ar

ARFLAGS
rv


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


top top

vordefinierte Regeln

Viele 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



top top

Links und Bücherempfehlung

->
Manualseite (von GNU)

->
Thomas Wieland: C++-Entwicklung mit Linux. dpunkt.verlag,
2., aktualisierte und erweiterte Auflage, 2002, 586 Seiten
vgl. insbesondere den Abschnitt "Steuerung der Übersetzung mit make-Dateien"
Dr. Thomas Wieland ist ehemaliger Mathematikstudent und Mitarbeiter an der Uni-Bayreuth.

->
Stephen R. Bourne: Das UNIXsystem V. Addison-Wesley


->
Jürgen Gulbins: UNIX, Version 7, System III und System V. Springer-Verlag








top top

Robert Baier ([e-mail-Adresse von Robert Baier])
© 2003 Robert Baier; © 1999-2002 Robert Baier, Sascha Herrmann
Make-Seiten: © 2003 Robert Baier, Ekue-sse Situ Tomety
[Seitenzähler] Last modified: 02.11.2016