Ricerca nel sito web

Una breve introduzione ai "Makefile" nello sviluppo di software open source con GNU Make


GNU Make è un'utilità di sviluppo che determina le parti di una particolare base di codice che devono essere ricompilate e può emettere comandi per eseguire tali operazioni sulla base di codice. Questa particolare utilità make può essere utilizzata con qualsiasi linguaggio di programmazione purché la sua compilazione possa essere effettuata dalla shell emettendo comandi.

Per utilizzare GNU Make, dobbiamo avere un insieme di regole che definiscano la relazione tra i diversi file nel nostro programma e comandi per aggiornare ciascun file. Questi vengono scritti su un file speciale chiamato "makefile". Il comando 'make' utilizza il database 'makefile' e gli orari delle ultime modifiche dei file per decidere cui tutti i file devono essere ricompilati nuovamente.

Contenuto di un Makefile

Generalmente i "makefile" contengono 5 tipi di cose, vale a dire: regole implicite, regole esplicite, definizioni di variabili , direttive e commenti.

  1. Una regola esplicita specifica come creare/rifare uno o più file (chiamati target, verrà spiegato più avanti) e quando fare lo stesso.
  2. Una regola implicita specifica come creare/rifare uno o più file in base ai loro nomi. Descrive come il nome di un file di destinazione è correlato a un file con un nome simile alla destinazione.
  3. Una definizione di variabile è una riga che specifica un valore stringa per una variabile da sostituire in seguito.
  4. Una direttiva è un'istruzione per make di fare qualcosa di speciale durante la lettura del makefile.
  5. Viene utilizzato il simbolo '#' per rappresentare l'inizio di un commento all'interno dei makefile . Una riga che inizia con "#" viene semplicemente ignorata.

Struttura dei Makefile

L'informazione che dice a make come ricompilare un sistema proviene dalla lettura di un database chiamato makefile. Un semplice makefile sarà costituito da regole con la seguente sintassi:

target ... : prerequisites ... 
	recipe 
... 
...

Un destinazione è definito come il file di output generato dal programma. Possono anche trattarsi di obiettivi fasulli, come verrà spiegato di seguito. Esempi di file di destinazione includono eseguibili, file oggetto o destinazioni fasulle come pulito, installa, disinstalla ecc.

Un prerequisito è un file utilizzato come input per creare i file di destinazione.

Una ricetta è l'azione che make esegue per creare il file di destinazione in base ai prerequisiti. È necessario inserire il carattere tab prima di ogni ricetta all'interno dei makefile a meno che non specifichiamo la variabile '.RECIPEPREFIX' per definire qualche altro carattere come prefisso alla ricetta.

Un Makefile di esempio

final: main.o end.o inter.o start.o
	gcc -o final main.o end.o inter.o start.o
main.o: main.c global.h
	gcc -c main.c
end.o: end.c local.h global.h
	gcc -c end.c
inter.o: inter.c global.h
	gcc -c inter.c
start.o: start.c global.h
	gcc -c start.c
clean:
	rm -f main.o end.o inter.o start.o

Nell'esempio sopra abbiamo utilizzato 4 file sorgente C e due file di intestazione per creare l'eseguibile final. Qui ogni file ".o" è sia un obiettivo che un prerequisito all'interno del makefile. Ora dai un'occhiata al nome dell'ultimo target clean. È solo un'azione piuttosto che un file di destinazione.

Poiché normalmente non ne abbiamo bisogno durante la compilazione, non è scritto come prerequisito in nessun'altra regola. I target che non si riferiscono a file ma sono solo azioni sono chiamati bersagli fasulli. Non avranno prerequisiti come gli altri file di destinazione.

Come GNU Make elabora un Makefile

Per impostazione predefinita, make inizia con il primo target nel "makefile" e viene chiamato " obiettivo predefinito'. Considerando il nostro esempio, abbiamo final come primo obiettivo. Poiché i suoi prerequisiti includono altri file oggetto, questi devono essere aggiornati prima della creazione final. Ciascuno di questi prerequisiti viene elaborato secondo le proprie regole.

La ricompilazione avviene se vengono apportate modifiche ai file sorgente o ai file di intestazione o se il file oggetto non esiste affatto. Dopo aver ricompilato i file oggetto necessari, make decide se ricollegare final o meno. Questo deve essere fatto se il file final non esiste o se uno qualsiasi dei file oggetto è più recente di esso.

Pertanto, se modifichiamo il file inter.c, eseguendo make ricompilerà il file sorgente da aggiornare il file oggetto inter.o e quindi il collegamento final.

Utilizzo delle variabili all'interno dei Makefile

Nel nostro esempio, dovevamo elencare tutti i file oggetto due volte nella regola final come mostrato di seguito.

final: main.o end.o inter.o start.o
	gcc -o final main.o end.o inter.o start.o

Per evitare tali duplicazioni, possiamo introdurre variabili per memorizzare l'elenco dei file oggetto utilizzati all'interno del makefile. Utilizzando la variabile OBJ possiamo riscrivere il makefile di esempio in uno simile mostrato di seguito.

OBJ = main.o end.o inter.o start.o
final: $(OBJ)
	gcc -o final $(OBJ)
main.o: main.c global.h
	gcc -c main.c
end.o: end.c local.h global.h
	gcc -c end.c
inter.o: inter.c global.h
	gcc -c inter.c
start.o: start.c global.h
	gcc -c start.c
clean:
	rm -f $(OBJ)

Regole per pulire la directory di origine

Come abbiamo visto nell'esempio makefile, possiamo definire regole perripulire la directory di origine rimuovendo i file oggetto indesiderati dopo la compilazione. Supponiamo di avere un file di destinazione chiamato clean. Come si può fare differenziare le due situazioni precedenti? Ecco il concetto di obiettivi fasulli.

Un bersaglio fasullo è quello che non è realmente il nome di un file, piuttosto è solo il nome di una ricetta da eseguire ogni volta che viene fatta una richiesta esplicita dal makefile<. Uno dei motivi principali per utilizzare bersaglio fasullo è evitare un conflitto con un file con lo stesso nome. Un altro motivo è migliorare le prestazioni.

Per spiegare questa cosa, svelerò una svolta inaspettata. La ricetta per clean non verrà eseguita per impostazione predefinita quando si esegue make. È invece necessario invocare lo stesso impartendo il comando make clean.

.PHONY: clean
clean:
	rm -f $(OBJ)

Ora prova a creare makefile per la tua codebase. Sentiti libero di commentare qui con i tuoi dubbi.