Se si desidera eseguire o aggiornare un’attività quando vengono aggiornati determinati file, l’utilitàmake
può tornare utile. L’utilitàmake
richiede un file,Makefile
(omakefile
), che definisce un insieme di attività da eseguire. Potresti aver usato make
per compilare un programma dal codice sorgente. La maggior parte dei progetti open source utilizzamake
per compilare un binario eseguibile finale, che può essere installato utilizzandomake install
.,
In questo articolo, esploreremo make
e Makefile
utilizzando esempi di base e avanzati. Prima di iniziare, assicurarsi che make
sia installato nel sistema.
Esempi di base
Iniziamo stampando il classico “Hello World” sul terminale. Creare una directory vuota myproject
che contiene un file Makefile
con questo contenuto:
say_hello:
echo "Hello World"
eseguire il file digitando make
all’interno della directory myproject
., L’output sarà:
$ make
echo "Hello World"
Hello World
Nell’esempio precedente, say_hello
si comporta come un nome di funzione, come in qualsiasi linguaggio di programmazione. Questo è chiamato il bersaglio. I prerequisiti o le dipendenze seguono la destinazione. Per semplicità, non abbiamo definito alcun prerequisito in questo esempio. Il comandoecho "Hello World"
è chiamato la ricetta. La ricetta utilizza i prerequisiti per creare un target. L’obiettivo, i prerequisiti e le ricette insieme fanno una regola.,
Per riassumere, di seguito è riportata la sintassi di una regola tipica:
target: prerequisites
<TAB> recipe
Ad esempio, un target potrebbe essere un file binario che dipende dai prerequisiti (file di origine). D’altra parte, un prerequisito può anche essere un target che dipende da altre dipendenze:
Non è necessario che il target sia un file; potrebbe essere solo un nome per la ricetta, come nel nostro esempio. Noi chiamiamo questi ” obiettivi fasulli.,”
Tornando all’esempio precedente, quando è stato eseguitomake
, è stato visualizzato l’intero comandoecho "Hello World"
, seguito dall’output effettivo del comando. Spesso non lo vogliamo. Per sopprimere l’eco del comando effettivo, dobbiamo avviareecho
con@
:
say_hello:
@echo "Hello World"
Ora prova a eseguire nuovamentemake
., L’output dovrebbe mostrare solo questo:
$ make
Hello World
Cerchiamo di aggiungere un paio di più falso obiettivi: generate
e clean
per il Makefile
:
Se si tenta di eseguire make
dopo le modifiche, solo il target say_hello
verrà eseguito. Questo perché solo il primo target nel makefile è il target predefinito. Spesso chiamato l’obiettivo predefinito, questo è il motivo per cui vedrai all
come primo obiettivo nella maggior parte dei progetti., È responsabilità di all
chiamare altri target. Possiamo ignorare questo comportamento usando uno speciale target fasullo chiamato .DEFAULT_GOAL
.
Lasciate che include che all’inizio della nostra makefile:
.DEFAULT_GOAL := generate
Questo sarà il target generate
come predefinito:
$ make
Creating empty text files...
touch file-{1..10}.txt
Come suggerisce il nome, falso bersaglio .DEFAULT_GOAL
possibile eseguire un solo bersaglio alla volta. Questo è il motivo per cui la maggior parte dei makefile include all
come target che può chiamare tutti i target necessari.,
Includiamo il target fasulloall
e rimuoviamo.DEFAULT_GOAL
:
Prima di eseguiremake
, includiamo un altro target speciale fasullo,.PHONY
, dove definiamo tutti gli obiettivi che non sono file. make
eseguirà la sua ricetta indipendentemente dall’esistenza di un file con quel nome o dall’ora dell’ultima modifica., Qui è la completa makefile:
make
dovrebbe chiamare say_hello
e generate
:
$ make
Hello World
Creating empty text files...
touch file-{1..10}.txt
si tratta di una buona pratica di non chiamare clean
nel all
o metterlo come primo target., clean
dovrebbe essere chiamato manualmente quando è necessaria una pulizia come primo argomento make
:
$ make clean
Cleaning up...
rm *.txt
Ora che avete un’idea di come una base makefile funziona e come scrivere un semplice makefile, diamo un’occhiata ad alcuni esempi più avanzati.
Esempi avanzati
Variabili
Nell’esempio precedente, la maggior parte dei valori target e prerequisiti sono hard-coded, ma nei progetti reali, questi vengono sostituiti con variabili e pattern.,
Il modo più semplice per definire una variabile in un makefile è usare l’operatore =
., Per esempio, per assegnare il comando gcc
variabile CC
:
CC = gcc
Questo è anche chiamata ricorsiva ampliato variabile, e viene utilizzato in una regola, come illustrato di seguito:
hello: hello.c
${CC} hello.c -o hello
Come si può immaginare, la ricetta si espande come di seguito quando è passato al terminale:
gcc hello.c -o hello
Sia ${CC}
e $(CC)
sono validi riferimenti per chiamare gcc
. Ma se si tenta di riassegnare una variabile a se stessa, causerà un ciclo infinito., Verifichiamo questo:
CC = gcc
CC = ${CC}
all:
@echo ${CC}
L’esecuzione di make
comporterà:
$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually). Stop.
Per evitare questo scenario, possiamo utilizzare l’operatore :=
(questo è anche chiamato la variabile semplicemente espansa). Non dovremmo avere problemi a eseguire il makefile qui sotto:
CC := gcc
CC := ${CC}
all:
@echo ${CC}
Pattern e funzioni
Il seguente makefile può compilare tutti i programmi C usando variabili, pattern e funzioni., Esploriamolo riga per riga:
Di seguito è riportata la riscrittura del makefile sopra, supponendo che sia posizionato nella directory con un singolo file foo.c: