Wir müssen ein Makefile verwenden, um alles für unser Projekt zusammenzuführen, aber unser Professor hat uns nie gezeigt, wie es geht.
Ich habe nur eine Datei a3driver.cpp
. Der Treiber importiert eine Klasse von einem Speicherort "/user/cse232/Examples/example32.sequence.cpp"
.
Das ist es. Alles andere ist in der enthalten .cpp
.
Wie würde ich ein einfaches Makefile erstellen, das eine ausführbare Datei namens erstellt a3a.exe
?
Antworten:
Da dies für Unix ist, haben die ausführbaren Dateien keine Erweiterungen.
Zu beachten ist, dass
root-config
es sich um ein Dienstprogramm handelt, das die richtige Kompilierung und Verknüpfung von Flags bietet. und die richtigen Bibliotheken zum Erstellen von Anwendungen gegen root. Dies ist nur ein Detail, das sich auf die ursprüngliche Zielgruppe dieses Dokuments bezieht.Mach mich Baby
oder du vergisst nie das erste Mal, dass du gemacht wurdest
Eine einführende Diskussion über make und wie man ein einfaches Makefile schreibt
Was ist Make? Und warum sollte es mich interessieren?
Das Tool Make ist ein Build-Abhängigkeitsmanager. Das heißt, es muss sichergestellt werden, welche Befehle in welcher Reihenfolge ausgeführt werden müssen, um Ihr Softwareprojekt aus einer Sammlung von Quelldateien, Objektdateien, Bibliotheken, Headern usw. usw. zu übernehmen. Einige davon haben sich möglicherweise geändert vor kurzem --- und verwandeln sie in eine korrekte aktuelle Version des Programms.
Eigentlich können Sie Make auch für andere Dinge verwenden, aber darüber werde ich nicht sprechen.
Ein triviales Makefile
Nehmen wir an, dass Sie ein Verzeichnis enthält:
tool
tool.cc
tool.o
support.cc
support.hh
undsupport.o
davon abhängen , welche aufroot
und sollen kompiliert werden in ein Programm mit dem Namentool
, und nehmen wir an, dass Sie schon auf den Quelldateien Hacking (was bedeutet , dass die bestehendetool
ist jetzt veraltet) und wollen Kompilieren Sie das Programm.Um dies selbst zu tun, könnten Sie
Überprüfen Sie, ob entweder
support.cc
odersupport.hh
neuer alssupport.o
, und führen Sie in diesem Fall einen Befehl wie ausÜberprüfen Sie, ob entweder
support.hh
odertool.cc
neuer alstool.o
, und führen Sie in diesem Fall einen Befehl wie ausÜberprüfen Sie, ob
tool.o
es neuer als isttool
, und führen Sie in diesem Fall einen Befehl wie ausPuh! Was für ein Ärger! Es gibt viel zu beachten und mehrere Möglichkeiten, Fehler zu machen. (Übrigens: Die Einzelheiten der hier gezeigten Befehlszeilen hängen von unserer Softwareumgebung ab. Diese funktionieren auf meinem Computer.)
Natürlich können Sie jedes Mal alle drei Befehle ausführen. Das würde funktionieren, lässt sich aber nicht gut auf eine umfangreiche Software skalieren (wie DOGS, deren Kompilierung auf meinem MacBook von Grund auf mehr als 15 Minuten dauert).
Stattdessen können Sie eine Datei mit dem folgenden Namen schreiben
makefile
:und geben Sie einfach
make
in die Befehlszeile ein. Dadurch werden die drei oben gezeigten Schritte automatisch ausgeführt.Die hier nicht eingerückten Zeilen haben die Form "Ziel: Abhängigkeiten" und geben an, dass die zugehörigen Befehle (eingerückte Zeilen) ausgeführt werden sollen, wenn eine der Abhängigkeiten neuer als das Ziel ist. Das heißt, die Abhängigkeitslinien beschreiben die Logik dessen, was neu erstellt werden muss, um Änderungen in verschiedenen Dateien zu berücksichtigen. Wenn sich dies
support.cc
ändert,support.o
muss dies neu erstellt werden,tool.o
kann aber in Ruhe gelassen werden. Wennsupport.o
Änderungen neu erstellt werdentool
müssen.Die Befehle, die jeder Abhängigkeitszeile zugeordnet sind, werden mit einer Registerkarte abgesetzt (siehe unten). Sie sollten das Ziel ändern (oder zumindest berühren, um die Änderungszeit zu aktualisieren).
Variablen, integrierte Regeln und andere Extras
An diesem Punkt erinnert sich unser Makefile einfach an die Arbeit, die erledigt werden muss, aber wir mussten immer noch jeden einzelnen Befehl in seiner Gesamtheit herausfinden und eingeben. Das muss nicht so sein: Make ist eine leistungsstarke Sprache mit Variablen, Textmanipulationsfunktionen und einer ganzen Reihe integrierter Regeln, die uns dies erheblich erleichtern können.
Variablen erstellen
Die Syntax für den Zugriff auf eine make-Variable lautet
$(VAR)
.Die Syntax für die Zuweisung zu einer Make-Variablen lautet:
VAR = A text value of some kind
(oderVAR := A different text value but ignore this for the moment
).Sie können Variablen in Regeln wie dieser verbesserten Version unseres Makefiles verwenden:
Das ist etwas besser lesbar, erfordert aber immer noch viel Tippen
Funktionen ausführen
GNU make unterstützt eine Vielzahl von Funktionen für den Zugriff auf Informationen aus dem Dateisystem oder andere Befehle im System. In diesem Fall interessiert uns,
$(shell ...)
welches die Ausgabe der Argumente erweitert und$(subst opat,npat,text)
welches alle Instanzen vonopat
durchnpat
im Text ersetzt.Dies auszunutzen gibt uns:
Das ist einfacher zu tippen und viel besser lesbar.
Beachte das
Implizite und Musterregeln
Wir würden allgemein erwarten, dass alle C ++ - Quelldateien gleich behandelt werden, und Make bietet drei Möglichkeiten, dies anzugeben:
Implizite Regeln sind eingebaut, und einige werden unten diskutiert. Musterregeln werden in einer Form wie angegeben
Dies bedeutet, dass Objektdateien aus C-Quelldateien generiert werden, indem der angezeigte Befehl ausgeführt wird, wobei die "automatische" Variable
$<
auf den Namen der ersten Abhängigkeit erweitert wird.Eingebaute Regeln
Make verfügt über eine ganze Reihe integrierter Regeln, die bedeuten, dass ein Projekt sehr oft tatsächlich mit einem sehr einfachen Makefile kompiliert werden kann.
Die in GNU eingebaute Regel für C-Quelldateien ist die oben gezeigte. Ebenso erstellen wir Objektdateien aus C ++ - Quelldateien mit einer Regel wie
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
.Einzelne Objektdateien werden mit verknüpft
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, dies funktioniert jedoch in unserem Fall nicht, da mehrere Objektdateien verknüpft werden sollen.Von integrierten Regeln verwendete Variablen
Die integrierten Regeln verwenden eine Reihe von Standardvariablen, mit denen Sie lokale Umgebungsinformationen angeben können (z. B. wo sich die ROOT-Include-Dateien befinden), ohne alle Regeln neu schreiben zu müssen. Die für uns wahrscheinlich interessantesten sind:
CC
- der zu verwendende C-CompilerCXX
- der zu verwendende C ++ - CompilerLD
- der zu verwendende LinkerCFLAGS
- Kompilierungsflag für C-QuelldateienCXXFLAGS
- Kompilierungsflags für C ++ - QuelldateienCPPFLAGS
- Flags für den c-Präprozessor (enthalten normalerweise in der Befehlszeile definierte Dateipfade und Symbole), die von C und C ++ verwendet werdenLDFLAGS
- Linker-FlagsLDLIBS
- zu verknüpfende BibliothekenEin einfaches Makefile
Indem wir die integrierten Regeln nutzen, können wir unser Makefile vereinfachen, um:
Wir haben auch einige Standardziele hinzugefügt, die spezielle Aktionen ausführen (z. B. das Bereinigen des Quellverzeichnisses).
Beachten Sie, dass beim Aufrufen von make ohne Argument das erste in der Datei gefundene Ziel verwendet wird (in diesem Fall alle). Sie können jedoch auch das abzurufende Ziel benennen, wodurch
make clean
die Objektdateien in diesem Fall entfernt werden.Wir haben immer noch alle Abhängigkeiten fest codiert.
Einige mysteriöse Verbesserungen
Beachte das
make
dannls -A
sehen Sie eine Datei mit dem Namen ,.depend
die Dinge enthält , die wie Make Abhängigkeit Linien aussehenAndere Lesart
Kennen Sie Fehler und historische Notizen
Die Eingabesprache für Make ist Leerzeichenempfindlich. Insbesondere müssen die Aktionszeilen nach Abhängigkeiten mit einer Registerkarte beginnen . Eine Reihe von Leerzeichen kann jedoch gleich aussehen (und tatsächlich gibt es Editoren, die Tabulatoren stillschweigend in Leerzeichen konvertieren oder umgekehrt), was zu einer Make-Datei führt, die richtig aussieht und immer noch nicht funktioniert. Dies wurde früh als Fehler identifiziert, aber ( die Geschichte geht ) es wurde nicht behoben, da es bereits 10 Benutzer gab.
(Dies wurde aus einem Wiki-Beitrag kopiert, den ich für Physikstudenten geschrieben habe.)
quelle
-pthread
Flag bewirktgcc
, dass die erforderlichen Makros definiert werden,-D_REENTRANT
ist nicht erforderlich .root-config
). Eine allgemeinere Alternative mit der gleichen Fähigkeit sollte vorgeschlagen werden, falls vorhanden, oder sie sollte einfach weggelassen werden. Ich habe wegen der Auflistung und Erklärung der am häufigsten verwendeten Makros nicht herabgestimmt.Ich habe immer gedacht, dass dies anhand eines detaillierten Beispiels einfacher zu lernen ist. So denke ich über Makefiles. Für jeden Abschnitt haben Sie eine Zeile, die nicht eingerückt ist und den Namen des Abschnitts gefolgt von Abhängigkeiten anzeigt. Die Abhängigkeiten können entweder andere Abschnitte sein (die vor dem aktuellen Abschnitt ausgeführt werden) oder Dateien (die bei Aktualisierung dazu führen, dass der aktuelle Abschnitt beim nächsten Ausführen erneut ausgeführt wird
make
).Hier ist ein kurzes Beispiel (denken Sie daran, dass ich 4 Leerzeichen verwende, in denen ich eine Registerkarte verwenden sollte. Beim Stapelüberlauf kann ich keine Registerkarten verwenden.)
Wenn Sie
make
eingeben, wird der erste Abschnitt (a3driver) ausgewählt. a3driver hängt von a3driver.o ab, daher wird dieser Abschnitt aufgerufen. a3driver.o hängt von a3driver.cpp ab, daher wird es nur ausgeführt, wenn sich a3driver.cpp seit dem letzten Ausführen geändert hat. Angenommen, es wurde ausgeführt (oder wurde noch nie ausgeführt), kompiliert es a3driver.cpp in eine .o-Datei, kehrt dann zu a3driver zurück und kompiliert die endgültige ausführbare Datei.Da es nur eine Datei gibt, kann diese sogar reduziert werden auf:
Der Grund, warum ich das erste Beispiel gezeigt habe, ist, dass es die Leistungsfähigkeit von Makefiles zeigt. Wenn Sie eine andere Datei kompilieren müssen, können Sie einfach einen weiteren Abschnitt hinzufügen. Hier ist ein Beispiel mit einer secondFile.cpp (die in einen Header namens secondFile.h geladen wird):
Auf diese Weise wird nur secondFile.cpp neu kompiliert (nicht a3driver.cpp), wenn Sie etwas in secondFile.cpp oder secondFile.h ändern und neu kompilieren. Wenn Sie alternativ etwas in a3driver.cpp ändern, wird secondFile.cpp nicht neu kompiliert.
Lassen Sie mich wissen, wenn Sie Fragen dazu haben.
Es ist auch traditionell, einen Abschnitt mit dem Namen "all" und einen Abschnitt mit dem Namen "clean" einzuschließen. "all" erstellt normalerweise alle ausführbaren Dateien und "clean" entfernt "Build-Artefakte" wie .o-Dateien und die ausführbaren Dateien:
EDIT: Ich habe nicht bemerkt, dass Sie unter Windows sind. Ich denke, der einzige Unterschied besteht darin, das
-o a3driver
zu ändern-o a3driver.exe
.quelle
Warum listet jeder gerne Quelldateien auf? Ein einfacher Suchbefehl kann dies leicht erledigen.
Hier ist ein Beispiel für ein schmutzig einfaches C ++ - Makefile. Legen Sie es einfach in einem Verzeichnis mit
.C
Dateien ab und geben Siemake
...quelle
Sie hatten zwei Möglichkeiten.
Option 1: einfachstes Makefile = KEINE MAKEFILE.
Benennen Sie "a3driver.cpp" in "a3a.cpp" um und schreiben Sie dann in die Befehlszeile:
Und das ist es. Wenn Sie GNU Make verwenden, verwenden Sie "make" oder "gmake" oder was auch immer.
Option 2: ein 2-zeiliges Makefile.
quelle
nmake
. Dielink
Befehlszeile sieht auch für einen bestimmten Compiler sehr spezifisch aus und sollte zumindest dokumentieren, welcher.Ihre Make-Datei enthält eine oder zwei Abhängigkeitsregeln, je nachdem, ob Sie mit einem einzelnen Befehl oder mit einem Befehl für die Kompilierung und einem für die Verknüpfung kompilieren und verknüpfen.
Abhängigkeiten sind ein Regelbaum, der so aussieht (beachten Sie, dass der Einzug muss eine TAB sein):
Dort muss eine leere Zeile nach den Befehlen für ein Ziel sein, und es darf nicht eine Leerzeile vor den Befehlen sein. Das erste Ziel im Makefile ist das Gesamtziel, und andere Ziele werden nur erstellt, wenn das erste Ziel von ihnen abhängt.
Ihr Makefile sieht also ungefähr so aus.
quelle
Ich schlage vor (beachten Sie, dass der Einzug ein TAB ist):
oder
Der letztere Vorschlag ist etwas besser, da er implizite GNU Make-Regeln wiederverwendet. Um zu funktionieren, muss eine Quelldatei jedoch denselben Namen wie die endgültige ausführbare Datei haben (dh:
tool.c
undtool
).Beachten Sie, dass es nicht erforderlich ist, Quellen zu deklarieren. Zwischenobjektdateien werden mithilfe impliziter Regeln generiert. Folglich
Makefile
funktioniert dies für C und C ++ (und auch für Fortran usw.).Beachten Sie auch, dass Makefile standardmäßig
$(CC)
als Linker verwendet wird.$(CC)
funktioniert nicht zum Verknüpfen von C ++ - Objektdateien. Wir ändernLINK.o
nur deswegen. Wenn Sie C-Code kompilieren möchten, müssen Sie denLINK.o
Wert nicht erzwingen .Natürlich können Sie auch Ihre Kompilierungsflags mit Variable
CFLAGS
hinzufügen und Ihre Bibliotheken hinzufügenLDLIBS
. Zum Beispiel:Eine Randbemerkung: Wenn Sie externe Bibliotheken verwenden müssen, schlage ich vor , zu pkg-config zu verwenden , um richtig eingestellt
CFLAGS
undLDLIBS
:Der aufmerksame Leser wird feststellen, dass dies
Makefile
nicht ordnungsgemäß wiederhergestellt wird, wenn ein Header geändert wird. Fügen Sie diese Zeilen hinzu, um das Problem zu beheben:-MMD
Ermöglicht das Erstellen von D-Dateien, die Makefile-Fragmente zu Header-Abhängigkeiten enthalten. Die zweite Zeile verwendet sie nur.Sicher, ein gut geschriebenes Makefile sollte auch
clean
unddistclean
Regeln:Beachten Sie,
$(RM)
ist das Äquivalent vonrm -f
, aber es ist eine gute Praxis, nicht anzurufenrm
direkt .Die
all
Regel wird auch geschätzt. Um zu arbeiten, sollte es die erste Regel Ihrer Datei sein:Sie können auch eine
install
Regel hinzufügen :DESTDIR
ist standardmäßig leer. Der Benutzer kann festlegen, dass Ihr Programm auf einem alternativen System installiert wird (obligatorisch für den Cross-Compilation-Prozess). Paketverwalter für die Mehrfachverteilung können sich ebenfalls ändernPREFIX
, um Ihr Paket in zu installieren/usr
.Ein letztes Wort: Platzieren Sie keine Quelldateien in Unterverzeichnissen. Wenn Sie das wirklich wollen, behalten Sie dies
Makefile
im Stammverzeichnis und verwenden Sie vollständige Pfade, um Ihre Dateien zu identifizieren (dhsubdir/file.o
).Zusammenfassend sollte Ihr vollständiges Makefile folgendermaßen aussehen:
quelle
make
mir bekannten Implementierungen (GNU Make und BSD Make) benötigt Leerzeilen zwischen Regeln. Es gibt jedoch Unmengen vonmake
Implementierungen mit ihren eigenen Fehlern.Ich habe die Antwort von Friedmud verwendet . Ich habe eine Weile darüber nachgedacht, und es scheint ein guter Weg zu sein, um loszulegen. Diese Lösung verfügt auch über eine genau definierte Methode zum Hinzufügen von Compiler-Flags. Ich antwortete erneut, weil ich Änderungen vorgenommen habe, damit es in meiner Umgebung, Ubuntu und g ++, funktioniert. Manchmal sind mehr Arbeitsbeispiele der beste Lehrer.
Makefiles scheinen sehr komplex zu sein. Ich habe eine verwendet, aber es wurde ein Fehler generiert, der darauf zurückzuführen ist, dass keine Verknüpfung in g ++ - Bibliotheken besteht. Diese Konfiguration hat dieses Problem gelöst.
quelle