Guten Abend,
ich versuche gerade ein Makefile zu schreiben. Was mir allerdings gerade
nicht gelingen will, ist den Compiler dazu zu bringen, alle
Quelltext-Verzeichnisse nach den Header-Dateien zu durchsuchen. Für den
Anfang probiere ich erst einmal, nur einen kleinen Teil zu bauen. Er
meldet allerdings immer, daß eine Header-Datei nicht auffindbar sei.
Das Makefile ist angehängt, allerdings hängt es schon an der einen
Zeile:
1
$(CC) -I"../common/src_unity" $c $< -o $@
Im Verzeichnis "../common/src_unity/" befindet sich eine Header-Datei.
Im Verzeichnis "../" relativ zum Makefile befindet sich die zu
kompilierende *.c-Datei. Diese wird von Make gefunden, der Build schlägt
allerdings dennoch fehl, da der GCC nicht die Headerdatei findet.
Ich nehme an, daß relative Pfade für den GCC unter Windows anders
dargestellt werden, als ich es versuche.
Kann mir jemand einen guten Tipp geben?
> Ich nehme an, daß relative Pfade für den GCC unter Windows anders> dargestellt werden, als ich es versuche.
Nee, Windows unterstützt schon lange "/" als Trennzeichen.
Das ist eigentlich auch keine Frage für make sondern für den gcc. Wenn
man so sucht, findet man auch was:
https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html
Demnach beziehen sich die relativen Pfade hinter -I auf das aktuelle
Verzeichnis aus dem heraus der gcc aufgerufen wird. Wenn du in dei9nem
Makefile keine cd Befehle drin hast, müsste das ja das Verzeichnis sein,
wo du gerade stehst, wenn du den make Befehl ausführst.
Auf der Seite steht auch, wie man das debuggen kann.
Dein Makefile ist ... naja, sagen wir mal, äußerst suboptimal.
Ein Makefile baut sich so auf:
1
ausgangsdatei: eingangsdatei(en)
2
befehl(e)
Das hast du nicht und das ist schlecht.
Dein Compiler macht aus einer ".c"-Datei genau eine ".o"-Datei.
Also:
1
%.o: %.c
2
$(CC) $(CFLAGS) -c -o $@ $<
Der Linker macht aus allen ".o"-Dateien genau ein Binary. Also:
1
$(OUT_EXE): $(OBJS)
2
$(CC) $(CFLAGS) $(LFLAGS) -o $@ $^
Die Unterscheidung zwischen "den Namen gibt's nur einmal" und "den Namen
gibt's mehrmals" ist großes pfui. Gib einfach an, wo die Objektdateien
liegen sollen, Make sucht sich die passenden Quelldateien anhand der
Regeln raus. Also:
Die Includes selbst trägst du als Variable ein und nutzt diese. Pfade
sind relativ zu dem Ort, wo du dich befindest, wenn du den Compiler
aufrufst (d.h. bei einem einfachen Makefile ausgehend von Ort des
Makefiles). Also:
S. R. schrieb:> Die Unterscheidung zwischen "den Namen gibt's nur einmal" und "den Namen> gibt's mehrmals" ist großes pfui. Gib einfach an, wo die Objektdateien> liegen sollen, Make sucht sich die passenden Quelldateien anhand der> Regeln raus. Also:OBJS := main.o font/font3x6.o font/font5x8.o> glcd/glcd_ks0108.o
Ja, das wäre deutlich besser. Alternativ rekursives Make, also in jedem
Verzeichnis ein eigenes Makefile.
Dann kann man sich auch VPATH und CPATH sparen. Die tragen auch nicht
unbedingt zur Übersicht bei.
Rolf M. schrieb:> Ja, das wäre deutlich besser. Alternativ rekursives Make, also in jedem> Verzeichnis ein eigenes Makefile.
Da gab es ein schönes Paper zu: "recursive make considered harmful". Es
gibt sicher Mittel und Wege, Make im Allgemeinen rekursiv zuverlässig zu
bekommen (für einfache Fälle geht es), aber in der Regel macht man sich
damit parallele Builds kaputt.
Besser ist es, in jedem Verzeichnis ein kleines Make-Schnipsel zu haben,
welches man dann in das eigentliche Makefile inkludiert. So
funktionieren z.B. der Linux-Kernel und Android.
S. R. schrieb:> Da gab es ein schönes Paper zu: "recursive make considered harmful".
Das weiß ich. Trotzdem finde ich es irgendwie "natürlicher" als alles in
ein Makefile zu stopfen.
Generell sollte man auch in Betracht ziehen, z.B. gleich CMake zu
verwenden.
Rolf M. schrieb:> Generell sollte man auch in Betracht ziehen,> z.B. gleich CMake zu verwenden.
Ja, wobei mir CMake extrem unsympathisch ist. Ich beschreibe dadrin
ausschließlich, was ich haben will, und CMake weiß dann selbst, wie
alles zusammenhängt, was wann und auf welche Weise zu tun ist.
Mit Make kann ich ohne zusätzlichen Aufwand auch VHDL, LaTeX oder
Android-Apps bauen (wenn ich die Schrittfolge verstanden habe), mit
CMake ist alles jenseits von C/C++ (oder mit komplizierteren
Bauanweisungen) schwierig.
Für den TO wäre CMake allerding die bessere Lösung, als sich Makefiles
beizubringen. Wobei der sich irgendwie nicht mehr meldet. :-(
S. R. schrieb:> Wobei der sich irgendwie nicht mehr meldet. :-(
Jaaaa.... wenn etwas partout nicht klappen will, macht man erst einmal
etwas Anderes, um Abstand zu gewinnen. Daß die Anfrage zu Make dann von
Compiler & IDE in µC & Elektronik verschoben wurde, war definitiv nicht
der Grund, das Thema aus den Augenwinkeln verloren zu haben.
Das Makefile muß überarbeitet werden - keine Frage. Das
Überverzeichnis-Problem ist erst der Anfang.
Was ich momentan immer noch nicht gefunden habe: Wie sieht die Syntax
bei Überverzeichnissen aus?
Das hier scheint ja nicht zu klappen:
S. R. schrieb:> Der Linker macht aus allen ".o"-Dateien genau ein Binary.> Also:$(OUT_EXE): $(OBJS)> $(CC) $(CFLAGS) $(LFLAGS) -o $@ $^
Das hier scheint auch nicht zu klappen. Die Liste der Objekte scheint
nicht expandiert zu werden.
Hier gilt die schöne alte Redewendung "you are barking up the wrong
tree".
Du suchst an der falschen Stelle.
1
build:$(CFILES)$(OFILES)
2
$(CC)-I"../common/src_unity"$c$<-o$@
Hier steckt des Pudels Kern. Aber nicht in der zweiten, sondern schon in
der ersten Zeile. Da sagst Du, daß build (das übrigens ein .PHONY target
sein sollte, sonst wunderst Du dich eines Tages, dass dein Build nicht
mehr funktioniert, bloß weil Du ein "build"-Verzeichnis angelegt hast)
von allen .c Dateien und von allen .o Dateien abhängig sein soll.
Make guckt sich alle .c's an (und stellt wahrscheinlich fest, dass da
neue dabei sind) und anschliessend alle .o's. Bei letzteren
registriert es dann, dass die gar nicht da sind und versucht sie zu
bauen.
Dummerweise hast Du nirgends gesagt, wie das zu bewerkstelligen wäre.
Make ist aber nett und bringt einen Satz von vordefinierten implicit
rules mit und weiss deswegen, wie es den C-Compiler aufzurufen hat.
Leider weiss diese Regel aber nichts von deinem Custom-Includepfad,
weshalb die Includes nicht gefunden werden.
Die zweite Zeile wird nie benutzt, weil der erste Teil schon
fehlschlägt.
Merke: Fehlermeldungen genau lesen (und mitposten, damit wir nicht
gemeinsam falsch bellen).
Danke für die Anmerkungen! Das war der entscheidende Hinweis.
Ich habe das Makefile mal überarbeitet. Prinzipiell macht es jetzt das,
was es soll. (Siehe Anhang - passt zur Verzeichnisstruktur in der obigen
ZIP-Datei.)
Was mich allerdings noch stört, ist dass ich die Pfade zweimal
einpflegen muss (einmal in VPATH und einmal in der Compile-Zeile).
Gibt es da noch eine Abhilfe?
Ich habe vor Jahren mal versucht, Makefiles mit "sed" unter Windows zu
nutzen. Nie wieder! Wenn sed die einzige Möglichkeit ist, das doppelte
Einpflegen von Pfaden zu vermeiden, ist letzteres das kleinere Übel.
Maker schrieb:> Okay. Ich habe jetzt das gefunden, was ich suchte.
Gut.
Das ist aber nur die halbe Miete.
Wär' doch viel cooler (und vor allem richtig), wenn auch dann neu gebaut
würde, wenn Du eins der Include-Files angefasst hast.
Maker schrieb:>> Wobei der sich irgendwie nicht mehr meldet. :-(>> Jaaaa.... wenn etwas partout nicht klappen will, macht man erst einmal> etwas Anderes, um Abstand zu gewinnen. Daß die Anfrage zu Make dann von> Compiler & IDE in µC & Elektronik verschoben wurde, war definitiv nicht> der Grund, das Thema aus den Augenwinkeln verloren zu haben.
Tschuldigung, aber ich hätte wenigstens erwartet, dass du - wenn du
schon eine Frage stellst - auch nach einer Antwort guckst. Aber du bist
ja noch da, von daher: Nix für ungut. :-)
> Das Makefile muß überarbeitet werden - keine Frage. Das> Überverzeichnis-Problem ist erst der Anfang.
Ich hab so ein bisschen das Gefühl, dass deine Projektstruktur an sich
schon komisch ist, was es dir natürlich deutlich erschwert, ein
sinnvolles Makefile zu bauen. Buildsysteme sind nunmal hässlich.
Ich hänge dir mal ein ziemlich minimales Makefile-Beispiel an, wo die
automatische Dependency-Ausgabe mit drin ist. Das heißt, dass der
Compiler während des Compilierens einer Datei die notwendigen Header
in ein Makefile-Snippet reinschreibt.
Wichtig ist die letzte Zeile in der Datei, wo sämtliche dieser
Snippets in das aktuelle Makefile mit eingebunden werden. Damit weiß
Make, von welchen Headern jede Datei abhängig ist. Das Minus vor dem
"include" sagt, dass eine fehlende Dependency-Datei kein Fehler ist
(wenn die Dependency fehlt, wurde die Datei noch nicht compiliert, d.h.
das Object fehlt ebenfalls und der Compiler muss laufen).
Tiefgründig getestet habe ich dieses spezifische Makefile nicht, und
obwohl ich vermute, dass Dependencies mit Clang genauso funktionieren
wie mit GCC, habe ich das auch nicht getestet.
Beachte, dass du nur eine solche Datei haben solltest, und zwar im
Rootverzeichnis deines Projektes, und dadrin listest du alle zu
generierenden Objekte auf. Wenn du das unbedingt splitten willst, dann
lege in die Unterverzeichnisse jeweils nur Makefile-Snippets, die nur
ihre eigenen Objekte an "OBJS" anhängen. Zusätzliche Regeln sollten da
drin nicht notwendig sein, sind aber möglich. Beachte aber, dass jedes
dieser Snippets dir das globale Buildsystem kaputtmachen kann (z.B.
mit CFLAGS spielen oder so), sei also entsprechend vorsichtig.
S. R. schrieb:> Ich hab so ein bisschen das Gefühl, dass deine Projektstruktur an sich> schon komisch ist, was es dir natürlich deutlich erschwert, ein> sinnvolles Makefile zu bauen.
Ich muß ein paar Änderungen in ein Altprojekt, das unter Atmel Studio 7
erstellt wurde, einpflegen. Tatsächlich habe ich drei Targets, was die
Sache nicht übersichtlicher macht.
S. R. schrieb:> Beachte, dass du nur eine solche Datei haben solltest, und zwar im> Rootverzeichnis deines Projektes
Geht nicht. Im Wurzelverzeichnis will Atmel Studio walten.
---
Aber treten wir von der konkreten Anforderung Atmel Studio zurück -
vielleicht ist der Vorschlag ja eine gute Idee für künftige Projekte.
Momentan habe ich die Targets in separaten Ordnern, die am Ende die
Zwischendateien (.d, .o etc.), das Executable und im Falle des Builds
mit make auch das makefile enthalten.
Wie könnte ich mit einem Makefile im Root-Verzeichnis mehrere Targets,
die unterschiedliche Compilerparameter und Quelltextdateien erfordern
umsetzen?
Maker schrieb:> Wie könnte ich mit einem Makefile im Root-Verzeichnis mehrere Targets,> die unterschiedliche Compilerparameter und Quelltextdateien erfordern> umsetzen?
Ist hier (denke ich, habs nicht DURCH gelesen) gut beschrieben:
https://www.buecherbillig.de/make-ge-packt.html
Das wurde mir mal kostenlos (und ungefragt) dazu gepackt, bzw. bei mir
entsorgt. :)
Markus F. schrieb:> Wär' doch viel cooler (und vor allem richtig), wenn auch dann neu gebaut> würde, wenn Du eins der Include-Files angefasst hast.
Der gcc hat eine Compileroption
-MMD
damit generiert er neben dem sonstigen Output auch noch für jede
Kompilationseinheit eine zugehörige .d Datei und legt sie neben die .o
Datei. Diese .d Datei ist ein gültiger Makefile-Syntax-Schnipsel und
kann ins Makefile includet werden und sie bewirkt daß jede .o Datei
jetzt zusäzlich auch noch von allen includes (und deren includes, usw.)
abhängt so daß jede Änderung in irgendeinem Header dafür sorgt daß genau
die Dateien nochmal kompiliert werden die davon betroffen sind.
Also -MMD an die Compileroptionen anfügen und unten im Makefile
sinngemäß so einen Schnipsel:
1
# try to include any compiler generated dependency makefile snippet *.d
2
# that might exist in BUILDDIR (but don't complain if it doesn't yet).
3
# This will effectively make all object files depend on all header files
4
# that were involved in creating them, so any change in a header file will
5
# result in rebuilding all object files that depend on them.
6
DEPS = $(patsubst %.o,%.d,$(OBJS))
7
-include $(DEPS)
Das Minus vor dem include bewirkt daß es keinen Fehler wirft wenn beim
allerersten mal oder nach einem clean noch keine .d Dateien existieren.