Forum: Mikrocontroller und Digitale Elektronik makefile: Relativer Pfad für Includes


von Maker (Gast)


Angehängte Dateien:

Lesenswert?

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?

von Stefan F. (Gast)


Lesenswert?

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

von S. R. (svenska)


Lesenswert?

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:
1
OBJS := main.o font/font3x6.o font/font5x8.o glcd/glcd_ks0108.o

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:
1
INCS := src/include src/glcd/include src/font/include
2
CFLAGS += $(addprefix -I,$(INCS))
Das fügt für jeden Pfad in INCS ein "-Ipfad" zu den CFLAGS hinzu. Wenn 
du das nicht willst, musst du das etwas anders lösen. :-)

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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. :-(

: Bearbeitet durch User
von Maker (Gast)


Lesenswert?

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:
1
$(CC) -I"../src_projekt1" $c $< -o $@

Die Verzeichnisstruktur sieht so aus:
1
Projektpfad
2
 | 
3
 +-+ src_projekt0
4
 | | a.c
5
 | | a.h
6
 +- src_projekt1
7
 | | b.c
8
 | | b.h
9
 +- src_projekt2
10
 +-+ target0
11
 | | makefile
12
 | | target0.exe <- irgendwann...
13
 +- target1
14
 | main.c

von Maker (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

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

von Maker (Gast)


Angehängte Dateien:

Lesenswert?

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?

von Maker (Gast)


Lesenswert?

Da für Makefiles offensichtlich die Browser-Vorschau nicht funktioniert, 
hier noch einmal als Text:
1
# Makefile fuer den Test-Build fuer eine Windows- oder Linux-Plattform
2
3
4
# Erster Test Makefile
5
6
# Fuer .o-File-Sourcen: Dateien in PROJECT gehen vor gleichnamige Dateien in COMMON
7
VPATH = \
8
..\
9
../src0\
10
11
12
# Dateien, bei denen es auf den Pfad ankommt, weil die Sourcen
13
# mehrmals existieren
14
CFILES = \
15
16
17
18
OFILES = main.o\
19
  a.o\
20
21
22
23
24
25
# Unterscheidung Windows/Linux
26
ifdef SystemRoot
27
  # Build auf Windows-System fuer Windows
28
  
29
  CC=gcc
30
  RM=del
31
32
  #CFLAGS = -g -pg -Wall -Wextra -Wpedantic -std=c99\
33
  #-Wsign-conversion -DGLCD_FRAMEBUFFER -DPLATFORM_PC 
34
  CFLAGS = 
35
  
36
  # Flags, um SDL2 korrekt zuzulinken. Die Reihenfolge ist wichtig.
37
  # LFLAGS = -lmingw32 -lSDL2main -llibSDL2  
38
  LFLAGS = 
39
  
40
  # Unter Windows darf das "ECHO"-Kommando keine Anfuehrungszeichen haben.
41
  ECHOQUOTE = 
42
  
43
  
44
  OUT_EXE = buildtest.exe
45
  
46
else
47
  # Build auf Linux-System fuer Linux
48
  
49
  #...
50
  
51
endif
52
53
54
55
56
$(OUT_EXE): $(OFILES)
57
  $(CC) $(CFLAGS) $(LFLAGS) -o $@ $^
58
59
60
%.o: %.c
61
  @echo $(value CC)
62
  @echo $(value CFLAGS)
63
  $(CC) -I"../src0" $(CFLAGS) -c -o $@ $<
64
    
65
    
66
# build0: $(CFILES) $(OFILES)
67
#  $(CC) $< $(OFILES) -o $(CFLAGS) -DUNITTEST_ENABLED=0 $(LFLAGS) -o $(OUT_EXE) $(CFILES) 
68
69
#build: $(CFILES) $(OFILES)
70
#  $(CC) -I"../src0" $c $< -o $@
71
   
72
  
73
clean:
74
  $(RM) *.o *.d
75
  $(RM) $(OUT_EXE)
76
  
77
rebuild: clean ${OUT_EXE}
78
79
80
run: $(OUT_EXE)
81
  ./${OUT_EXE}
82
83
84
FORCE:

von Markus F. (mfro)


Lesenswert?

Maker schrieb:
> Gibt es da noch eine Abhilfe?

Du solltest das hier:

https://www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html

Lesen, verstehen und befolgen.

Abhängigkeiten pflegt man am besten gar nicht. Besser man lässt pflegen.

von Maker (Gast)


Lesenswert?

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.

von Maker (Gast)


Lesenswert?

Ist sed die einzige Lösung?

von Maker (Gast)


Angehängte Dateien:

Lesenswert?

Okay. Ich habe jetzt das gefunden, was ich suchte.

von Markus F. (mfro)


Lesenswert?

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.

von S. R. (svenska)


Angehängte Dateien:

Lesenswert?

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.

von Maker (Gast)


Lesenswert?

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?

von Teo D. (teoderix)


Lesenswert?

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

von Bernd K. (prof7bit)


Lesenswert?

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.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.