lunedì 26 settembre 2011

Creare un MakeFile


A volte, i file sorgente da compilare sono tanti ed è sconveniente, per chi compila da terminale, scrivere ogni volta il comando g++ seguito da tutti i nomi delle sorgenti...
I makefile servono proprio a questo, sono dei file speciali che consentono, tramite la sola chiamata al comando (make appunto) di compilare tutti i file sorgenti che vogliamo. Ma com'è composto un makefile? Un makefile semplice in genere contiene 3 parti:
  1. Commenti 
  2. Macro
  3. Dipendenze e comandi
I commenti sono preceduti da # e non sono necessari per creare un makefile, ma sono ottimi per ricordarci cosa abbiamo scritto e l'eventuale significato di alcuni comandi/macro.

Le Macro sono dei contenitori aventi un nome e servono a dare più leggibilità e modularità al makefile.Le Macro vanno assegnate, un pò come le variabili, indicando il nome (in genere si usa scriverlo in maiuscolo) il segno d'uguaglianza e le istruzioni che deve contenere.
Per poter richiamare le Macro dichiarate in precedenza, bisogna usare il simbolo $ seguito da (NomeMacro) o anche ${NomeMacro}, in questo modo viene chiamato il contenuto assegnato precedentemente alla macro (tutto ciò che abbiamo messo dopo il segno di uguaglianza).
Esempio:

#Macro
CC=gcc #indica che la macro CC contiene il nome del compilatore #usato, nel nostro caso gcc per c++.

Vi sono alcuni nomi di Macro usati per convenzione, tra di essi ricordiamo:
CC-contiene il nome del compilatore che usiamo 
CFLAGS-contiene i parametri di compilazione
OBJ-contiene i file oggetto (.o)
La fase delle dipendenze e dei comandi è formata così:

target: dipendenze
(TAB)comando

Sia il target che le dipendenze sono presenti nella cartella del makefile. Il make esegue i comandi se il target ha la data di modifica più recente o se le due date coincidono, altrimenti si salta il target principale e si passa ai target delle dipendenze (se essi sono presenti).


Esempio di un MakeFile:
Abbiamo un gestore di una Banca avente i file:


main Banca.cpp Cliente.cpp ContoCorrente.cpp ContoCorrenteGold.cpp ContoCorrentePlatinum.cpp ContoCorrenteSilver.cpp ContoCorrenteStandard.cpp (e ovviamente tutti i .h relativi ai cpp).
In ogni file abbiamo scritto cosa importiamo, per esempio nel file ContoCorrenteGold avrà la sua dipendenza verso ContoCorrente. Ora creiamo il nostro makefile


  1. #Makefile per Banca

  2. #Macro
  3. BIN=banca
  4. CC=g++ #compilatore gcc per c++
  5. # File oggetto
  6. OBJS=main.o Banca.o Cliente.o ContoCorrente.o ContoCorrenteGold.o ContoCorrentePlatinum.o ContoCorrenteSilver.o ContoCorrenteStandard.o

  7. #Dipendenze e comandi

  8. #banca dipende da main.o Banca.o Cliente.o ContoCorrente.o #ContoCorrenteGold.o ContoCorrentePlatinum.o ContoCorrenteSilver.o #ContoCorrenteStandard.o

  9. $(BIN): $(OBJS)
  10. $(CC) -o $(BIN) $(OBJS) 

  11. main.o: main.cpp ContoCorrenteStandard.h ContoCorrenteGold.h Cliente.h Banca.h
  12. $(CC) -c main.cpp

  13. Banca.o: Banca.h Banca.cpp ContoCorrenteStandard.h ContoCorrenteGold.h
  14. $(CC) -c Banca.cpp
  15. Cliente.o: Cliente.cpp Cliente.h
  16. $(CC) -c Cliente.cpp

  17. ContoCorrente.o: ContoCorrente.cpp ContoCorrente.h
  18. $(CC) -c ContoCorrente.cpp

  19. ContoCorrenteGold.o: ContoCorrenteGold.cpp ContoCorrenteGold.h ContoCorrente.h 
  20. $(CC) -c ContoCorrenteGold.cpp 

  21. ContoCorrenteSilver.o: ContoCorrenteSilver.cpp ContoCorrenteSilver.h ContoCorrente.h 
  22. $(CC) -c ContoCorrenteSilver.cpp 

  23. ContoCorrenteStandard.o: ContoCorrenteStandard.cpp ContoCorrenteStandard.h ContoCorrente.h
  24. $(CC) -c ContoCorrenteStandard.cpp 

  25. ContoCorrentePlatinum.o: ContoCorrentePlatinum.cpp ContoCorrentePlatinum.h ContoCorrente.h
  26. $(CC) -c ContoCorrentePlatinum.cpp 

Ora un pò di commenti:


In riga 7 dichiariamo la macro chiamata OBJS che conterrà i nostri file oggetto. In riga 13 diciamo che il file che vogliamo creare dipende da tutti i file oggetto ed eseguiamo nella riga successiva il comando classico per compilare (usando le macro stavolta). Le righe che seguono sono le altre dipendenze, necessarie se vogliamo che il nostro makefile sia ottimale anche per eventuali modifiche sui file .h e .cpp. La regola è semplice, ogni file oggetto dipende dal suo .h e dal suo .cpp ed eventualmente da altri file headers inclusi nel codice. Dopo ogni dichiarazione di dipendenza, va compilato (è sufficiente il cpp) tramite g++ -c nomeFile.cpp. I makefile consentono anche tante altre semplificazioni e peculiarità, che verranno discussi in seguito su altri post. 


Foto post: DRUPAL











3 commenti: