So erstellen Sie ein EINFACHES C ++ - Makefile

303

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?

Widerfahren
quelle
9
.EXE so ist es definitiv Windows. Beim zweiten Gedanken ... ist der Pfad im Unix-Stil. Wahrscheinlich mit Mingw-32.
Nathan Osman
2
Seufzer. Ich nehme an, Sie müssen die Grundlagen jedes Gewerbes lernen, auch wenn Sie sie niemals anwenden werden. Ich muss nur verstehen, wie Sachen funktionieren. Die Chancen stehen jedoch gut, dass Sie sich immer in einer IDE wie Eclipse entwickeln. Sie erhalten hier eine Antwort auf Ihren einfachen einzeiligen Fall und es gibt viele Web-Tutorials. Wenn Sie jedoch fundiertes Wissen wünschen, können Sie das O'reilly-Buch nicht übertreffen (das gleiche gilt für die meisten s / w-Themen). amazon.com/Managing-Projects-Make-Nutshell-Handbooks/dp/… Wählen Sie eine gebrauchte Kopie von amazon, half.com, betterworldbooks eBay
Mawg sagt, Monica am
2
Der von @Dennis gepostete Link ist jetzt tot, aber das gleiche Material finden Sie auf dieser archive.org-Seite .
Guilherme Salomé
Ich bevorzuge die Ideen dieser Person. ( hiltmon.com/blog/2013/07/03/… ) Die Projektstruktur kann leicht angepasst werden. Und ich stimme auch zu, dass Entwicklerzeit für andere Dinge als automake / autoconf aufgewendet werden sollte. Diese Tools haben ihren Platz, aber vielleicht nicht für interne Projekte. Ich baue ein Skript, das eine solche Projektstruktur erzeugt.
Daisuke Aramaki
@ GuilhermeSalomé Danke, ich glaube, dies ist das beste einfache und vollständige Tutorial.
Hareen Laks

Antworten:

560

Da dies für Unix ist, haben die ausführbaren Dateien keine Erweiterungen.

Zu beachten ist, dass root-configes 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.hhund support.odavon abhängen , welche auf rootund sollen kompiliert werden in ein Programm mit dem Namen tool, und nehmen wir an, dass Sie schon auf den Quelldateien Hacking (was bedeutet , dass die bestehende toolist jetzt veraltet) und wollen Kompilieren Sie das Programm.

Um dies selbst zu tun, könnten Sie

  1. Überprüfen Sie, ob entweder support.ccoder support.hhneuer als support.o, und führen Sie in diesem Fall einen Befehl wie aus

    g++ -g -c -pthread -I/sw/include/root support.cc
  2. Überprüfen Sie, ob entweder support.hhoder tool.ccneuer als tool.o, und führen Sie in diesem Fall einen Befehl wie aus

    g++ -g  -c -pthread -I/sw/include/root tool.cc
  3. Überprüfen Sie, ob tool.oes neuer als ist tool, und führen Sie in diesem Fall einen Befehl wie aus

    g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
    -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
    -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

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

tool: tool.o support.o
    g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
        -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
        -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

tool.o: tool.cc support.hh
    g++ -g  -c -pthread -I/sw/include/root tool.cc

support.o: support.hh support.cc
    g++ -g -c -pthread -I/sw/include/root support.cc

und geben Sie einfach makein 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.omuss dies neu erstellt werden, tool.okann aber in Ruhe gelassen werden. Wenn support.oÄnderungen neu erstellt werden toolmü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 (oder VAR := A different text value but ignore this for the moment).

Sie können Variablen in Regeln wie dieser verbesserten Version unseres Makefiles verwenden:

CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
       -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
       -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
       -lm -ldl

tool: tool.o support.o
    g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)

tool.o: tool.cc support.hh
    g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
    g++ $(CPPFLAGS) -c support.cc

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 von opatdurch npatim Text ersetzt.

Dies auszunutzen gibt uns:

CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

tool: $(OBJS)
    g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh
    g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
    g++ $(CPPFLAGS) -c support.cc

Das ist einfacher zu tippen und viel besser lesbar.

Beachte das

  1. Wir geben immer noch explizit die Abhängigkeiten für jede Objektdatei und die endgültige ausführbare Datei an
  2. Wir mussten die Kompilierungsregel für beide Quelldateien explizit eingeben

Implizite und Musterregeln

Wir würden allgemein erwarten, dass alle C ++ - Quelldateien gleich behandelt werden, und Make bietet drei Möglichkeiten, dies anzugeben:

  1. Suffixregeln (in GNU make als veraltet angesehen, aber aus Gründen der Abwärtskompatibilität beibehalten)
  2. implizite Regeln
  3. Musterregeln

Implizite Regeln sind eingebaut, und einige werden unten diskutiert. Musterregeln werden in einer Form wie angegeben

%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c $<

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-Compiler
  • CXX - der zu verwendende C ++ - Compiler
  • LD - der zu verwendende Linker
  • CFLAGS - Kompilierungsflag für C-Quelldateien
  • CXXFLAGS - Kompilierungsflags für C ++ - Quelldateien
  • CPPFLAGS - Flags für den c-Präprozessor (enthalten normalerweise in der Befehlszeile definierte Dateipfade und Symbole), die von C und C ++ verwendet werden
  • LDFLAGS - Linker-Flags
  • LDLIBS - zu verknüpfende Bibliotheken

Ein einfaches Makefile

Indem wir die integrierten Regeln nutzen, können wir unser Makefile vereinfachen, um:

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
    $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh

support.o: support.hh support.cc

clean:
    $(RM) $(OBJS)

distclean: clean
    $(RM) tool

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 cleandie Objektdateien in diesem Fall entfernt werden.

Wir haben immer noch alle Abhängigkeiten fest codiert.

Einige mysteriöse Verbesserungen

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
    $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

depend: .depend

.depend: $(SRCS)
    $(RM) ./.depend
    $(CXX) $(CPPFLAGS) -MM $^>>./.depend;

clean:
    $(RM) $(OBJS)

distclean: clean
    $(RM) *~ .depend

include .depend

Beachte das

  1. Es gibt keine Abhängigkeitslinien mehr für die Quelldateien!?!
  2. Es gibt eine seltsame Magie im Zusammenhang mit .depend und abhängig
  3. Wenn Sie das tun , makedann ls -Asehen Sie eine Datei mit dem Namen , .dependdie Dinge enthält , die wie Make Abhängigkeit Linien aussehen

Andere 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.)

dmckee --- Ex-Moderator Kätzchen
quelle
9
Diese Methode zum Generieren von Abhängigkeiten ist veraltet und tatsächlich schädlich. Siehe Erweiterte automatische Abhängigkeitsgenerierung .
Maxim Egorushkin
5
-pthreadFlag bewirkt gcc, dass die erforderlichen Makros definiert werden, -D_REENTRANTist nicht erforderlich .
Maxim Egorushkin
8
@jcoe Es wird ein unnötiger zusätzlicher Präprozessor-Durchlauf ausgeführt, um Abhängigkeiten zu generieren. Wenn man unnötige Arbeit leistet, leitet es nur die Wärme ab, die die Eispole schmilzt, und nähert sich in größerem Maßstab dem Hitzetod unseres Universums.
Maxim Egorushkin
2
Wahrscheinlich ist "schädlich" ein bisschen zu viel, aber angesichts der Tatsache, dass explizite Phasen oder Ziele zur Erzeugung von Abhängigkeiten veraltet sind, da zumindest GCC 3, denke ich wirklich, dass wir alle daran vorbeikommen sollten. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail trauert um den 27.
2
Eigentlich sollte die akzeptierte Antwort nicht von einer bestimmten Software abhängen ( 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.
grüne Diode
56

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

a3driver: a3driver.o
    g++ -o a3driver a3driver.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

Wenn Sie makeeingeben, 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:

a3driver: a3driver.cpp
    g++ -o a3driver a3driver.cpp

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

a3driver: a3driver.o secondFile.o
    g++ -o a3driver a3driver.o secondFile.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

secondFile.o: secondFile.cpp secondFile.h
    g++ -c secondFile.cpp

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:

all: a3driver ;

clean:
    # -f so this will succeed even if the files don't exist
    rm -f a3driver a3driver.o

EDIT: Ich habe nicht bemerkt, dass Sie unter Windows sind. Ich denke, der einzige Unterschied besteht darin, das -o a3driverzu ändern -o a3driver.exe.

Brendan Long
quelle
Der absolute Code, den ich verwenden möchte, lautet: p4a.exe: p4driver.cpp g ++ -o p4a p4driver.cpp ABER er sagt mir "fehlendes Trennzeichen". Ich benutze TAB, aber das sagt mir das immer noch. Irgendeine Idee?
Befall
2
Soweit ich das beurteilen kann, wird diese Fehlermeldung nur angezeigt, wenn Sie Leerzeichen haben. Stellen Sie sicher, dass keine Zeilen mit Leerzeichen beginnen (Leerzeichen + Tabulator geben diesen Fehler aus). Das ist das einzige, woran ich denken kann.
Brendan Long
Hinweis für zukünftige Redakteure: StackOverflow kann keine Registerkarten rendern, selbst wenn Sie sie in der Antwort bearbeiten. Versuchen Sie daher bitte nicht, meinen Hinweis dazu zu "korrigieren".
Brendan Long
35

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 .CDateien ab und geben Sie make...

appname := myapp

CXX := clang++
CXXFLAGS := -std=c++11

srcfiles := $(shell find . -name "*.C")
objects  := $(patsubst %.C, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend
gebratener Schlamm
quelle
2
Ein Grund, Quelldateien nicht automatisch zu finden, besteht darin, dass unterschiedliche Build-Ziele unterschiedliche Dateien benötigen.
Hmijail trauert um Rücktritte
Einverstanden mit @hmijail sowie Submodulen, die eine Menge Quellen / Header enthalten, die Sie nicht kompilieren / verknüpfen möchten ... und zweifellos viele andere Umstände, unter denen eine umfassende Suche / Verwendung ungeeignet ist.
Ingenieur
Warum stattdessen "Shell Find" und nicht "Wildcard" verwenden?
Nolan
1
@ Nolan, um Quelldateien in einem Quellverzeichnisbaum zu finden
AlejandroVD
13

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:

nmake a3a.exe

Und das ist es. Wenn Sie GNU Make verwenden, verwenden Sie "make" oder "gmake" oder was auch immer.

Option 2: ein 2-zeiliges Makefile.

a3a.exe: a3driver.obj
    link /out:a3a.exe a3driver.obj
Niemand
quelle
3
Dies wäre eine ausgezeichnete Antwort, wenn nicht so viele Dinge über Details der OP-Umgebung vorausgesetzt würden. Ja, sie sind unter Windows, aber das bedeutet nicht, dass sie verwenden nmake. Die linkBefehlszeile sieht auch für einen bestimmten Compiler sehr spezifisch aus und sollte zumindest dokumentieren, welcher.
Tripleee
6

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

main_target : source1 source2 etc
    command to build main_target from sources

source1 : dependents for source1
    command to build source1

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.

a3a.exe : a3driver.obj 
    link /out:a3a.exe a3driver.obj

a3driver.obj : a3driver.cpp
    cc a3driver.cpp
John Knoeller
quelle
6

Ich schlage vor (beachten Sie, dass der Einzug ein TAB ist):

tool: tool.o file1.o file2.o
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

oder

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o

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.cund tool).

Beachten Sie, dass es nicht erforderlich ist, Quellen zu deklarieren. Zwischenobjektdateien werden mithilfe impliziter Regeln generiert. Folglich Makefilefunktioniert 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 ändern LINK.onur deswegen. Wenn Sie C-Code kompilieren möchten, müssen Sie den LINK.oWert nicht erzwingen .

Natürlich können Sie auch Ihre Kompilierungsflags mit Variable CFLAGShinzufügen und Ihre Bibliotheken hinzufügen LDLIBS. Zum Beispiel:

CFLAGS = -Wall
LDLIBS = -lm

Eine Randbemerkung: Wenn Sie externe Bibliotheken verwenden müssen, schlage ich vor , zu pkg-config zu verwenden , um richtig eingestellt CFLAGSund LDLIBS:

CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)

Der aufmerksame Leser wird feststellen, dass dies Makefilenicht ordnungsgemäß wiederhergestellt wird, wenn ein Header geändert wird. Fügen Sie diese Zeilen hinzu, um das Problem zu beheben:

override CPPFLAGS += -MMD
include $(wildcard *.d)

-MMDErmö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 cleanund distcleanRegeln:

clean:
    $(RM) *.o *.d

distclean: clean
    $(RM) tool

Beachten Sie, $(RM)ist das Äquivalent von rm -f, aber es ist eine gute Praxis, nicht anzurufenrm direkt .

Die allRegel wird auch geschätzt. Um zu arbeiten, sollte es die erste Regel Ihrer Datei sein:

all: tool

Sie können auch eine installRegel hinzufügen :

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIRist 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 ändern PREFIX, um Ihr Paket in zu installieren /usr.

Ein letztes Wort: Platzieren Sie keine Quelldateien in Unterverzeichnissen. Wenn Sie das wirklich wollen, behalten Sie dies Makefileim Stammverzeichnis und verwenden Sie vollständige Pfade, um Ihre Dateien zu identifizieren (dh subdir/file.o).

Zusammenfassend sollte Ihr vollständiges Makefile folgendermaßen aussehen:

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)

all: tool
tool: tool.o file1.o file2.o
clean:
    $(RM) *.o *.d
distclean: clean
    $(RM) tool
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin
Jérôme Pouiller
quelle
Gegen Ende: Sollte es keine Leerzeilen zwischen den Regeln geben? John Knoellers Antwort behauptete das.
Peter Mortensen
Keine der makemir bekannten Implementierungen (GNU Make und BSD Make) benötigt Leerzeilen zwischen Regeln. Es gibt jedoch Unmengen von makeImplementierungen mit ihren eigenen Fehlern.
Jérôme Pouiller
5

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.

appname := myapp

CXX := g++
CXXFLAGS := -Wall -g

srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects  := $(patsubst %.cpp, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend

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.

VectorVortec
quelle