[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]


160. Automazione della compilazione: Make e file-make

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.

160.1 Make

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' (quest'ultimo, con l'iniziale maiuscola, è quello preferito). 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, e in questo caso, tenta di determinare l'operazione più adatta da compiere 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.

160.2 File-make

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.

160.2.1 Macro

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, e 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 160.1.

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

Tabella 160.1: Elenco di alcune macro predefinite di Make.

Per verificare il contenuto delle macro predefinite, si può predisporre un file-make simile a quello seguente, e quindi eseguire 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.

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.

Tabella 160.2: Elenco di alcune macro interne.

160.2.2 Regole

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, e 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:

160.3 Regole deduttive

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 160.3 ne riporta alcune per chiarire il concetto.

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

Tabella 160.3: Elenco di regole deduttive a singolo e a doppio suffisso.

160.4 File-make tipico

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:

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.

---------------------------

Appunti Linux 2000.04.12 --- Copyright © 1997-2000 Daniele Giacomini --  daniele @ pluto.linux.it


[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]