Makefile Grundlagen

Gast #5738239
Lesenswert?

Also, der Einstieg ist sehr simpel. Angenommen, du speicherst obiges 
Programm unter foo.c ab und willst am Ende ein Programm names foo haben, 
dann reicht schon dieses Makefile:
1
foo: foo.o
Make weiß von selbst, dass es foo.c in foo.o compilieren muss und wie 
das geht.

Aber von hier an wird es schnell kompliziert. Der größte Fallstrick ist, 
dass make immer das erste Target baut, das es im  Makefile vorfindet. 
Daher empfehle ich als erstes immer ein Target all (der Name ist aber 
wurscht):
1
all: foo
2

3
foo: foo.o
Dann hat man für alle weiteren Targets darunter freie Hand.
Gast #5738250
Lesenswert?

Solltest du dann jetzt dein Programm in mehrere Dateien aufteilen, 
foo1.c, foo2.c, bar.c..., dann schaut das so aus:
1
all: foo
2

3
foo: foo1.o foo2.o bar.o
Nächstes Problem ist, dass du ja auch Header-Dateien haben wirst, und du 
alle C-Dateien neu kompilieren willst, wenn sich ein Header ändert. Das 
kann man dann manuell verwalten und schaut so aus:
1
all: foo
2

3
foo: foo1.o foo2.o bar.o
4

5
foo1.o: foo1.h bar.h
6
foo2.o: foo2.h bar.h
7
bar.o: bar.h
Diese Zeilen bedeuten: Wenn sich eine der Dateien rechts vom : 
verändert, dann muss die Datei links vom : neu gebaut werden.

Diese Abhängigkeiten können auch automatisch verwaltet werden. Aber das 
ist ein Expertenthema, auf das ich jetzt nicht eingehen möchte.
Gast #5738358
Lesenswert?

Nein, am Namen "all" ist nichts besonderes, du könntest es auch 
"frotzldiblubb" nennen. Wichtig ist nur, dass es die erste Regel im 
Makefile ist. An der Regel selbst ist auch nichts besonderes, außer dass 
sie die erste ist.

Man kann unterhalb einer Regel, eingerückt durch ein TAB (nicht 
Leerzeichen) die Anweisung hinschreiben, die ausgeführt werden soll. Im 
Fall von all: foo steht aber nichts dergleichen darunter, also wird auch 
nichts weiter gemacht. Make versucht zwar "all" aus "foo" zu bauen, aber 
da es keine Anweisungen gibt, passiert auch nichts.

Wie jede andere Regel auch, hat sie aber einen wichtigen Nebeneffekt: 
Make schaut nämlich, bevor es versucht "all" zu bauen, ob nicht die 
Dateien rechts vom Doppelpunkt, "foo", veraltet ist. Und wenn dem so 
ist, versucht es vorher, diese Datei zu bauen. Das führt dann dazu, 
dass Compiler und Linker für "foo" aufgerufen werden.
#5738413
Lesenswert?

Christoph M. schrieb:
> So richtig klar, was "all" jetzt bedeutet, ist mir nicht.
> Bedeutet "all" das nachher das exe-file den Namen von "all" bekommt?

"all" ist in diesem Fall ein sogenanntes "phony target" (phony = 
unecht). Da heißt, so wie es verwendet wird, wird keine Datei mit dem 
Namen "all" gebaut. Es gibt dem Target "foo" nur einen weiteren Namen.

Manchmal kann es vorkommen, dass Make nicht alleine erkennt, dass ein 
Target ein unechtes Ziel ist. Dann kann man den meisten Makes auch 
deutlich sagen:
1
.PHONY: all
2
all: foo
3

4
foo: foo1.o foo2.o bar.o

Das .PHONY: in dem Beispiel ist ein in vielen Makes eingebautes "special 
target". Sieht fast aus wie ein Target, steuert aber interne 
Make-Abläufe.

Wenn du sehen willst was Make sich so "denkt" und welche eingebauten 
Regeln es kennt, dann ruf dein Make mal mit der Option "-d" auf. Da wird 
seitenweise Text kommen.
#5738439
Lesenswert?

Hannes J. schrieb:
> Da heißt, so wie es verwendet wird, wird keine Datei mit dem
> Namen "all" gebaut. Es gibt dem Target "foo" nur einen weiteren Namen.

Falsch (oder zumindest nicht ganz richtig).

Daß Targets Dateien erzeugen ist gar nicht notwendig, deswegen muß nicht 
jedes Target, daß keine Datei gleichen Namens erzeugt, gleich .PHONY 
sein.

Das .PHONY schreibt man deswegen hin, um das zugehörige Recipe auch dann 
auszuführen, wenn es eine aktuelle Datei gäbe, die wie das Target heißt.

Sonst würde ein zufällig rumliegendes "all" u.U. bewirken, daß dein 
Makefile gar nicht "anspringt".
#5738469
Lesenswert?

Markus F. schrieb:
> Daß Targets Dateien erzeugen ist gar nicht notwendig, deswegen muß nicht
> jedes Target, daß keine Datei gleichen Namens erzeugt, gleich .PHONY
> sein.

Allerdings stehen Targets mit Files schon in einer sehr engen Beziehung: 
make prüft vor dem Ausführen jeder Regel, ob es eine Datei mit dem Namen 
des Targets gibt und ob die neuer ist als alle Abhängigkeiten. Nur wenn 
das gilt, wird die Regel auch ausgeführt.

> Das .PHONY schreibt man deswegen hin, um das zugehörige Recipe auch dann
> auszuführen, wenn es eine aktuelle Datei gäbe, die wie das Target heißt.
>
> Sonst würde ein zufällig rumliegendes "all" u.U. bewirken, daß dein
> Makefile gar nicht "anspringt".

Eben. Deshalb ist das .PHONY schon sinnvoll. Vor allem wird all nicht 
das einzige phony-Target bleiben. In einem komplexen Makefile kann es 
sehr viele davon geben.
Beitrag #5738487 wurde von einem Moderator gelöscht.
Beitrag #5738660 wurde von einem Moderator gelöscht.
#5738697
Lesenswert?

Christoph M. schrieb:
> Interessanterweise habe ich mich beim Makefile oben vertippt.
>
> In meinem Makefile steht:
> hallo: hallo.o
> Das beste ist, es funktioniert einwandfrei. Die Datei hallo.c wird zur
> ausführbaren Datei "hallo" compiliert.

Wenn du "make hallo" eingibst, geht es sogar ganz ohne Makefile.

Markus F. schrieb im Beitrag #5738660:
> Ich empfehle ein Duden-Studium.

Dann gib doch mal "daß" auf http://www.duden.de ein und schau mal, ob es 
ein solches Wort dort gibt.
Beitrag #5738718 wurde von einem Moderator gelöscht.
OP #5738730
Lesenswert?

Mittlerweile habe ich ein extrem ausgefeiltes Softwarepaket erstellt ( 
die Dateien im Anhang ) ;-)

Jetzt stellt sich natürlich die Frage, wie ich das Makefile dazu bringe, 
dass es alle Dateien kompiliert und mit "make clean" auch wieder alle 
löscht.

Es erscheint mir umständlich, wenn ich für das löschen alle Namen von 
Hand wieder eintippen muss.

Wer weiß Rat?
Angehängte Dateien:
#5738743
Lesenswert?

wenn Du das, was Du schon über Makefiles gelernt hast, richtig 
anwendest, könntest Du da selbst draufkommen.

Alles was Du braucht ist ein Target (ja, es darf ruhig .PHONY sein) und 
ein Recipe - das sind das/die Kommandos, die make ausführen soll, um das 
Target zu bauen.

target: abhängigkeit
<TAB> Kommandos

Damit man nicht immer wieder dieselben Listen von Abhängigkeiten angeben 
muß, kann make auch mit Variablen umgehen:

DATEILISTE=dateia dateib dateic

Wenn Du also eine Liste

OBJS=main.o eins.o zwei.o

hast, kannst Du die Liste überall dort verwenden, wo Du sonst einzelne 
Namen verwenden würdest:

<TAB>rm $(OBJS)
#5738756
Lesenswert?

Markus F. schrieb:
> Wenn Du also eine Liste
>
> OBJS=main.o eins.o zwei.o

Ich würde

OBJS:=main.o eins.o zwei.o

vorschlagen. Spielt zwar an dieser Stelle keine große Rolle, aber man 
sollte sich des Unterschieds bewusst sein.

Markus F. schrieb im Beitrag #5738718:
> Rolf M. schrieb:
>> Markus F. schrieb:
>>> Ich empfehle ein Duden-Studium.
>>
>> Dann gib doch mal "daß" auf http://www.duden.de ein und schau mal, ob es
>> ein solches Wort dort gibt.
>
> Mein Duden ist schon - genau wie ich - ein wenig älter.

Also nicht mehr aktuell (dein Duden zumindest - Du vielleicht schon noch 
;-)
Übrigens: Die zweite Verwendung im bemängelten Posting ist auch nach 
alter Rechtschreibung falsch.
Beitrag #5738761 wurde von einem Moderator gelöscht.
OP #5738767
Lesenswert?

Autor: Markus F. (mfro)
>Alles was Du braucht ist ein Target (ja, es darf ruhig .PHONY sein) und
>ein Recipe - das sind das/die Kommandos, die make ausführen soll, um das
>Target zu bauen.

Meiner Meinung nach ist es beim lernen extrem hilfreich, wenn man die 
englischen Begriffe durch deutsche ersetzt ( auch wenn ich das 
verhandlungssicher spreche ).
Warum? Weil man dann den Befehlssyntax ( englische Befehle ), von den 
erfundenen deutschen Wörtern besser trennen kann.

Hier mein neues Makefile:
1
alles: machMalDrucker machMalHallo
2
  g++ drucker.o hallo.o -o halloExe
3

4
machMalDrucker: drucker.c
5
  gcc -c drucker.c
6

7
machMalHallo: hallo.c
8
  gcc -c hallo.c
9

10
clean:
11
  rm -rf *.o halloExe

inspiriert durch
https://www.youtube.com/watch?v=aw9wHbFTnAQ
#5738778
Lesenswert?

Das Makefile ist weniger ideal. Du verwendest die Targets hier nicht in 
dem Sinne, wie es eigentlich gedacht ist. Es gibt bestimmte Targets wie 
all und clean, die PHONY sein sollen, aber du hast quasi ausschließlich 
PHONY-Targets, was am Sinn von make vorbei geht. Zunächst einmal sollte 
dein zu erstellenedes Executable, also halloExe ein Target sein, und 
deine .o-Files sollten auch alle Targets sein. Das hast du mit den 
Targets machMalDrucker und machMalHallo quasi umgangen. Warum?
Also eher so:
1
halloExe: drucker.o hallo.o
2
  g++ drucker.o hallo.o -o halloExe
3

4
drucker.o: drucker.c
5
  gcc -c drucker.c
6

7
hallo.o: hallo.c
8
gcc -c hallo.c
9

10
.PHONY: clean
11
clean:
12
   rm -rf *.o halloExe

Dein "alles" kannst du davor noch hinzufügen:
1
.PHONY: alles
2
alles: halloExe


Das kann man dann nachher noch mit Variablen garnieren und mit 
generischen Regeln:
1
ZIEL=halloExe
2
OBJEKTE=drucker.o hallo.o
3

4
$(ZIEL): $(OBJEKTE)
5
  g++ $^ -o $@
6

7
%.o: %.c
8
  gcc -c $< -o $@
9

10
.PHONY: clean
11
clean:
12
   rm -rf $(OBJEKTE) $(ZIEL)

$@ wird durch den Namen des Ziels ersetzt, $< durch den Namen der ersten 
Abhängigkeit, $^ durch die Namen aller Abhängigkeiten. %.o : %.c sagt, 
dass für jedes Target, das auf .o endet, eine gleichnamige .c-Datei 
gesucht und als Abhängigkeit verwendet werden soll.
Das gilt alles für GNU make. Wie weit das auf andere makes übertragbar 
ist, weiß ich nicht.
OP #5738846
Lesenswert?

RolfM:
>Das hast du mit den
>Targets machMalDrucker und machMalHallo quasi umgangen. Warum?

Ähm, ich weiß nicht.
Ich habe es so verstanden, dass hier
1
hallo.o: hallo.c
2
gcc -c hallo.c

hallo.o ein Name ist und sinnvollerweise durch einen deutschen Namen 
ersetzt wird, damit man sieht, dass es sich hier nicht um das o-file 
handelt.

Im nächsten Schritt befindet sich das Source-File im 
examples-Verzeichnis.
Jetzt allerdings leider mit dem Ergebnis, dass der Compiler die includes 
nicht mehr findet.

Wie teilt man die richtigen Pfade dem Makefile mit?
Angehängte Dateien:
#5738866
Lesenswert?

Christoph M. schrieb:
> RolfM:
>>Das hast du mit den
>>Targets machMalDrucker und machMalHallo quasi umgangen. Warum?
>
> Ähm, ich weiß nicht.
> Ich habe es so verstanden, dass hier
> hallo.o: hallo.c
> gcc -c hallo.c
>
> hallo.o ein Name ist und sinnvollerweise durch einen deutschen Namen
> ersetzt wird, damit man sieht, dass es sich hier nicht um das o-file
> handelt.

Aber genau um das sollte es sich handeln. Die Grundidee von make ist, 
dass nur die Sachen neu gebaut werden, die sich seit dem letzten mal 
geändert haben. Dazu sucht er nach einer Datei, deren Namen dem Target 
entspricht, und wenn eine solche nicht existiert oder älter ist als 
mindestens eine der Abhängigkeiten, wird das Kommando zu dieser Regel 
ausgeführt. Und das wird für jede der Abhängigkeiten wiederum rekursiv 
weitergeführt.
Das heißt, es sollte so sein: Wenn du make aufrufst, wird dein Programm 
erstellt. Machst du das nochmal, ohne vorher was zu ändern, erkennt 
make, dass alles gleich geblieben ist, und es wird nichts mehr gemacht. 
Änderst du jetzt z.B. hallo.c, wird make danach nur hallo.o neu erzeugen 
und daraus das Executable, aber drucker.o wird nicht neu gebaut, da sich 
dort nichts geändert hat.
Wenn nun dein Target nicht dem Dateinamen entspricht, funktioniert 
dieser Mechanismus nicht mehr.
OP #5738918
Lesenswert?

Rolf M. (rmagnus)
>Wenn nun dein Target nicht dem Dateinamen entspricht, funktioniert
>dieser Mechanismus nicht mehr.

Ah, danke. Jetzt habe ich's kapiert.
1
halloExe: drucker.o hallo.o
ist besser, weil hallo.o erzeugt wird.

im anderen Fall
1
alles: machMalDrucker machMalHallo
nützt das nichts, weil keine machMalDrucker Datei produziert wird.
Das Makefile funktioniert erst mal, aber es wird jedes mal alles gebaut 
und es tritt kein Einspareffekt auf.

Jetzt die Frage zum "SchoenWetterProjekt": Wie sollte das Makefile 
aussehen, damit die Include-Datei gefunden wird?
#5739077
Lesenswert?

Christoph M. schrieb:
> Jetzt die Frage zum "SchoenWetterProjekt": Wie sollte das Makefile
> aussehen, damit die Include-Datei gefunden wird?

Es gibt eine Option die man dem gcc mitgeben kann dann erzeugt er für 
jede kompilierte c-Datei ein Makefile-schnipsel das die Abhängigkeiten 
von den Headern ausdrückt, das kann man in sein Makefile inkludieren.

Meine Makefiles enden alle so:
1
#####################
2
## Advanced Voodoo ##
3
#####################
4

5
# try to include any compiler generated dependency makefile snippet *.d
6
# that might exist in BUILDDIR (but don't complain if it doesn't yet).
7
DEPS = $(addprefix $(BUILDDIR),$(patsubst %.c,%.d,$(filter %.c,$(SRCS))))
8
-include $(DEPS)
9

10
# make the object files also depend on the makefile itself
11
$(OBJS): Makefile

dafür muß der compiler mit dem -MMD Schalter aufgerufen werden, dann 
entstehen diese *.d Dateien. Spätestens beim nächsten make Aufruf werden 
die dann ebenfalls berücksichtigt.

Das obige ist speziell auf mein Makefile zugeschnitten, ich habe eine 
Variable BUILDDIR die den Ordner bezeichnet in dem alle binaries gebaut 
werden (out of tree build), ich häng mal das ganze Makefile an.
Angehängte Dateien:
#5742680
Lesenswert?

Christoph M. schrieb:
>> Also nur ein main file.
>
> Für nur ein File bringst nichts. Es ist für viele Files und den Fall,
> dass man nur eines ändert aber nicht alle schon kompilierten neu
> kompilieren will.
> Das kann bei großen Projekten schon mal mehrere Minuten gehen.

Die sind dann eher noch klein. Bei wirklich großen Projekten kann das 
auch mal nen halben Tag dauern.
#5743731
Lesenswert?

W. schrieb:
> Bei Ubuntu heißt das Paket "9base".
1
9base is a port of following original Plan 9 userland tools to Unix:
2
 awk, basename, bc, cat, cleanname, date, dc, echo, grep, mk, rc, sed, seq,
3
 sleep, sort, strings, tee, test, touch, tr, uniq, and yacc.

Was passiert wenn ich das installiere? Zerschießt mir das mein System 
und jedes der oben genannten Standard-Tools hat von dann an eine subtil 
andere Funktion und jedes zweite Shellscript fällt auf die Schnauze? 
Wenn das nämlich nur im Kombipack mit so ner Streubombe erhältlich ist 
verbietet sich der praktische Einsatz wohl leider.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren