[successivo] [precedente] [inizio] [fine] [indice generale] [violazione GPL] [translators] [docinfo] [indice analitico] [volume] [parte]
La compilazione di un programma, in qualunque linguaggio sia scritto, può essere un'operazione molto laboriosa, soprattutto se si tratta di aggregare un sorgente suddiviso in più parti. Una soluzione potrebbe essere quella di predisporre uno script che esegue sequenzialmente tutte le operazioni necessarie, ma la tradizione impone di utilizzare il programma Make.
Uno dei vantaggi più appariscenti sta nella possibilità di evitare che vengano ricompilati i file sorgenti che non sono stati modificati, abbreviando quindi il tempo di compilazione necessario quando si procede a una serie di modifiche limitate.
Make, per la precisione l'eseguibile make, viene utilizzato normalmente assieme a un file, il file-make (o makefile), il cui nome può essere generalmente makefile
o Makefile
, dove tra i due si tende a preferire l'ultimo con l'iniziale maiuscola. Il file-make serve a elencare a Make le operazioni da compiere e le interdipendenze che ci sono tra le varie fasi.
Make può anche essere usato da solo, senza file-make, per compilare un solo sorgente; in questo caso, tenta di determinare l'operazione da compiere più adatta, in base all'estensione del sorgente stesso. Per esempio, se esiste il file prova.c
nella directory corrente, il comando
$
make prova
fa sì che make avvii in pratica il comando seguente:
$
cc -o prova prova.c
Se invece esistesse un file-make, lo stesso comando, make prova, avrebbe un significato diverso, corrispondendo alla ricerca di un obiettivo con il nome prova all'interno del file-make stesso.
Un file-make è uno script specializzato per l'automazione della compilazione attraverso Make. Contiene la definizione di macro, simili alle variabili di ambiente di uno script di shell, e di obiettivi che rappresentano le varie operazioni da compiere.
All'interno di questi file, il simbolo # rappresenta l'inizio di un commento, cioè di una parte di testo che non viene interpretata da Make.
La definizione di una macro avviene in modo molto semplice, indicando l'assegnamento di una stringa a un nome che da quel momento la rappresenterà.
nome = stringa
La stringa non deve essere delimitata. Il funzionamento è molto simile alle variabili di ambiente dichiarate all'interno di uno script di shell. Per esempio,
prefix=/usr/local
definisce la macro prefix che da quel punto in poi equivale a /usr/local. La sostituzione di una macro si indica attraverso due modi possibili:
$(nome)
oppure
${nome}
come nell'esempio seguente, dove la macro exec_prefix viene generata a partire dal contenuto di prefix.
prefix=/usr/local exec_prefix=$(prefix)
Esistono alcune macro predefinite il cui contenuto può anche essere modificato. Le più importanti sono elencate nella tabella 290.1.
Tabella 290.1. Elenco di alcune macro predefinite di Make.
Nome | Contenuto |
MAKE | make |
AR | ar |
ARFLAGS | rw |
YACC | yacc |
YFLAGS | |
LEX | lex |
LFLAGS | |
LDFLAGS | |
CC | cc |
CFLAGS | |
FC | f77 |
FFLAGS |
Per verificare il contenuto delle macro predefinite, si può predisporre un file-make simile a quello seguente, eseguendo poi semplicemente make (i vari comandi echo sono rientrati con un carattere di tabulazione).
all: @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)
Oltre alle macro predefinite ne esistono altre, la cui utilità si vedrà in seguito.
Tabella 290.2. Elenco di alcune macro interne.
Macro | Significato |
$< | Il nome del file per il quale è stato scelto l'obiettivo per deduzione. |
$* | Il nome dell'obiettivo senza suffisso. |
$@ | L'obiettivo della regola specificata. |
Le regole sono il fondamento dei file-make. Attraverso di esse si stabiliscono degli obiettivi abbinati ai comandi necessari per ottenerli.
obiettivo... : [dipendenza...]
<HT>comando[; comando]...
La sintassi indica un comando che deve essere eseguito per raggiungere uno degli obiettivi nominati all'inizio, con le dipendenze che devono essere soddisfatte. In pratica, non si può eseguire il comando se prima non esistono i file indicati nelle dipendenze.
La dichiarazione inizia a partire dalla prima colonna, con il nome del primo obiettivo, mentre i comandi devono iniziare dopo un carattere di tabulazione. |
L'esempio seguente mostra una regola attraverso cui si dichiara il comando necessario a eseguire il link di un programma oggetto, specificando che questo può essere eseguito solo quando esiste già il file oggetto in questione.
mio_prog: prova.o cc -o prova prova.o
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 (nella sezione precedente è già stato mostrato un esempio di questo tipo). Quello che conta è che le righe aggiuntive inizino sempre dopo un carattere di tabulazione.
Il comando di una regola può iniziare con un prefisso particolare:
- 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.
Make prevede alcune regole predefinite, o deduttive, riferite ai suffissi dei file indicati come obiettivo. Si distingue tra due tipi di regole deduttive: a suffisso singolo e a suffisso doppio. La tabella 290.3 ne riporta alcune per chiarire il concetto.
Tabella 290.3. Elenco di regole deduttive a singolo e a doppio suffisso.
Obiettivo | Comando corrispondente |
.c | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< |
.f | $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $< |
.c.o | $(CC) $(CFLAGS) -o $< |
.f.o | $(FC) $(FFLAGS) -o $< |
Il file-make tipico, permette di automatizzare tutte le fasi legate alla ricompilazione di un programma e alla sua installazione. Si distinguono alcuni obiettivi comuni, usati di frequente:
all
utile per definire l'azione da compiere quando non si indica alcun obiettivo;
clean
per eliminare i file oggetto e i binari già compilati;
install
per installare il programma eseguibile dopo la compilazione.
Si ricorderà che le fasi tipiche di un'installazione di un programma distribuito in forma sorgente sono appunto:
#
make
che richiama automaticamente l'obiettivo all del file-make, coincidente con i comandi necessari per la compilazione del programma, e
#
make install
che provvede a installare gli eseguibili compilati nella loro destinazione prevista.
Supponendo di avere realizzato un programma, denominato mio_prog.c
, il cui eseguibile debba essere installato nella directory /usr/local/bin/
, si potrebbe utilizzare un file-make composto come l'esempio seguente:
prefix=/usr/local bindir=${prefix}/bin all: cc -o mio_prog mio_prog.c clean: rm -f core *.o mio_prog install: cp mio_prog $(bindir)
Come si può osservare, sono state definire le macro prefix e bindir in modo da facilitare la modifica della destinazione del programma installato, senza intervenire sui comandi.
L'obiettivo clean elimina un eventuale file core
, generato da un errore irreversibile durante l'esecuzione del programma, probabilmente mentre lo si prova tra una compilazione e l'altra, quindi elimina gli eventuali file oggetto e infine l'eseguibile generato dalla compilazione.
daniele @ swlibero.org
Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome automazione_della_compilazione_make_e_file_make.html
[successivo] [precedente] [inizio] [fine] [indice generale] [violazione GPL] [translators] [docinfo] [indice analitico]