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

132. 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.

132.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 singolo 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 obbiettivo con il nome prova all'interno del file-make stesso.

132.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 obbiettivi 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.

132.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 132.1.

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

Tabella 132.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'obbiettivo per deduzione.
$* Il nome dell'obbiettivo senza suffisso.
$@ L'obbiettivo della regola specificata.

Tabella 132.2: Elenco di alcune macro interne.

132.2.2 Regole

Le regole sono il fondamento dei file-make. Attraverso di esse si stabiliscono degli obbiettivi abbinati ai comandi necessari per ottenerli.

<obbiettivo>... : [<dipendenza>...]
<HT><comando>[; <comando>]...

La sintassi indica un comando che deve essere eseguito per raggiungere uno degli obbiettivi 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 obbiettivo, 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:

132.3 Regole deduttive

Make prevede alcune regole predefinite, o deduttive, riferite ai suffissi dei file indicati come obbiettivo. Si distingue tra due tipi di regole deduttive: a singolo e a doppio suffisso. La tabella 132.3 ne riporta alcune per chiarire il concetto.

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

Tabella 132.3: Elenco di regole deduttive a singolo e a doppio prefisso.

132.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 obbiettivi 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'obbiettivo 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'obbiettivo 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 1999.09.21 --- Copyright © 1997-1999 Daniele Giacomini --  daniele @ pluto.linux.it


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