<- XML - Copertina - Impressioni su Linux ->

Articolo


I makefile

1. Makefile

1.1 Cosa è un Makefile

Come tutti i programmatori sanno, creare un programma eseguibile da più file sorgenti consiste nel compilare separatamente i file sorgenti uno ad uno e poi unirli nel file eseguibile finale. Ma quando i file sorgente sono molti, come si fa a far si che non diventi snervante il compilare? Semplicemente creando un file contente il compilatore da usare, in che modo usarlo, quali file compilare, come creare il file eseguibile (programma o libreria che esso sia) e altre cose sempre utili, come la directory dove trovare le librerie necessarie ed i file di include. Questo è il contenuto del file Makefile.

1.2 Creare un file Makefile

Creare un file Makefile è allo stesso tempo facile e difficile, il tutto dipende da cosa dovete fare. Per ora verrà descritto il metodo per creare un semplice file Makefile che consentirà di compilare il proprio programma. Ad esempio quello che segue è un mio file Makefile.

#Makefile del programma XLOAD
VERSION = 0.8

CC       = gcc
VERSIONGTK = 0.99.0
SRCDIRGTK = /src/
#OPTIMIZE = -g -Wall
OPTIMIZE = -O3 -s -fomit-frame-pointer 
CFLAGS   = $(DEFINES) $(OPTIMIZE)
LFLAGS   = -lgtk -lgdk -lglib -lm

PROGS    = zb_xload

PROGS_O  = main.o 
           
LIBS     = 

all:    objs progs

progs : 
        $(CC) $(CFLAGS) $(LFLAGS) -o $(PROGS) $(PROGS_O) $(LIBS)

objs:   $(PROGS_O)

.c.o:
        $(CC) $(CFLAGS) -c -o $*.o $< 

.c.s:
        $(CC) $(CFLAGS) -S -o $*.s $<

.o:
        $(CC) $(CFLAGS) $(LFLAGS) -o $* $(PROGS_O) $(LIBS)
        #chmod a+rs,go-w $*

testaccel: testaccel.o
        $(CC) $(CFLAGS) $(LFLAGS) -o testaccel testaccel.o $(LIBS) -lm
        chmod a+rs,go-w testaccel

clean           :       cleanbin
        rm -f *.o *~

cleanbin        :
        rm -f $(PROGS)

dep             :
        rm -f .depend
        make .depend

tar:
        tar czvf zb_xload.tgz Makefile main.c README TODO

# DO NOT DELETE

Ma vediamo nel dettaglio come è composto il Makefile.

La prima riga è

#Makefile del programma XLOAD

Come si può ben capire il simbolo # denota l'inizio di un commento, commento che non si può protrarre su più linee. Per creare un commento di più linee bisogna iniziare ogni linea con il simbolo #.

Ad esempio:

#Prima linea del commento.                      
#Seconda linea.                                 
comando         #inizio commento sulla terza riga
#Terzo commento                                 

La seguente linea definisce quale è il compilatore da usare.

CC       = gcc

Logicamente il gcc sarà sostituito da g++ se si compila in C++. Definire il compilatore da usare rende più facile la compilazione di sorgenti situati sotto directory diverse, poiché da un Makefile se ne può chiamare (ed eseguire) un altro passandogli alcuni valori (decisi dall'utente) come il compilatore da usare. Questo è utile specialmente se vogliamo portare un programma da Linux (dove si usa il gcc) a Irix (che usa il cc).

Le 14 righe successive definiscono alcune cose come le librerie da usare o i flag da passare al compilatore. Logicamente queste informazioni variano da programma a programma e anche il modo di chiamare le definizioni possono variare.

Delle definzioni è utile spiegare la seguente linea:

CFLAGS   = $(DEFINES) $(OPTIMIZE)

In questo modo la variabile CFLAGS viene istanziata con il contenuto della variabile DEFINES e e della variabile OPTIMIZE. Praticamente la variabile CFLAGS è l'unione di DEFINES e OPTIMIZE.

Il seguente pezzo di codice del Makefile è molto importante:

all:    objs progs

progs : 
        $(CC) $(CFLAGS) $(LFLAGS) -o $(PROGS) $(PROGS_O) $(LIBS)

objs:   $(PROGS_O)

.c.o:
        $(CC) $(CFLAGS) -c -o $*.o $< 

.c.s:
        $(CC) $(CFLAGS) -S -o $*.s $<

.o:
        $(CC) $(CFLAGS) $(LFLAGS) -o $* $(PROGS_O) $(LIBS)
        #chmod a+rs,go-w $*

testaccel: testaccel.o
        $(CC) $(CFLAGS) $(LFLAGS) -o testaccel testaccel.o $(LIBS) -lm
        chmod a+rs,go-w testaccel

clean           :       cleanbin
        rm -f *.o *~

cleanbin        :
        rm -f $(PROGS)

dep             :
        rm -f .depend
        make .depend

tar:
        tar czvf zb_xload.tgz Makefile main.c README TODO

Queste linee definscono quali sono le sezioni del Makefile che possono essere richiamate singolarmente. In parole più semplici al comando make si può passare anche quale sezione del Makefile va eseguita semplicemente aggiungendola dopo il comando make.

Si pensi alla installazione di una libreria già compilata precedentemente. Se il comando eseguito fosse solo

% make

(con un un certo tipo di Makefile) la libreria sarebbe ricompilata del tutto e solo in seguito installata. Passando, al make, anche il nome della sezione che si occupa della installazione della nostra libreria (nel nostro caso install) questa non sarebbe ricompilata, ma subito installata. Quindi:

% make install

Il nome di ogni sezione deve essere formata da una parola unica che può, però, essere anche l'unione di più parole tramite i simboli - e _ e deve essere seguita dal simbolo :

Nel caso in cui al comando make non fosse passato anche il nome della sezione da eseguire, automaticamente make esegue la prima sezione che incontra, che nel nostro caso è all.

Le sezioni si possono dividere in 2 casi:

Richiamanti
Sono le funzioni, come all, che non eseguono una vero programma come potrebbe essere rm, ma che richiamano (e quindi eseguono) altre sezioni dello stesso Makefile. Questo tipo di sezioni è denotato dal fatto che il nome (o i nomi) della sezione da richiamare è inserito sulla stessa linea.
esecutori
Sono le funzioni, come cleanbin che eseguono un comando vero e proprio come rm. In questo tipo di funzioni il o i comandi di funzione devono iniziare dalla riga sottostante.

È inoltre importante sapere che nei file makefile non devono esistere spazi prima di un comando o del nome di una sezione. Nel caso della sezione cleanbin:

 
cleanbin        :
rm -f $(PROGS)

alla sinistra del comando rm non ci sono spazi, ma bensì una tabulazione.

È inoltre possibile unire, in una unica sezione, il richiamo ad altre sezioni e l'esecuzione di uno o più specifici comandi. Un esempio di questi è la sezione clean

clean           :       cleanbin
rm -f *.o * 

In questo caso prima viene eseguita la sezione cleanbin e poi il comando rm.

1.3 Perché definire alcune cose

Il file Makefile sembra un pò strano in alcune parti, specialmente perché definisce alcune cose abbastanza inutili (almeno così sembra) come quale compilatore usare o come chiamare il file eseguibile; il motivo di tutto ciò è derivato dal fatto che il il file Makefile può essere utilizzato per creare un file chiamato configure che gestisce meglio il porting di uno stesso programma da una piattaforma ad un'altra controllando se tutto quello che occorre per compilare esattamente il programma stesso esiste ed è accessibile sulla macchina in questione. Si pensi ad esempio al compilatore standard che sotto linux è il gcc, mentre sotto Irix è il cc.

1.4 Eseguire un file Makefile differente

Può essere utile, in alcuni casi, creare più Makefile differenti senza crearne uno che vada bene per tutto. In questo modo il comando make, quando eseguito come descritto nella sezione precedente, terminerà la propria esecuzione con il seguente messaggio di errore:

make: *** No targets.  Stop.
Questo accade perché il comando make per default cerca il file chiamato esattamente Makefile, che noi, in questo caso, non abbiamo. Per far sì che il comando make utilizzi il Makefile da noi desiderato (ad esempio Makefile.mio) dobbiamo passare, tramite l'opzione -f anche il nome del file Makefile da eseguire. Nel nostro caso, il tutto si risolve esegeundo
% make -f Makefile.mio

2. Bibliografia

Post su it.comp.linux ed comp.unix.programmer

di Michel Morelli


<- XML - Copertina - Impressioni su Linux ->