Home>>Il comando make

Quando si deve lavorare con un progetto molto esteso, formato da decine di file sorgente, la compilazione ed il collegamento dei vari moduli può diventare un'operazione molto laboriosa, specialmente se si vuole evitare che siano ricompilati i file sorgenti che non sono stati modificati. Questo articolo mostra l'utilizzo del programma di utilità make che consente di gestire agevolmente questo genere di situazioni.

Premessa

Lo sviluppo di applicazioni reali comporta la gestione di un numero elevato di file; in questi casi può essere conveniente suddividere il programma in un insieme di moduli funzionali, ciascuno implementato in un file sorgente separato. Ciascun file sorgente conterà il codice relativo alla funzionalità che si vuole gestire con quel modulo, mentre la funzione main() dovrà essere in un solo file. Organizzando il progetto in questo modo sarà possibile riutilizzare le funzionalità implementate in altri programmi semplicemente includendo i sorgenti relativi in fase di compilazione.

L'utility make

make è un programma di utilità, sviluppato originariamente per l'ambiente UNIX e successivamente portato anche su altre piattaforme, per gestire gruppi di file sorgenti. Più precisamente, nel caso di applicazioni suddivise in più moduli, potrebbe essere necessario ricompilare solamente i file modificati dopo una certa data, oppure scrivere complesse stringhe di comando per collegare assieme i moduli che costituiscono l'applicazione. L'utility make garantisce una compilazione esente da errori e solo dei moduli di cui non esiste il file oggetto aggiornato.

In realtà make può essere utilizzato anche per compilare un solo file sorgente: in questo caso tenta di determinare l'operazione più adatta da compiere, sulla base dell'estensione del file ricevuto in ingresso. Ad esempio se nella directory corrente esistesse il file prova.c, allora l'istruzione:

$ make prova.o[Invio]

è tradotto automaticamente nel comando seguente:

cc -o prova prova.c[Invio]

quindi nel caso in cui nella directory corrente esista il file prova.c, il comando andrà a buon fine.

Più comunemente questa utility riceve in ingresso un altro genere di file, denominato makefile, che specifica le dipendenze esistenti tra i moduli che compongono il progetto e le operazioni da compiere per aggiornarli.

Per eseguire un makefile è sufficiente digitare il comando make dalla shell; il programma cercherà automaticamente un file di nome "makefile" da eseguire. Nel caso in cui avessimo salvato il makefile con un nome diverso (ad esempio "compila_tutto"), possiamo specificare il file corretto aggiungendo l'opzione “-f”:

# make -f backup_all

Ovviamente esistono altre opzioni disponibili per il comando make, che possono essere scoperte tramite i comandi:

$ make --help$ man make 

Il makefile

Il makefile è un semplice file di testo contenente definizioni di macro, regole e commenti. Sebbene il makefile contenga uno script che generalmente è utilizzato per automatizzare la compilazione ed il collegamento di un insieme di moduli sorgenti, è bene notare che questo non rappresenta l'unico utilizzo: le azioni che possono essere specificate all'interno di un makefile, infatti, non sono limitate all'invocazione del compilatore o del linker, ma può essere richiamato qualunque comando della shell.

Le regole del makefile

Le regole si suddividono in regole di dipendenza e regole di interpretazione; generalmente ad una regola di dipendenza è associata una o più regole di interpretazione.

Una regola di dipendenza è formata di due parti separate dal carattere ":".

target : dipendenze

La parte di sinistra specifica il nome di un target che deve essere creato, mentre quella di destra definisce i file da cui dipende il target. Di solito target rappresenta il nome dell'eseguibile o del file oggetto da ricompilare, ma può anche rappresentare una sorta di identificatore delle azioni da compiere, specificate dalle regole di interpretazione associate.

Se, durante l'esecuzione del makefile, il target risulta non aggiornato rispetto ai file da cui dipende (cioè la sua data di creazione è antecedente a quella dei file specificati nella parte di destra), allora sono eseguite le regole di interpretazione associate alla regola di dipendenza.

Quindi più in generale le regole del makefile avranno l'aspetto seguente:

target : dipendenze ...comando...

Affinché il makefile sia valido è necessario che tutte le regole di interpretazione (i comandi) inizino con un carattere di tabulazione.

# Un esempio di Makefile con dipendenzeone:
@echo UNO!two:
one
@echo DUE!three:
one two
@echo E TRE!all:
one two three
@echo TUTTI E TRE!

Listato 1: esempio di makefile con dipendenze

L'esempio precedente (Listato 1) mostra un makefile che definisce quattro target e per ognuno di essi stampa sulla shell una stringa diversa. Poiché i target "three" e "all" dipendono dai target precedenti invocando il comando:

$make all

si otterrà l'output seguente:

UNO!
DUE!
E TRE!
TUTTI E TRE!

Il comando indicato in una regola, può proseguire su più righe successive, basta concludere la riga, prima del codice di interruzione di riga, con una barra obliqua inversa (vedi il Listato 2). Quello che conta è che le righe aggiuntive inizino sempre dopo un carattere di tabulazione.

Inoltre, come si può osservare dal Listato 1, il comando di una regola può iniziare con un prefisso particolare (vedi Tabella 1) che modifica il comportamento del programma make durante l'esecuzione del comando stesso.

Prefisso Significato
- fa in modo che gli errori vengano ignorati;
+ fa in modo che il comando venga eseguito sempre;
@ fa in modo che il testo del comando non venga mostrato.

Tabella 1: Elenco dei prefissi applicabili ai comandi delle regole

Le macro del makefile

Le macro sono simili alle variabili di ambiente definite negli script della shell e si definiscono semplicemente assegnando una stringa ad un nome:

nome = stringa

La stringa non deve essere delimitata., L'esempio seguente definisce la macro prefix, che da quel punto del makefile in poi equivale a /usr/local:

prefix=/usr/local

La sostituzione del nome di una macro con il valore corrispondente si indica attraverso due possibili notazioni:

$(nome)

oppure:

${nome}

Esistono alcune macro predefinite il cui contenuto può anche essere modificato. Le più importanti sono elencate nella Tabella 2.

Nome Definizione
MAKE make
AR ar
ARFLAGS rw
YACC yacc
YFLAGS  
LEX lex
LFLAGS  
LDFLAGS  
CC cc
CFLAGS  
FC f77
FFLAGS  

Tabella 2: Macro predefinite

Il Listato 2 rappresenta un semplice makefile che consente di mostrare sullo schermo il contenuto delle macro elencate nella Tabella 2.

flags:@echo MAKE $(MAKE) ; \
echo AR $(AR) ; \
echo ARFLAGS $(ARFLAGS) ; \
echo YACC $(YACC) ; \
echo YFLAGS $(YFLAGS) ; \
echo LEX $(LEX) ; \
echo LFLAGS $(LFLAGS) ; \
echo LDFLAGS $(LDFLAGS) ; \
echo CC $(CC) ; \
echo CFLAGS $(CFLAGS) ; \
echo FC $(FC) ; \
echo FFLAGS $(FFLAGS)

Listato 2: makefile che visualizza il contenuto delle macro

Un altro insieme di macro molto importante, il cui significato sarà più chiaro nei prossimi paragrafi, è il seguente (vedi Tabella 3).

Nome Significato
$< La prima dipendenza della regola.
$* Il nome del target senza suffisso.
$@ Il target della regola specificata.

Tabella 3: Altre macro predefinite

Le regole deduttive

make prevede alcune regole predefinite, o deduttive, riferite ai suffissi dei file indicati come target. Ad esempio, make è consapevole che per creare un file con il suffisso “.o”, è necessario invocare il comando cc -c sul corrispondente file “.c”; oppure se nelle dipendenze è specificato solamente un file “.h”, l'utility riterrà il target dipendente anche dal corrispondente file con estensione “.c”. Queste regole possono essere sfruttare per rendere più corti i makefile.

Si distingue tra due tipi di regole deduttive: a suffisso singolo e a suffisso doppio. Quindi, a seconda del suffisso specificato nel target, make esegue il comando predefinito più opportuno. La Tabella 4 ne riporta alcune per chiarire il concetto.

Target Comando corrispondente
.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
.f $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<
.c.o $(CC) $(CFLAGS) -c $<
.f.o $(FC) $(CFLAGS) -c $<

Tabella 4: Alcune regole deduttive

Ad esempio la terza regola della Tabella 4 significa: "prendi tutti i file .c e trasformali in file con estensione .o eseguendo il comando cc su tutti i file .c (espresso con il simbolo $<)”.]

L'esistenza delle regole deduttive consente al programma make di eseguire il makefile seguente (vedi Listato 3), nonostante che per il target dtmain non sia specificato alcun comando.

dt:	dtmain.o dtproc.occ -o dt dtmain.o dtproc.odtmain.o:	dtmain.c dt.hdtproc.o:	dtproc.c dt.hdatecc -c dtproc.c

Listato 3: makefile che fa uso delle regole deduttive

Conclusione

In questo articolo sono state descritte le principali funzionalità del programma make e quali vantaggi porta per la compilazione ed il collegamento di progetti software organizzati in più file.

E' pur vero che la scrittura di makefile per la compilazione e installazione di pacchetti portabili e complessi, può diventare un compito molto arduo. In questi casi è preferibile l'utilizzo di strumenti più sofisticati come autoconf, automake e libtool che generano makefile in modo automatico, partendo da specifiche di livello più alto.


Aggiungi commento




  Country flag
biuquote
  • Commento
  • Anteprima
Loading


Calendario

<<  febbraio 2012  >>
lumamegivesado
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar

Archivio

Licenza d'uso
Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati mediante:

Licenza Creative Common