L'utility make
è un programma che di per se non fa parte del famoso GCC, perché non è altro che un program manager molto usato, soprattutto in Ingegneria del Software, per gestire un gruppo di moduli di un programma, una raccolta di programmi o un sistema completo. Sviluppata originariamente per UNIX, questa utility per la programmazione anche in C, ha subito il porting su altre piattaforme, tra cui, appunto, Linux e Windows.
Se dovessimo mantenere molti file sorgenti, come ad esempio:
main.c funzione1.c funzione2.c ... funzionen.c
potremmo compilare questi file utilizzando il gcc, ma rimane comunque un problema di fondo anche quando abbiamo già creato qualche file oggetto (.o
) che vogliamo linkare durante la compilazione globale del programma:
- è tempo sprecato ricompilare un modulo per il quale abbiamo già il file oggetto, dovremmo compilare solamente quei file modificati dopo una determinata data, e facendo degli errori (cosa che aumenta con l'aumentare delle dimensioni del progetto) potremmo avere il nostro programma finale sostanzialmente errato o non funzionante.
- Per linkare un oggetto, spesso, è necessario scrivere grossi comandi da riga di comando. Scrivere tanti comandi lunghi e diversi per svariati file può indurre facilmente all'errore.
L'utility make viene in aiuto in questi scenari, fornendo automaticamente gli strumenti per fare questi controlli e quindi, garantisce una compilazione esente da errori e solo di quei moduli di cui non esiste già il file oggetto aggiornato.
L'utility make utilizza, a tal fine un semplice file di testo di nome "makefile", con all'interno regole di dipendenza e regole di interpretazione.
Una regola di dipendenza ha due parti, una sinistra ed una destra, separate dai due punti (:).
lato_sinistro : lato_destro
La parte sinistra contiene il nome di un target (nome del programma o del file di sistema) che deve essere creato, mentre quella destra definisce i nomi dei file da cui dipende il file target (file sorgenti, header o dati); se il file target (destinatario) risulta non aggiornato rispetto ai file che lo compongono, allora si devono seguire le regole di interpretazione che seguono quelle di dipendenza, infatti quando si esegue un makefile vengono seguite queste regole:
- Viene letto il makefile e si estrapola quali file hanno bisogno di essere solamente linkati e quali, invece, hanno bisogno di essere ricompilati
- Come detto prima, si controlla la data e l'ora di un file oggetto e se esso risulta "antecedente" rispetto ai file che lo compongono, si ricompila il file, altrimenti si linka solamente
- Nella fase finale si controlla data e ora di tutti i file oggetto e se anche uno solo risulta più recente del file eseguibile, allora si ricompilano tutti i file oggetto
Naturalmente possiamo estendere l'utility di make a qualsiasi ambito, perché possiamo usare qualsiasi comando dalla riga di comando, in questo modo risulterà intuitiva qualsiasi operazione di manutenzione, come fare il backup.
Creare un makefile
Ammettiamo di avere i file simili all'esempio precedente:
programma.c funzione1.c funzione2.c header.h
e di voler far si che ci siano delle regole che impongano di ricompilare se cambia qualche file, ecco come, ad esempio, potrebbe essere strutturato un makefile (modificabile con un qualsiasi editor di testo):
programma : programma.o funzione1.o funzione2.o funzione3.o gcc programma.o funzione1.o funzione2.o funzione3.o programma.o : header.h programma.c gcc -c programma.c funzione1.o : funzione1.c gcc -c funzione1.c funzione2.o : funzione2.c. gcc -c funzione2.c funzione3.o : funzione3.c. gcc -c funzione3.c
Questo programma può essere interpretato in questo modo:
- "programma" dipende da tre file oggetto, "programma.o", "funzione1.o" e "funzione2.o"; se anche uno solo di questi tre file risulta cambiato, i file devono essere linkati nuovamente
- "programma.o" dipende da due file, "header.h" e "programma.c", se uno di questi due file è stato modificato, allora bisogna ricompilare il file oggetto. Questo vale anche per ultimi due comandi
Gli ultimi tre comandi vengono chiamati regole esplicite perché si usano i nomi dei file in modo esplicito, mentre volendo si possono usare regole implicite come la seguente:
.c.o : gcc -c $<
che può sembrare apparentemente incomprensibile, ma che si traduce facilmente in "prendi tutti i file .c
e trasformali in file con estensione .o eseguendo il comando gcc
su tutti i file .c
(espresso con il simbolo $<
).
Per quanto riguarda i commenti, si usa il carattere cancelletto (#
), così tutto ciò che è sulla medesima riga viene ignorato; questo tipo di commento è lo stesso disponibile in python ed in perl.
Per eseguire un makefile è sufficiente digitare dalla linea di comando, il comando make
, il quale andrà automaticamente a cercare un file di nome makefile
da eseguire. Se però abbiamo utilizzato un nome diverso ad esempio backup_all
(abbiamo creato un makefile per fare il backup), possiamo dire a make
di interpretare quello come makefile corrente, digitando:
# make -f backup_all
Ovviamente esistono altre opzioni per il comando make
, che possono essere scoperte digitando da console, il comando:
# make --help # man make
Abbiamo dato un'idea dell'uso del make
e dei makefile, questo per farne comprendere le possibilità, senza addentrarci troppo nella programmazione di un makefile complesso, comprensivo di comandi più avanzati o dell'uso delle macro. Tutto ciò spero possa servire come punto di partenza per uno studio più approfondito di tale strumento che, comunque, viene utilizzato quando si inizia a lavorare su progetti di una certa dimensione e che, quindi, non è utile trattare ulteriormente in questo ambito.