Bei diesem Beispiel verstehe ich nicht ganz wozu ich die erste Zeile bei
allen Anweisungen brauche? Dient das zur übersicht?
Kann man außerdem das:
gcc -o programm main.c foo.c bar.c mit
gcc -o programm main.o foo.o bar.o gleichsetzen ??
Beides erzeugt die selbe ausführbare Datei wenn ich das richtig sehe.
Wann verwendet man jetzt was?
Die erste Zeile? Damit beginnt eine Regel. Alle Anweisungen gehören zu
einer Regel, sonst hätten sie gar keine Daseinsberechtigung. Hinter dem
Namen des Ziels und dem Doppelpunkt stehen die Abhängigkeiten. Make
überprüft für jedes Ziel (falls es schon existiert) ob eines ihrer
Abhängigkeiten neuer ist und übersetzt es in dem Fall neu. Das ist der
Hauptzweck von Make, sonst könnte man auch einfach ein Shell-Skript
verwenden.
Erkan schrieb:> Kann man außerdem das:> gcc -o programm main.c foo.c bar.c mit> gcc -o programm main.o foo.o bar.o gleichsetzen ??
Allgemein ja, denn gcc enthält selber ein paar Regeln, um aus Dateinamen
die passenden Aktionen zu ermitteln und auszuführen (in diesem Fall mit
dem C-Compiler übersetzen und dann linken).
Im konkreten Fall nein, denn die Abhängigkeit ist von den Objektdateien.
Würde man das so machen wollen, kann man die Einträge für die
Objektdateien alle entfernen. Allerdings werden dann bei jeder Änderung
alle Sourcen neu übersetzt.
Andreas B. schrieb:> Make> überprüft für jedes Ziel (falls es schon existiert) ob eines ihrer> Abhängigkeiten neuer ist und übersetzt es in dem Fall neu.
Ok, daher auch das hier:
gcc -o programm main.o foo.o bar.o
sonst wie gesagt ein shell script mit:
gcc -o programm main.c foo.c bar.c
Weil ich könnte ja genau so das hier in mein makefile schreiben:
1
prog:main.cfoo.cbar.
2
gcc-oprogmain.cfoo.cbar.c
Viel weniger Code, übersichtlicher und kompakter.
Andreas B. schrieb:> Make> überprüft für jedes Ziel (falls es schon existiert) ob eines ihrer> Abhängigkeiten neuer ist und übersetzt es in dem Fall neu.
Woher weiß make aber, dass ich eine Datei verändert habe?
Geht das in etwa so vor sich: Wenn eine datei sich verändert hat, dann
wird eben der Abschnitt
1
foo.o:foo.c
2
gcc-cfoo.c
vom makefile aufgerufen?
Dann würde es auch sinn machen.
Danke für die Erklärung!
Erkan schrieb:> Viel weniger Code, übersichtlicher und kompakter.
Und muss bei jeder Änderung das komplette Projekt neu übersetzen statt
nur die geänderte Datei. Außerdem sind Header-Dateien nicht als
Abhängigkeit erfasst, also wird es unter Umständen nicht neu übersetzt
obwohl nötig.
Erkan schrieb:> Woher weiß make aber, dass ich eine Datei verändert habe?
Es vergleicht die Zeitstempel der letzten Änderungen laut Dateisystem.
Das zitierte Makefile ist sowieso etwas suboptimal, zumindest wenn es um
GNU make geht. Da verwendet man besser Variablen um nicht alles doppelt
hinzuschreiben und dabei etwas zu vergessen.
1
prog: main.o foo.o bar.o
2
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
3
4
main.o: main.c
5
foo.o: foo.c
6
bar.o: bar.c
Das verwendet Variablen, die übersichtlich am Anfang des Makefiles
gesetzt werden können. Bei den Regeln kann man meist das Kommando
weglassen, weil GNU make eine ausführliche Liste impliziter Regeln
enthält (für .c nach .o etwa eine Regel der Form "$(CC) -c $(CPPFLAGS)
$(CFLAGS)" plus den entsprechenden Dateinamen).
Streng genommen kann man hier auch diese Regeln weglassen, da make
automatisch nach passenden möglichen Sourcen sucht wenn es ein .o bauen
muss und diese Regel implizit anwendet. Aber zu den Abhängigkeiten
gehören in der Praxis auch Header-Dateien, und die müssen explizit
angegeben werden.
Habs jetzt wie im Bild.
Eine Frage noch, wenn ich Header einbinden will, schreib ich dann
einfach zu den OBJECTS header.o dazu?
Und wie lasse ich es neu compilieren wenn du sagst, dass es nicht
automatisch compiliert wird?
Heißt das, beim header muss dann stehn:
programm: $(OBJECTS)
$(Compiler) -o $@ $(OBJECTS)
$(Compiler) -c $@ header.h
?
überschreibt da die 3. zeile nicht die erste?
Wenn du die Variable CC statt Compiler nennst kannst du dir deine eigene
Pattern Rule fürs Übersetzen von .c nach .o sparen, denn die ist wie
gesagt schon vordefiniert. Außerdem fehlt deinem Pattern beim
Compileraufruf ein "-o $@", bei einer Regel wie "foo.o: bar.c" würde es
sonst schiefgehen (es würde bar.o statt foo.o erzeugen).
Erkan schrieb:> Eine Frage noch, wenn ich Header einbinden will, schreib ich dann> einfach zu den OBJECTS header.o dazu?
Nein, zu den Abhängigkeiten der Dateien, die davon abhängen (sie per
#include einbinden). Etwa
1
main.o: main.c header.h
Erkan schrieb:> Und wie lasse ich es neu compilieren wenn du sagst, dass es nicht> automatisch compiliert wird?
Es wird automatisch übersetzt, /wenn es nötig ist./ Wenn du ein
Neuübersetzen erzwingen willst, ist es am einfachsten die erzeugten
Dateien zu löschen. Dann müssen sie logischerweise neu übersetzt werden.
Erkan schrieb:> überschreibt da die 3. zeile nicht die erste?
Da überschreibt nix. Die erste Zeile benennt das Ziel und die
Abhängigkeiten. Die folgenden Zeilen werden alle ausgeführt, wenn das
Ziel neu erzeugt werden muss. Was drin steht interessiert make nicht, es
ersetzt einfach die Variablen in jeder Zeile durch ihre aktuellen Werte
und übergibt das Ergebnis einer Shell zur Ausführung.
Ich weise an dieser Stelle mal wieder auf Makefile-Generatoren wie cmake
hin, die einem vor dem auf Dauer doch recht anstrengenden manuellen
Warten dieser Makefiles bewahren. In CMake sähe das so aus:
Andreas, danke für deine äußerst gut Hilfe!
Ich werde einpaar von dir vorgeschlagene Dinge nicht anwenden, da ich
das ganze übersichtlich halten will, ich scheitere aber immer noch daran
das Headerfile erfolgreich einzubinden.
Habe es so versucht:
1
CC = gcc
2
OBJECTS = main1.o ausgabe.o
3
4
# $@ entspricht dem vollen Namen des Ziels einer Regel
5
# % ... bedeutet suche nach Datei
6
7
programm: $(OBJECTS)
8
$(CC) -o $@ $(OBJECTS)
9
10
main1.o:
11
$(CC) -c main1.c
12
$(CC) -c header1.h
13
14
ausgabe.o:
15
$(CC) -c ausgabe.c
16
$(CC) -c header1.h
Jedoch funktioniert es nicht ganz.
Kriege als Fehlermeldung:
1
gcc -o programm main1.o ausgabe.o
2
ausgabe.o: In function `ausgabe':
3
ausgabe.c:(.text+0x20): undefined reference to `spule'
4
collect2: ld returned 1 exit status
5
make: *** [programm] Error 1
Ich verstehe aber nicht ganz wieso. Ich habe das Headerfile in allen C
Files eingebungen. Und die Variable spule ist schon in main1.c
initialisiert.
Ich weiß immer noch nicht ganz wo ich das Headerfile dazugeben soll,
einige machen es doch in der Vsriable OBJECTS.
Aber du sagst schreib es in main1.o...
Ich bin da gerade so orientierungslos, weil ich weiß nicht wieso und was
für einen Unterschied es macht.
Wie soll denn spule, das in main deklariert ist, gefunden werden? Du
mußt es ausserhalb, zB in einer Datei deklarieren und diese (ich weiß
nicht ob gcc bei Vorwärts-Referenzen meckert, vermutl. aber schon) den
Compiler finden lassen bevor du etwas damit machst.
Zypern-rettender Steuerzahler schrieb:> Wie soll denn spule, das in main deklariert ist, gefunden werden?
Durch das Headerfile. Es müsste doch dem ausgabe.c File dadurch bekannt
sein.
Ich schreibe meine Programme bisher immer so, nur halt in
Entwicklungsumgebungen, dort hat es mit 1000en von Codezeilen immer gut
funktioniert die Variable nur in einem File zu initialisieren
Der Compiler ist ja ganz zufrieden -- das ist ein Linker-Fehler. Du hast
die deine statische Variable nur deklariert, es fehlt eine Definition,
bei der für die Variable dann auch ein Speicherbereich reserviert wird.
Erkan schrieb:> main1.o:> $(CC) -c main1.c> $(CC) -c header1.h
Nein, so geht das nicht. Damit rufst du den Compiler zwei mal auf,
einmal für die main1.c, einmal versuchst du header1.h für sich allein zu
übersetzen. Beim Aufruf des Compilers wirst du header1.h nie erwähnen
müssen, die wird durch #include schon eingebunden. Und es fehlen jetzt
die Abhängigkeiten.
Also nochmal:
1
main1.o: main1.c header1.h
2
${CC} ${CFLAGS} -c main1.c
(Und natürlich entsprechend für ausgabe.o) Das ist alles für das
einfaches Beispiel, mehr gehört da nicht hin. CFLAGS habe ich noch
eingefügt, das braucht man fast immer (etwa um Debug- und
Optimierungs-Flags einzuschalten).
Aber da in GNU make eine passende implizite Regel schon existiert,
reicht auch:
1
main1.o: main1.c header1.h
Das Kommando zum Übersetzen müsste man wirklich nur schreiben, wenn die
vorgegebene implizite Regel überhaupt nicht passt. Also eigentlich nur
bei einem Compiler, der ganz anders als gcc aufgerufen wird.
Die Flags lasse ich mal raus.
Ich hatte lediglich vergessen in die erste Zeile zu schreiben, dass
beide files das neue machen sollen.
Danke Andreas für die Hilfe!!!
PS: Gibt es eigentlich sonst noch irgendwelche Regeln außer diese?
Kann man auch Shellbefehle in makefiles einbauen?
Erkan schrieb:> PS: Gibt es eigentlich sonst noch irgendwelche Regeln außer diese?> Kann man auch Shellbefehle in makefiles einbauen?
Natürlich kann man, die Kommandos in den Regeln werden einfach einer
Shell zur Ausführung übergeben, wie ich schon geschrieben habe. Wenn du
mit andere Regeln meinst, etwas anderes als nur Sachen zu übersetzen,
auch das:
1
clean:
2
$(RM) programm $(OBJECTS)
3
4
.PHONY: clean
Das .PHONY ist eine Pseudo-Regel, die make dahingehend auswertet, dass
alle Abhängigkeiten nicht wirklich eine Ergebnisdatei erzeugen. Hier
also, dass bei "make clean" nicht geschaut wird, ob eine Datei clean
bereits existiert sondern die Regel bedingungslos ausgeführt wird.
Erkan schrieb:> So danke!! Jetzt funktioniert es, habs so:>
1
>main1.o:main1.cheader1.h
2
>gcc-cmain1.cheader1.h
3
>
Ist trotzdem falsch. Statt
1
main1.o: main1.c header1.h
2
gcc -c main1.c header1.h
muss es heißen:
1
main1.o: main1.c header1.h
2
gcc -c main1.c
Man wirft dem Compiler keine Header-Dateien (*.h) zum Fraß vor, sondern
nur die .c-Dateien. In der Abhängigkeits-Regel müssen natürlich alle
benutzten Header-Dateien angegeben werden.
Von daher ist:
Andreas B. schrieb:> clean:> $(RM) programm $(OBJECTS)>> .PHONY: clean
Cool, was genau passiert da eigentlich?
clean ist nehme ich mal an eine Regel.
Aber was ist die Variable RM ?
Gibt es irgendwo tutorials die diese ganzen Befehle detailliert
behandeln? Ich finde kaum was im Internet, wo auch die ganzen Befehle
sehr übersichtlich und verständlich erklärt werden?
Das kann doch nicht sein
Ok, bin jetzt auf einiges draufgekommen.
Shell Befehlele kann man anscheinend direkt nach der Regel definieren
und die Regel kann über make REGELNAME aufgerufen werden.
Das ist einfach klasse!!
Hab nur ne Frage wie ich bei nicht vorhanden einer Datei folgende
Fehlermeldung verhindere:
1
rm *.o
2
rm: cannot remove `*.o': No such file or directory
Erkan schrieb:> Ok, bin jetzt auf einiges draufgekommen.> Shell Befehlele kann man anscheinend direkt nach der Regel definieren> und die Regel kann über make REGELNAME aufgerufen werden.> Das ist einfach klasse!!
Irgendwie habe ich das Gefühl, dass du einfach nur meine Beispiele
abtippst und meine Erklärungen nicht liest. Eben das habe ich auch schon
zwei mal erklärt.
So, noch was: "make ziel1 ziel2 ziel3 ..." ist an Sich der Normalfall,
damit wird ziel1, ziel2, usw. aktualisiert. Das kannst du für beliebige
Regeln machen (also auch "make main.o"). Wenn du keine Ziele angibst,
nimmt make einfach die erste Regel im Makefile. Sinnvollerweise lässt
man also die Regel, die das ganze Projekt baut, die erste im Makefile
sein.
Erkan schrieb:> rm *.o> rm: cannot remove `*.o': No such file or directory> make: *** [clean] Error 1
Ist zu erwarten, wenn die Dateien nicht existieren. $(RM) ist mit "rm
-f" vordefiniert, wenn du das nicht verwendest willst musst du selbst
auch -f verwenden.
Andreas B. schrieb:> Ist zu erwarten, wenn die Dateien nicht existieren. $(RM) ist mit "rm> -f" vordefiniert, wenn du das nicht verwendest willst musst du selbst> auch -f verwenden.
DAAAAANKE!!
Funktioniert.