Makefile, ik kom er niet uit

Pagina: 1
Acties:
  • 152 views sinds 30-01-2008
  • Reageer

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Hoewel ik me redelijk om m'n gemak voel in de wondere wereld van linux, zijn makefiles nog steeds een klein wonder voor mij :+

Ik krijg maar niet onder de pet hoe deze dingen werken, dus ik hoop dat een van jullie mij verder kan helpen :)

stel de volgende situatie:

sources: src/*.cpp
headers: include/*.h
target: bin/progje
objects: obj/*.o

nu wil ik graag een Makefile maken die op basis hiervan, de cpp's kan compileren tot *.o files, en het liefst alleen opnieuw compilen if needed en het target kan linkej op basis van deze *.o files. Het lijkt mij een haalbaar doel, maar ik kom er maar niet uit :P

ik had het volgende in gedachte:
CC=/usr/bin/g++
SRC=src/*.cpp
OBJD=obj/
OBJS=*.o
CFLAGS=-Iinclude

clean:
    -rm $(OBD)$(OBJS)

obj: $(SRC)
    $(CC) $(CFLAGS) -c -o $(OBJD)$@

prog: obj
    $(CC) $(CFLAGS) -o bin/$@

Mijn idee was, dat wanneer je 'make prog' aanroept, deze eerst obj aanroept, welke eerst de *.o files maakt, waarna deze gebruikt worden om naar de executable gelinkt te worden. Echter heb ik de logica kennelijk niet begrepen, want zo werkt het helaas niet :P

wie kan mij uitleggen hoe het wel moet?

alvast bedankt!

oprecht vertrouwen wordt nooit geschaad


  • Ghost
  • Registratie: April 2002
  • Laatst online: 04-02-2025

Ghost

Phasma Ex Machina

Hoewel Makefiles bij mij ook niet heel duidelijk is, heb ik wel een idee van waar het fout gaat.

Als ik de laatste rule bekijk, is die afhankelijk van elk _bestaande_ objectfile die in de directory zit. Als je dus foo.o en bar.o in je dir hebt staan, is die rule afhankelijk van die twee objecten. Heb je alleen foo.o erin zitten is de rule afhankelijk van foo.o. Dus deze rule zal altijd uitgevoerd kunnen worden.
Okay, dat is dus onzin... ik snap nu pas wat die regel inhoudt. Maar nog steeds denk ik het volgende:

Wat je moet doen is expliciet de objecten aangeven.

Daarnaast heb je je variabele verkeerd (zou $(obj) moeten zijn)

code:
1
prog: foo.o bar.o

[ Voor 37% gewijzigd door Ghost op 05-09-2007 21:53 . Reden: Verkeerd gelezen :X ]

When in doubt, press some random buttons.


  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Ghost schreef op woensdag 05 september 2007 @ 21:47:
Hoewel Makefiles bij mij ook niet heel duidelijk is, heb ik wel een idee van waar het fout gaat.

Als ik de laatste rule bekijk, is die afhankelijk van elk _bestaande_ objectfile die in de directory zit. Als je dus foo.o en bar.o in je dir hebt staan, is die rule afhankelijk van die twee objecten. Heb je alleen foo.o erin zitten is de rule afhankelijk van foo.o. Dus deze rule zal altijd uitgevoerd kunnen worden.

Wat je moet doen is expliciet de objecten aangeven.

Daarnaast heb je je variabele verkeerd (zou $(obj) moeten zijn)

code:
1
prog: foo.o bar.o
hmm, de objecten zijn gelijk aan de cpp bestanden, zou ik de lijst met objecten ook dynamisch kunnen laten genereren?

oprecht vertrouwen wordt nooit geschaad


  • Ghost
  • Registratie: April 2002
  • Laatst online: 04-02-2025

Ghost

Phasma Ex Machina

Geen idee, hoewel er wel programmaatjes zijn die dat op kunnen lossen voor header files. Check http://www.hsrl.rutgers.edu/ug/make_help.html eens onderaan voor makedepend.

When in doubt, press some random buttons.


  • Super_ik
  • Registratie: Maart 2001
  • Laatst online: 13:39

Super_ik

haklust!

mischien met $(SRC:.cpp=.o)
maar volgens mij mag *.cpp niet, maar moet je echt
SRC=a.cpp b.cpp c.cpp opgeven

even getest, nee, dit mag idd niet.

8<------------------------------------------------------------------------------------
Als ik zo door ga haal ik m'n dood niet. | ik hou van goeie muziek


  • Sendy
  • Registratie: September 2001
  • Niet online
Eigenlijk is het heel eenvoudig.

De eerste regel van een "regel" geeft aan het "target", zoals "prog" bij jou. Gevolgd door de dubbele punt en spatie, en daarna de "afhankelijkheden". Als aan al deze afhankelijkheden voldaan is wordt de regel eronder uitgevoerd (en is je make dus klaar). Als een afhankelijkheid (dit is gewoon een bestand) niet bestaat, wordt het target gezocht dat dat bestand kan maken. Dit heeft natuurlijk weer afhankelijkheden, enzovoort.

Die variabelen die je gebruikt $(..) is alleen opsmuck.

Je krijgt dus zoiets:

Bash Session:
1
2
printenv: printenv.c
        $(CC) $(CFLAGS) $? $(LDFLAGS) -o $@


Het programma printenv is afhankelijk van printenv.c. Om deze te maken roep je je C compiler aan ($(CC)) met flaggen $(CFLAGS), met modules ($? -- de gewijzigde afhankelijkheden (dit kan je dus niet zomaar gebruiken als je meer afhankelijkheden hebt), met nog meer flaggen $(LDFLAGS) en produceerd een bestand met de naam van je target ($@)

edit:

En met makedepend kan je inderdaad die lijsten van afhankelijkheden laten genereren.

[ Voor 5% gewijzigd door Sendy op 05-09-2007 22:29 ]


  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Sendy schreef op woensdag 05 september 2007 @ 22:27:
Eigenlijk is het heel eenvoudig.

De eerste regel van een "regel" geeft aan het "target", zoals "prog" bij jou. Gevolgd door de dubbele punt en spatie, en daarna de "afhankelijkheden". Als aan al deze afhankelijkheden voldaan is wordt de regel eronder uitgevoerd (en is je make dus klaar). Als een afhankelijkheid (dit is gewoon een bestand) niet bestaat, wordt het target gezocht dat dat bestand kan maken. Dit heeft natuurlijk weer afhankelijkheden, enzovoort.
[...]
kijk, dat is een heldere uitleg :)
CC=/usr/bin/g++
SRC=src/a.cpp src/b.cpp src/c.cpp
OBJ=obj/a.o obj/b.o obj/c.o
CFLAGS=-Iinclude

clean:
    -rm $(OBD)$(OBJS)

%.o: %.c
    $(CC) $(CFLAGS) -c -o obj/$@

prog: $(OBJ)
    $(CC) $(CFLAGS) $(OBJ) -o bin/$@

zoiets zou dus beter zijn?

oprecht vertrouwen wordt nooit geschaad


  • ph0t0nix
  • Registratie: December 2006
  • Laatst online: 27-01 01:06
Wellicht ten overvloede: Je CLEAN regel verwijst nog naar de oude OBJD variabele
Je kunt gerust SRC=src/*.cpp en OBJ=obj/*.o opgeven.

De rest ziet er wel OK uit.

[ Voor 66% gewijzigd door ph0t0nix op 05-09-2007 22:42 ]


Verwijderd

En dan heb je nog steeds een probleem imho: header dependencies.

Het is duidelijk dat je C/C++ gebruikt, en zoals je weet hangen je C/C++ files doorgaans ook af van een aantal header files (.h/.hpp). Nu is het zo dat (GNU) make natuurlijk alleen dependencies kan controleren waar het weet van heeft, en make weet niets van C/C++ af.

Stel dat je nu even je programma bouwt, en dat dit werkt. Als je nu één of meerdere van je headers gaat aanpassen en je gaat even make uitvoeren, zal je programma niet opnieuw gebouwd worden. De reden is simpel: make controleert of je programma up to date is door te controleren of de object files up to date zijn. Als geen enkele source file is aangepast is dat zo (denkt ie) en doet dus niets. Dat is op zich jammer, maar stel je voor dat je ook nog source files hebt aangepast. Dan gaat ie deze aangepaste source files wel opnieuw compileren (omdat die zijn opgenomen in de dependencies), en zo kun je dus eindigen met een applicatie waarvan sommige object files de meest recente versie van een header hebben gebruikt en andere niet. Ik hoef je niet uit te leggen dat dat niet goed is en de meest bizarre dingen kan veroorzaken gaande van segmentation faults tot verkeerde output...

Daar zijn ook goede oplossingen voor, maar die vergen wel wat kennis vrees ik. Een goed begin - en nog te begrijpen voor een niet GNU make gevorderde/expert denk ik - lijkt me deze site. En ja, de paul in de URL is Paul D. Smitch, de maintainer van GNU make :)

Succes!

[edit]
Nog een 'fout' in je makefile: maak je clean target ook een PHONY target. Dat vermijdt dat een 'make clean' niets doet als er toevallig ooit een file genaamd 'clean' in die directory zou opduiken. Weinig waarschijnlijk, maar we kunnen je beter goede gewoontes aanleren vind ik ;)

Dus:
code:
1
2
3
.PHONY : clean
clean:
   <commando's>


En tenslotte verwijdert een 'make clean' je progje zelf niet ;)

[ Voor 11% gewijzigd door Verwijderd op 06-09-2007 22:15 ]


Verwijderd

Arjan schreef op woensdag 05 september 2007 @ 22:37:
%.o: %.c
    $(CC) $(CFLAGS) -c -o obj/$@
Je mist hier de sourcefile nog, wat je via $< kan ophalen.

Verwijderd

Verwijderd schreef op vrijdag 07 september 2007 @ 07:27:
Je mist hier de sourcefile nog, wat je via $< kan ophalen.
:o :o

/me vraagt zich af hoe hij dat kon missen en gaat zich schamen in een hoekje...

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Verwijderd schreef op donderdag 06 september 2007 @ 22:09:
En dan heb je nog steeds een probleem imho: header dependencies.
[...]deze site. En ja, de paul in de URL is Paul D. Smitch, de maintainer van GNU make :)
bookmarked :)
[edit]
Nog een 'fout' in je makefile: maak je clean target ook een PHONY target. Dat vermijdt dat een 'make clean' niets doet als er toevallig ooit een file genaamd 'clean' in die directory zou opduiken. Weinig waarschijnlijk, maar we kunnen je beter goede gewoontes aanleren vind ik ;)

Dus:
code:
1
2
3
.PHONY : clean
clean:
   <commando's>


En tenslotte verwijdert een 'make clean' je progje zelf niet ;)
make ziet het bestand clean staan, en besluit dat het target 'clean' niet meer hoeft te bouwen. Hoe helpt die .PHONY target daarbij?

de rest van de opmerkingen heb ik meegenomen in deze nieuwe versie:
CC=/usr/bin/g++
SRC=a.cpp b.cpp c.cpp
SRCDIR=src/
OBJ=a.o b.o c.o
OBJDIR=obj/
CFLAGS=-Iinclude
PRG=prog

clean:
    -rm $(OBJDIR)$(OBJ)
    -rm bin/$(PRG)

%.o: %.cpp
    $(CC) $(CFLAGS) $(SRCDIR)$< -c -o $(OBJDIR)$@

$(PRG): $(OBJDIR)$(OBJ)
    $(CC) $(CFLAGS) $(OBJDIR)$(OBJ) -o bin/$@

ik heb ook %.c even naar %.cpp veranderd :)

nu wil ik zelf eens wat experimenteren met make, is er een mogelijkheid om dmv een commando een bep. file over te slaan tijdens het builden? of zal ik deze gewoon uit de dependencies moeten halen?

[edit]$(OBJDIR) toegevoegd aan target $(PRG)[/edit]

hmm, omdat $(OBJDIR)$(OBJ) naar "src/a.o b.o c.o" wordt uitgeschreven werkt dit natuurlijk niet zoals bedoeld... is hier een weg omheen? of moet ik de mappen in de $(OBJ) vastleggen?


nog maar een poging
CC=/usr/bin/g++
SRC=a.cpp b.cpp c.cpp
SRCDIR=src/
OBJ=a.o b.o c.o
OBJDIR=obj/
CFLAGS=-Iinclude
PRG=prog
RECOMPILE=0

clean:
	-rm $(OBJDIR)$(OBJ)
	-rm bin/$(PRG)

%.o: %.cpp
	if [ $(RECOMPILE) -eq 1 ]; then $(CC) $(CFLAGS) -c $(SRCDIR)$< -o $(OBJDIR)$@; fi

%.cpp: %.h
	if [ $(SRCDIR)$@ -nt $(OBJDIR)$*.o ]; then export RECOMPILE=1; fi

%.h:
	if [ $(HDRDIR)$@ -nt $(OBJDIR)$*.o ]; then export RECOMPILE=1; fi

$(PRG): $(OBJ)
	$(CC) $(OBJDIR)$(OBJ) $(CFLAGS) $(LDFLAGS) -o bin/$@

een poging om te checken of de source/header file veranderd zijn sinds de laatste make, de environment variable RECOMPILE lijkt echter niet door te komen...doe ik iets fout?

[ Voor 23% gewijzigd door Arjan op 08-09-2007 12:26 ]

oprecht vertrouwen wordt nooit geschaad


Verwijderd

Arjan schreef op zaterdag 08 september 2007 @ 10:48:
make ziet het bestand clean staan, en besluit dat het target 'clean' niet meer hoeft te bouwen. Hoe helpt die .PHONY target daarbij?
In normale omstandigheden denkt make dat ieder target een bestand (eventueel directory, maar da's eigenlijk hetzelfde). Als je een target PHONY maakt, zoals je clean, zeg je eigenlijk aan make "dit is niet de naam van een bestand of directory maar gewoon een naam voor een hoopje commando's" en make zal dan niet eens controleren of er een bestand bestaat met die naam, want het boeit toch niet. Die commando's zullen gewoon altijd uitgevoerd worden als dat target in de prerequisites van een ander target zitten, of als je dat target expliciet opgeeft zoals jij zou doen met 'make clean'. Omdat make weet dat het geen naam van een bestand is worden ook de 'implicit rules' niet doorzocht, dus het is nog goed voor de performantie ook.

Je zult ongetwijfeld alle info terugvinden in de GNU make documentatie. Er is zeker een stuk over PHONY targets.
nu wil ik zelf eens wat experimenteren met make, is er een mogelijkheid om dmv een commando een bep. file over te slaan tijdens het builden? of zal ik deze gewoon uit de dependencies moeten halen?
Nee, dat hoeft niet. Je kunt de '-o file' en '-t file' opties gebruiken om respectievelijk een bepaald bestand als 'oud' te markeren en een bestand gewoon te updaten zonder de bijhorende commando's uit te voeren. Bvb 'make -o a.cpp ' na een touch van alle source files en dan zal make doen alsof a.cpp oud is (en dus a.o niet opnieuw bouwen). Of 'make -t a.o' en dan zal make a.o ook niet bouwen maar wel de modification time van het bestand aanpassen zoals touch zou doen zodat het voor een volgende make run lijkt alsof a.o wel werd gebouwd. Die opties staan ook uitgelegd in de documentatie, en na wat experimenteren merk je vanzelf wel hoe je ze kunt misbruiken ;)
hmm, omdat $(OBJDIR)$(OBJ) naar "src/a.o b.o c.o" wordt uitgeschreven werkt dit natuurlijk niet zoals bedoeld... is hier een weg omheen? of moet ik de mappen in de $(OBJ) vastleggen?
Nee, je moet niets vastleggen want GNU make heeft ook functies die je kunt gebruiken om dit te doen. Je kunt bvb de 'addprefix' functie gebruiken en schrijven '$(addprefix $(OBJDIR),$(OBJ))'. Dan plakt make wat er in OBJDIR zit voor ieder stuk (en white space bepaalt wat stukken zijn) in OBJ. Staat uiteraard ook in die documentatie :)

Als je dan ook nog SRC gebruikt om OBJ op te bouwen hoef je zelfs die twee niet meer in sync te houden:
OBJ=$(SRC:.cpp=.o)

Dit is wat ze 'substitution references' noemen. Staat uiteraard...juist, in de documentatie :P

Ik hoop dat je hiermee alvast een start hebt om wat te gaan experimenteren en de relevante stukken van de documentatie waar ik naar verwijs te vinden. Ik zeg niet dat je die volledige documentatie nu volledig van begin tot einde moet doorgronden, dat is waarschijnlijk niet nodig als je maar wat basisdingen van plan bent. Wat basisdingen (rules, phony target, substitution references, variabelen en de twee types, ...) zijn wel handig om te weten dus die zou ik wel eens doornemen mocht je dat nog niet gedaan hebben.

Succes!

Verwijderd

Arjan schreef op zaterdag 08 september 2007 @ 10:48:
CC=/usr/bin/g++
SRC=a.cpp b.cpp c.cpp
SRCDIR=src/
OBJ=a.o b.o c.o
OBJDIR=obj/
CFLAGS=-Iinclude
PRG=prog
RECOMPILE=0

clean:
	-rm $(OBJDIR)$(OBJ)
	-rm bin/$(PRG)

%.o: %.cpp
	if [ $(RECOMPILE) -eq 1 ]; then $(CC) $(CFLAGS) -c $(SRCDIR)$< -o $(OBJDIR)$@; fi

%.cpp: %.h
	if [ $(SRCDIR)$@ -nt $(OBJDIR)$*.o ]; then export RECOMPILE=1; fi

%.h:
	if [ $(HDRDIR)$@ -nt $(OBJDIR)$*.o ]; then export RECOMPILE=1; fi

$(PRG): $(OBJ)
	$(CC) $(OBJDIR)$(OBJ) $(CFLAGS) $(LDFLAGS) -o bin/$@

een poging om te checken of de source/header file veranderd zijn sinds de laatste make, de environment variable RECOMPILE lijkt echter niet door te komen...doe ik iets fout?
Ja ;)

Je exporteert RECOMPILE in de commando's voor een regel. GNU make voert ieder commando uit met een aparte shell invocatie (om ervoor te zorgen dat verschillende commando's dezelfde omgeving zien) Dat wil zeggen dat je export wel werkt, maar dat hij alleen zichbaar is binnen hetzelfde commando en dus niet tussen verschillende rules. En dat is nu net wat jij probeerde. Dat zal dus niet werken...

Mocht je dus bvb
%.cpp: %.h
	if [ $(SRCDIR)$@ -nt $(OBJDIR)$*.o ]; then export RECOMPILE=1; fi && echo $RECOMPILE

schrijven, zou je zien dat die echo wel de waarde 1 uitschrijft als je die export doet. Maar eens dit commando is afgelopen (het loopt in z'n eigen shell) is het afgelopen met die pret.

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
ok, ik heb in principe een makefile die voldoet voor de meeste projecten :)

wel erg wierd, af en toe flipt make en schrijft ie naar de Makefile zelf...

CC=/usr/bin/g++
CFLAGS=-I$(INCDIR)
LDFLAGS=
PROG=test

SRCDIR=src/
SRC=$(notdir $(wildcard $(SRCDIR)*.cpp))

OBJDIR=obj/
OBJ=$(SRC:.cpp=.o)

INCDIR=include/
INC=$(SRC:.cpp=.h)


$(PROG): $(addprefix $(OBJDIR),$(OBJ))
	-mkdir bin
	$(CC) $< $(CFLAGS) $(LDFLAGS) -o bin/$@

%.o: $(addprefix $(SRCDIR),$(SRC)) $(addprefix $(INCDIR),$(INC))
	-mkdir obj
	$(CC) $(CFLAGS) -c $< -o $@

update:
	cd $(INCDIR); svn update
	cd $(SRCDIR); svn update

.PHONY: clean

clean:
	-rm bin/$(PROG)
	-rm $(OBJDIR)*.o

[ Voor 3% gewijzigd door Arjan op 14-09-2007 20:45 ]

oprecht vertrouwen wordt nooit geschaad

Pagina: 1