...faccia da "ma ho solo rubato un makefile... |
Bando alle ciance: se state leggendo questa riga avete risposto NO alla domanda precedente, e quindi vai con l'esempio!
Come vedete il makefile presentato è veramente semplice. Però è anche veramente completo: fa tutto quello che serve, compresa la generazione dei file di dipendenza dagli header, e possiamo usarlo per qualsiasi progetto, indipendentemente dal numero di file (le directory lib e include potrebbero essere vuote oppure contenere centinaia di file). Possiamo aggiungere e togliere sorgenti e header e ricompilare senza modificare una sola linea del makefile, perché lui si adatta automaticamente a quello che trova nelle tre directory del progetto: cosa vogliamo di più?# variabiliCC = gccSRCS = $(wildcard *.c)SRCS_LIB = $(wildcard ../lib/*.c)OBJS = $(SRCS:.c=.o)OBJS_LIB = $(SRCS_LIB:.c=.o)DEPS = $(SRCS:.c=.d)DEPS_LIB = $(SRCS_LIB:.c=.d)# creazione del target file eseguibilepluto: $(OBJS) $(OBJS_LIB)$(CC) $^ -o $@ -lcurl# creazione degli object files%.o: %.c$(CC) -MMD -MP -I../include -c $< -o $@ -g -Wall -std=c11 -D SIMULATION# direttive phony.PHONY: clean# pulizia progetto ($(RM) è di default "rm -f")clean:$(RM) $(OBJS) $(OBJS_LIB) $(DEPS) $(DEPS_LIB)# creazione dipendenze-include $(DEPS) $(DEPS_LIB)
Qualche piccolo dettaglio sui blocchi (commentati) che compongono il makefile:
# variabili
Qua si mettono le variabili che vengono usate nel resto del makefile. In particolare la variabile CC indica il compilatore da usare: nel nostro caso è gcc, ma potrebbe essere, per esempio, g++ (per il C++). Ovviamente in questo caso i sorgenti sarebbero dei .cpp o .cc, quindi bisogna ricordarsi di modificare anche le altre variabili che fanno riferimento ai .c.
# creazione del target file eseguibile
Qua si mette il comando per linkare i file oggetto creati e produrre il file eseguibile finale. Se usiamo qualche libreria esterna il riferimento si aggiunge qui (nell'esempio si linka la libcurl usando -lcurl).
# creazione degli object files
Qua si mette il comando per compilare ogni sorgente e creare il file oggetto corrispondente, attivando tutte le opzioni del compilatore che ci servono. Se usiamo qualche #ifdef particolare (come quelle viste la) la attivazione si mette qui (nell'esempio si attiva una define SIMULATION usata nei sorgenti).
# direttive phony
Qua si mettono tutte le direttive phony (è un po' lungo da spiegare: guardate il link, che è chiarissimo).
# pulizia progetto ($(RM) è di default "rm -f")
Qua si mette il comando di cancellazione degli oggetti per, eventualmente, forzare una successiva ricompilazione completa.
# creazione dipendenze
Qua si mette il comando per generare i file di dipendenza che ci permettono di ricompilare solo quello che serve quando modifichiamo un header file.
Il makefile presentato è un esempio reale, pronto all'uso. Ovviamente le direttive -lcurl e -D SIMULATION sono state aggiunte come esempio per indicare come estendere le funzionalità del makefile: se non ci servono possiamo toglierle senza problemi (e aggiungeremo quelle che ci servono usando lo stessa sintassi).
Che ne dite? L'obbiettivo non era di spiegare cosa è un makefile e come si scrive (uff, c'è in rete una documentazione enorme sull'argomento). E neppure era di spiegare i segreti della sintassi (che permette anche soluzioni complesse). L'obbiettivo era di fornire un makefile basico e completo allo stesso tempo, un makefile universale per (quasi) qualsiasi progetto. Io direi che l'obbiettivo è compiuto... poi, se dobbiamo fare progetti complessi e portabili, con auto-installatori, ecc. magari ci troveremo più comodi usando un IDE di buona qualità oppure usando a mano tools come Autotools o CMake... ma vi assicuro che il metodo rapido e vecchia-scuola che ho descritto è usabile sempre e senza limitazioni. Sono soddisfazioni...
Ciao, e al prossimo post!