Tipps für makefiles gesucht



  • Hallo,
    ich bin auf den Geschmack von make-Files gekommen.
    Aber ich habe das Gefühl, es noch nicht wirklich sinnvoll zu verwenden. Es funktioniert zwar wunderbar aber bei größeren Projekte verliert man die Übersicht und die Wartbarkeit leidet ziemlich. Deshalb würde ich mich freuen, hier paar Tipps zu bekommen.

    Ich habe in diesem Forum bereits den Thread über allgemeine Makefiles gefunden. Das finde ich sehr interessant aber man verliert damit den Vorteil von make, dass es nach der Aktualität von Abhängigkeiten schaut und einen Teil nur neu kompaliert, wenn sich was verändert hat.
    Außerdem werden einfach alle Programme gelinkt, die im Ordner liegen, was nicht immer gewünscht ist.

    Deshalb will ich makefiles spezifisch für meine Projekte machen.

    Mein momentanter Aufbau (habe versucht mit Kommentaren bisschen zu erklären warum ich was mache):

    # Compiler spezifische
    CC=g++
    CFLAGS=-O2 -Wall -pedentic
    
    # Linker spezifisches
    LINK=g++
    LFLAGS=
    
    # Sonstige Programmaufrufe
    RM=/bin/rm -f
    MKDIR=/bin/mkdir -p
    
    # Der Name des Programms das rauskommen soll
    MAIN = ./TestProgramm
    # Der Ordner in den die Kompilate der einzelnen cpp-Dateien kommen
    DIR_BIN = ./bin
    
    # Alle Ordner in denen die Dateien liegen
    DIR_FOLDER_ONE = ./outerfolder
    DIR_FOLDER_TWO = $(DIR_FOLDER_ONE)/innerfolder
    
    # Sonstiges
    OBJECT_SUFFIX = o
    
    # alle Dateien die das Programm ausmachen
    OBJS = FileOne FileTwo \
    	FileThree
    
    # da aus Faulheit bei OBJS alles weggelassen wurde, was bei jedem hingehoert,
    # wird nun an alle Objekt-Files die Endung .o angehaengt
    # und vorne kommt der bin-Ordner Name, damit die Kompilate sauber aufgeraeumt sind
    override OBJS := $(addsuffix .$(OBJECT_SUFFIX), $(addprefix $(DIR_BIN)/, $(OBJS)))
    
    # ------- Start: Bei allen Projekte gleich -------
    all: $(DIR_BIN) $(MAIN)
    
    $(DIR_BIN):
    	@echo Den Ordner $(DIR_BIN) erstellen.
    	@$(MKDIR) $(DIR_BIN)
    
    $(MAIN): $(OBJS)
    	@echo Kompilate zu einem Programm linken.
    	@$(LINK) -o $@ $(LFLAGS) $(OBJS)
    	@echo Kompilierung abgeschlossen.
    	@echo Beispielsaufruf: $@
    # ------- Ende: Bei allen Projekte gleich -------
    
    # jetzt muessen fuer alle Objekt-Dateien in OBJS Regeln zur Kompilierung angegeben werden
    
    $(DIR_BIN)/FileOne.$(OBJECT_SUFFIX): $(DIR_FOLDER_ONE)/FileOne.cpp $(DIR_FOLDER_ONE)/FileOne.h $(DIR_FOLDER_ONE)/DependencyOne.h $(DIR_FOLDER_TWO)/DependencyTwo.h
    	@echo '>>>' Kompilierung von $(*F). '<<<'
    	@$(CC) $(CFLAGS) -c $< -o $@
    
    $(DIR_BIN)/FileTwo.$(OBJECT_SUFFIX): $(DIR_FOLDER_ONE)/FileTwo.cpp $(DIR_FOLDER_ONE)/FileTwo.h $(DIR_FOLDER_ONE)/DependencyOne.h
    	@echo '>>>' Kompilierung von $(*F). '<<<'
    	@$(CC) $(CFLAGS) -c $< -o $@
    
    $(DIR_BIN)/FileThree.$(OBJECT_SUFFIX): $(DIR_FOLDER_TWO)/FileThree.cpp $(DIR_FOLDER_TWO)/FileThree.h
    	@echo '>>>' Kompilierung von $(*F). '<<<'
    	@$(CC) $(CFLAGS) -c $< -o $@
    
    clean:
    	@echo Loeschen aller Erzeugten Dateien.
    	@$(RM) *.ptb *.mtb $(DIR_BIN)/*.o $(MAIN)
    

    Ich habe vor allem 3 Probleme damit.
    Problem 1:
    Alle Regeln für die Kompilierung haben die gleichen 2 Anweisungen

    @echo '>>>' Kompilierung von $(*F). '<<<'
    @$(CC) $(CFLAGS) -c $< -o $@
    

    In dem oben genannten Thread wurde dieses Problem so umgesetzt:

    %.o: %.cpp
    	$(CXX) -c $<
    

    Das kann ich (glaub ich) so nicht machen, da ich ja gerne meine Abhängigkeiten drin haben würde, die sich bei jedem Objekt unterscheiden.

    Wie kann ich es lösen, in dem ich die oberen 2 Zeilen nicht immer wiederholen muss aber trotzdem verschiedene Abhängigkeiten der einzelnen .cpp Files einbauen kann?

    Problem 2:
    Irgend wie nervt es mich, oben in OBJS ein neues File eintragen zu müssen und dann für das gleiche File unten nochmal eine Regel anzugeben.

    Mir würde es zum Beispiel besser gefallen, wenn OBJS ein zwei dimensioanles Array wäre, in dem ich an 1. Stelle den Namen des Objekt-Files schreibe (der bis jetzt in OBJS steht), an 2. Stelle den Pfad + Namen der .cpp Datei und alle Files ab der 3. Stelle sind die Abhängigkeiten.
    Dann kann mit allen ersten Einträgen so verfahren werden, wie mit OBJS bis jetzt und aus jedem Array Eintrag des "äußeren Arrays" könnte automatisch eine Regel zur Compilierung gebaut werden.
    Das ist die Theorie die ich mir vorstelle, aber ich glaube nicht, dass dies mit make geht (allein mehrdimensionale Arrys kenne ich bei make nicht).

    Habt ihr eine Idee, wie dieses Problem schöner umgesetzt werden kann?

    Problem 3:
    Bei den Abhängigkeiten muss ich ständig beachten, dass der richtige Ordner angegeben wird. Und da Abhängigkeiten meistens öfters als ein mal vorkommen heißt das, ich muss zick Mal den gleichen Ordner mit der gleichen Abhängigkeit angeben.

    Vielleicht wäre es eine Lösung, für alle Header-Files ein Macro anzulegen, so dass ich nicht mehr

    $(DIR_BIN)/FileTwo.$(OBJECT_SUFFIX): $(DIR_FOLDER_ONE)/FileTwo.cpp $(DIR_FOLDER_ONE)/FileTwo.h $(DIR_FOLDER_ONE)/DependencyOne.h
    

    schreibe, sondern:

    DEPENDENCYONE = $(DIR_FOLDER_ONE)/DependencyOne.h
    $(DIR_BIN)/FileTwo.$(OBJECT_SUFFIX): $(DIR_FOLDER_ONE)/FileTwo.cpp $(DIR_FOLDER_ONE)/FileTwo.h $(DEPENDENCYONE)
    

    So kann ich beim nächsten mal wenn ich DependencyOne.h brauche, auch nur noch $(DEPENDENCYONE) hinschreiben. Zweiter Vorteil, wenn ich die Header Datei verschiebe, zum Beispiel in einen extra "include"-Verzeichnis, muss ich nicht jede Abhängigkeite ändern, sondern nur das Macro.
    Wie würdet ihr dieses Problem angehen?

    Würde mich sehr freuen, wenn ihr mir paar Tipps geben könnt.
    Auch wenn ihr euch denkt "Was macht der Typ da?" würde ich mich freuen, wenn ihr konstruktuive Kritik abgebt und mir sagt, wie ihr grundlegend an make files ran geht.
    Danke



  • nimm CMake



  • Hambrana schrieb:

    Ich habe in diesem Forum bereits den Thread über allgemeine Makefiles gefunden. Das finde ich sehr interessant aber man verliert damit den Vorteil von make, dass es nach der Aktualität von Abhängigkeiten schaut und einen Teil nur neu kompaliert, wenn sich was verändert hat.

    Ganz falsch! Der Vorteil bleibt voll erhalten.



  • volkard schrieb:

    Hambrana schrieb:

    Ich habe in diesem Forum bereits den Thread über allgemeine Makefiles gefunden. Das finde ich sehr interessant aber man verliert damit den Vorteil von make, dass es nach der Aktualität von Abhängigkeiten schaut und einen Teil nur neu kompaliert, wenn sich was verändert hat.

    Ganz falsch! Der Vorteil bleibt voll erhalten.

    Das Makefile ist genial.
    Ich kompiliere meine ganzen Projekte (je >10kloc) nach einem Makefile, das an diesem ultimativen Makefile angelehnt ist.



  • Wie würdet ihr dieses Problem angehen?

    Dependencies manuell zu "warten" ist VIEl zu viel Aufwand und VIEL zu fehleranfällig! => Auf jeden Fall automatisch generieren lassen.

    zick Mal

    Kleiner Tip: das kommt von der Endung "zig" in "vierzig", "fünfzig", "sechzig" etc. Und wird daher "zig" und nicht "zick" geschrieben.
    Und ich glaube auch nicht dass "Mal" hier als Substantiv verwendet wird. Also kleinschreiben.



  • volkard schrieb:

    Ganz falsch! Der Vorteil bleibt voll erhalten.

    Tut mir leid für diese falsche Aussage.

    Hab mir das Makefile nochmals genauer angeschaut. Da ich .deps Dateien vorher noch nicht kannte bin ich davon ausgegangen, dass es spezielle C++-Dateien sind, die ich in meinen kleinen Projekten nicht verwende.
    Diese sind aber der Schlüssel für die Abhängigkeiten (wenn man es so betrachtet, passt ".deps" ziemlich gut).

    Man lässt sich also die Abhängigkeiten automatisch generieren (kannte die Funktion des Compilers noch nicht, aber sehr nützlich) und fügt sie mit include danach ein.

    Somit steht beispielsweise nach dem inkludieren da:

    Test.o: Test.cpp Test.h Dependency.h
    
    %.o: %.cpp
      $(CXX) $(CXXFLAGS) -c $< -o $@
    

    Heißt das, dass %.o: %.cpp nur aufgerufen wird, wenn Test.o: Test.cpp ... sagt, "hier ist was veraltet"? Bzw. verallgemeinert, wenn mehrere Regeln zutreffen, wird von oben nach unten durchgegangen und sobald eine sagt, "hier ist nichst verlatet", wird keine weitere Regel gesucht?

    Ich habe mir auch das ultimative Makefile von dir angeschaut, dass ultimator verlinkt hat. Das sieht echt gut aus. Habe mich damit auch mal gespielt. Es funktioniert automatisch mit allen Source-Files im selben Ordner. Wenn Unterordner verwendet werden, muss man SOURCES anpassen und die Dateien von Hand eintragen. Seh ich das richtig? Finde ich aber nicht schlimm. Wie oben gesagt, pauschal alles zu einem Programm linken will man ja eh nicht immer.

    Hätte noch Fragen:
    Wieso wird eine versteckte Datei .tag in der ersten ebene von "build" erstellt?
    Was bringen die Compilerflags -c und -MT. Habe die Erklärungen im Handbuch nicht ganz verstanden.

    hustbaer schrieb:

    zick Mal

    Kleiner Tip: das kommt von der Endung "zig" in "vierzig", "fünfzig", "sechzig" etc. Und wird daher "zig" und nicht "zick" geschrieben.
    Und ich glaube auch nicht dass "Mal" hier als Substantiv verwendet wird. Also kleinschreiben.

    Danke für den Hinweis von zig. Da hast du natürlich recht, werde in Zukunft drauf achten.
    Bei dem "Mal" dürftest du allerdings nicht recht haben.
    Laut Wörterverzeichnis:

    Mal: das achte Mal, zum achten Mal[e] (aber achtmal, bei besonderer Betonung auch acht Mal), dieses Mal (aber diesmal), dieses eine Mal (aber einmal, bei besonderer Betonung auch ein Mal)



  • Hambrana schrieb:

    Wie oben gesagt, pauschal alles zu einem Programm linken will man ja eh nicht immer.

    Und wenn Du die Ordner so machst, daß pro Programm ein Ordner ist? Ich kann viel einfacher Ordner verwalten als makefiles.

    Hambrana schrieb:

    Wieso wird eine versteckte Datei .tag in der ersten ebene von "build" erstellt?

    Nur, um das Verzeichnis zu erzeugen.



  • Hambrana schrieb:

    Wenn Unterordner verwendet werden, muss man SOURCES anpassen und die Dateien von Hand eintragen. Seh ich das richtig?

    Vielleicht sollte man eher das makefile umbauien, daß es auch Unterverzeichnisse scannt.
    Eigentlich sind bei mir Unterverzeichnisse dann so zusammenhängend, daß sie eine lib (bzw .a) werden.

    Hambrana schrieb:

    Was bringen die Compilerflags -c und -MT. Habe die Erklärungen im Handbuch nicht ganz verstanden.

    Steht im auchg verlinkten Handbuch zu make.


Log in to reply