Ricerca tra la vecchia roba

Makefile

Posted: giugno 20th, 2007 | Author: | Filed under: Guide | 1 Comment »

Uno degli splendidi strumenti costruiti dai programmatori del mondo Unix è rappresentato dal programma make che ha lo scopo di aiutare uno sviluppatore nello sviluppo di progetti di grosse dimensioni tenendo conto in automatico quali file sono stati modificati e dunque necessitano operazioni su di esse.

Nasce per opera di Stuart Feldman nei laboratori Bell nel 1977 (esistevano comunque altri tools similari ma questo è quello che si è diffuso di più!); basa principalmente il suo funzionamento sull’interpretazione di file nominati Makefile da cui deduce le cosidette regole.

Per averlo installato sul sistema si necessita del pacchetto make da installarsi sui sistemi debian-like attraverso il ben noto

apt-get install make

 

Rules (info make rules)

È la parte costitutiva dei Makefile: sono costituiti da un target (cioè un file o più file da creare) e da i suoi/loro prerequisiti necessari per ottenerlo; tra loro sono separati dai due punti. Subito sotto vanno posizionati i comandi da eseguire ognuno preceduto da un [TAB] ad inizio linea:

target:prerequisites
[TAB]command1
[TAB]…
[TAB]commandn

make segue la strategia:  controlla se i prerequisiti esistono, in caso contrario cerca se esistono regole implicite od esplicite per ottenerli (in caso contrario esce con errore), se esistono controlla che non siano stati modificati e nel caso uno o più di essi risulti modificato riesegue su di esso la regola ed infine esegue la regola iniziale.

Commands (info make commands

Come abbiamo visto subito sopra, all’interno delle regole abbiano i comandi da eseguire per ottenere il target: questi non vengono eseguiti da make, ma gestiti attraverso una shell che interpreta direttamente tutto  quello che si trova nelle righe che iniziano con il carattere [TAB]; quindi se dopo questo la linea è vuota alla shell arriverà un comando "vuoto", se è presente un commento sarà interpretato come tale solo se lo è anche per la shell. Anche la definizione di variabili è interna alla shell e non influenzerà i comandi successivi: nel caso

echo:
    @PIPPO="la madonna piange sperma" && echo $$PIPPO
    @echo $$PIPPO

si avrà il seguente output da console

packz@godel:/tmp
🙂 $ make
la madonna piange sperma

packz@godel:/tmp
🙂 $

Si può notare che nel primo caso avviene l’output seguendo il contenuto della variabile PIPPO, mentre nella riga seguente no (tutto sta nel costutto && e ancora adesso mi sfugge perché non funga…).

 È possibile spezzare comandi su più linee usando il carattere di backslash () seguito da newline ed in tal caso il make passa più righe alla shell (se dopo n è presente il [TAB] allora viene rimosso) che interpreterà questi in base alle sue regole, per esempio

all : ; @echo ‘hello
             world’ ; echo "hello
         world"

verrà trasformato nel comando di shell

echo ‘hello
     world’ ; echo "hello
         world"

da cui risulterà un output

hello
     world
     hello     world

siccome essa avrà tenuto conto delle regole di  quoting interne (in questo caso specifico ci si riferisce ad/bin/sh).

Variabili

 Ovviamente è possibile definire delle variabili che possono influenzare il comportamento del programma  e dei comandi durante l’esecuzione dello stesso: per definirne una basta collocare nel Makefile una riga del tipo

NOME_VARIABILE=valore

per poi richiamarlo nei comandi tramite la forma

$(NOME_VARIABILE) oppure ${NOME_VARIABILE}

Da notare che siccome il carattere $ è utilizzato da make per scopi suoi, nel caso abbiate la necessità di passare quel carattere alla shell, dovete raddoppiarla, cioé usare $$ (vedasi esempio sopra).

Per ogni regola make crea automaticamente delle variabili che individuano secondo alcune modalità i target ed i prerequisiti (vedi sezione "automatic variables").

Automatic Variables (info make "automatic variables")

Sono variabili definiti per ogni regola ed identificano i prerequisiti etc…

  • $@ – target
  • $% – utilizzato solo dagli archivi ed individua l’elemento interno dell’archivio del target; nei casi in cui nel target non c’è un archivio è nulla.
  • $< – primo prerequisito
  • $? – prerequisiti più nuovi (modificati)
  • $^ – prerequisiti (senza duplicati)
  • $+ – prerequisiti (con duplicati)
  • $| –
  • $* – radice con cui la regola implicita (se è il caso questo) viene identificata

Pattern rules (info make "pattern rules")

Consistono di regole definite attraverso i suffissi/prefissi/radici riconoscibili negli elementi da elaborare: per capirci, in tutti i progetti di programmi in C, i file oggetto vengono sempre creati a partire da file con la stesso nome ma con il suffisso cambiato da ‘.c’ a ‘.o’, esplicitato tramite una regola da Makefile diventa

file.o:file.c
     gcc file.c -c

siccome è una regola condivisibile tra tutti i file di questo tipo, è possibile definire delle regole che tengano conto dello schema dei file. Fondamentalmente le pattern rules

  • contengono il carattere % che individua qualunque stringa non nulla
  • nei prerequisiti (il carattere %) viene sostituito a runtime con la radice individuata nel target

quindi nel caso precedente è possibile riscrivere la regola usando % nel seguente modo

%.o:%.c
     gcc $< -c

(l’uso di $< vedi "automatic variables"); la particolarità consiste nell’aver esteso la regola a qualunque file .o, basta che sia presente il corrispettivo file .c nella directory corrente. Per conoscere le regole interne predefinite usate make -p in una directory senza un makefile; così scoprirete che un file .o viene creato da  un file .c secondo la regola

%.o: %.c
#  commands to execute (built-in):
        $(COMPILE.c) $(OUTPUT_OPTION) $<

dove vengono definite in automatico le variabili

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
OUTPUT_OPTION = -o $@

mentre un file eseguibile viene generato da un file .o attraverso la regola

%: %.c
#  commands to execute (built-in):
        $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@

dove

LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

Including

È possibile inserire altri makefile all’interno di un Makefile usando la direttiva

[-]include FILENAMES

dove il dash si usa nel caso in cui non si desiderano warning nel caso non esistano; questo è utile nel caso le dipendenze dei file vengano generati in automatico tramite l’opzione -M del compilatore (gcc ha anche l’opzione -MM che non include le librerie standard che probabilmente non verranno modificate dal programmatore e quindi non sono vincolanti per l’aggiornamento del file oggetto): nelle pagine di info viene consigliato di creare un file .d per ogni sorgente .c per poi includerlo esplicitamente tramite

SRC= file1.c file2.c file3.c
include $(SRC:.c=.d)

%.d: %.c
  @set -e; rm -f $@;
    $(CC) -MM $(CPPFLAGS) $(CPP_CAIRO) $(CPP_PANGO) $(CPPFLAGS_X11) $< > $@.$$$$;
    sed ‘s,($*).o[ :]*,1.o $@ : ,g’ < $@.$$$$ > $@;
    rm -f $@.$$$$

Nel caso in cui gli include non vengano trovati alla prima lettura del Makefile, make  cerca di generarli o aggiornarli e solo in questo caso la loro mancanza crea un errore "fatale" che blocca la sequenza.

Archivi

 Un discorso a parte merita la gestione degli archivi da parte di make: è possibile usare un membro interno di un archivio come target e/o prerequisito in una regola seguendo lo schema

nome_archivio(nome_elemento)

nel caso sia necessario è possibile individuare più elementi interni all’archivio separandoli con degli spazi come

nome_archivio(nome_primo_elemento nome_secondo_elemento)

 Per esempio ecco un Makefile che a partire dai file .c genera un archivio ‘packz’

ARCHIVIO=packz
MEMBERS=$(wildcard *.c)

$(ARCHIVIO):$(ARCHIVIO)($(MEMBERS))
  @echo "Creating archive from files $(MEMBERS)"

(%):%
  ar rv $(ARCHIVIO) $%

clean:
  rm -f $(ARCHIVIO)

 (per adesso la gestione degli archivi pare funzionare solamente con ar!).

Informazioni

  • info make
  • Guida sul sito della GNU

One Comment on “Makefile”

  1. 1 simmese said at 1:44 pm on giugno 26th, 2007:

    Ottimo tutorial… Ma non ci si poteva aspettare niente di meno da uno come te…
    Packz rules… (Degno di menzione)

    NB Lodate Javhè miscredenti…