GNU Make è un build system che automatizza la compilazione leggendo un file chiamato Makefile (o makefile, GNUmakefile). Determina quali parti di un programma devono essere ricompilate ed esegue i comandi necessari basandosi su regole, dipendenze e timestamp dei file.
| Comando | Descrizione |
|---|---|
make | Esegue il primo target del Makefile |
make target | Esegue uno specifico target |
make -f file.mk | Usa un Makefile con nome diverso |
make -j4 | Compilazione parallela (4 job) |
make -jN | N job paralleli (-j senza N = illimitati) |
make -n | Dry run: mostra comandi senza eseguirli |
make -B | Forza ricompilazione di tutto (unconditional) |
make -k | Continua anche dopo errori |
make -s | Silent: non stampa i comandi |
make -C dir | Cambia directory prima di leggere il Makefile |
make VAR=value | Override di una variabile dalla command line |
make -p | Stampa il database interno (regole e variabili) |
make -d | Debug mode: output dettagliato |
make --debug=basic | Debug selettivo (basic, verbose, implicit, jobs, all) |
make -t | Touch: aggiorna timestamp senza compilare |
make -q | Query: exit 0 se il target è aggiornato, 1 altrimenti |
# Make cerca in questo ordine: 1. GNUmakefile 2. makefile 3. Makefile # ← convenzione più usata
Makefile (con la M maiuscola) perché appare prima nei listing di directory ordinati (le maiuscole precedono le minuscole in ASCII). GNUmakefile è sconsigliato perché non portabile verso altre implementazioni di Make.Una regola è l'unità fondamentale di un Makefile. Definisce un target (cosa produrre), le sue dipendenze (prerequisiti) e una recipe (comandi da eseguire). Make ricompila un target solo se è più vecchio di almeno una delle sue dipendenze.
target: dipendenza1 dipendenza2 ... comando1 comando2 # IMPORTANTE: i comandi DEVONO essere indentati con TAB, non spazi! # Esempio concreto: prog: main.o utils.o gcc main.o utils.o -o prog main.o: main.c main.h gcc -c main.c utils.o: utils.c utils.h gcc -c utils.c
# Più target con la stessa regola file1.o file2.o: common.h # Equivale a: file1.o: common.h file2.o: common.h # Dipendenze aggiuntive (si accumulano) main.o: main.c main.o: config.h utils.h # main.o dipende da main.c, config.h E utils.h
# I prerequisiti dopo | non influenzano il timestamp # Usati tipicamente per creare directory build/prog.o: prog.c | build gcc -c prog.c -o build/prog.o build: mkdir -p build # La directory viene creata se non esiste, # ma modificarla non forza la ricompilazione
make senza argomenti. Convenzione: chiamarlo all.Un target phony non corrisponde a un file reale. Senza .PHONY, se esistesse un file chiamato clean, Make lo considererebbe già aggiornato e non eseguirebbe la recipe.
.PHONY: all clean install uninstall rebuild test all: prog clean: rm -f *.o prog install: prog install -m 755 prog /usr/local/bin/ uninstall: rm -f /usr/local/bin/prog rebuild: clean all test: prog ./prog --test
| Target | Convenzione |
|---|---|
all | Compila tutto (default goal) |
clean | Rimuove file generati (.o, eseguibili) |
install | Installa il programma nel sistema |
uninstall | Rimuove l'installazione |
dist | Crea un archivio per la distribuzione |
distclean | clean + rimuove file di configurazione |
check / test | Esegue i test |
help | Mostra i target disponibili |
Le variabili rendono il Makefile flessibile e manutenibile. Si referenziano con $(VAR) o ${VAR}. Per convenzione i nomi sono in MAIUSCOLO.
CC = gcc # compilatore C CXX = g++ # compilatore C++ CFLAGS = -Wall -Wextra -g # flag compilazione C CXXFLAGS= -Wall -Wextra -g # flag compilazione C++ LDFLAGS = -L/usr/local/lib # flag linker (percorsi) LDLIBS = -lm -lpthread # librerie da linkare CPPFLAGS= -I./include -DDEBUG # flag preprocessore AR = ar # archiviatore ARFLAGS = rcs # flag per ar TARGET = prog SRC = main.c utils.c io.c OBJ = $(SRC:.c=.o) # main.o utils.o io.o $(TARGET): $(OBJ) $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS)
| Variabile | Default | Uso |
|---|---|---|
CC | cc | Compilatore C |
CXX | g++ | Compilatore C++ |
CFLAGS | (vuoto) | Flag per $(CC) |
CXXFLAGS | (vuoto) | Flag per $(CXX) |
CPPFLAGS | (vuoto) | Flag preprocessore (-I, -D) |
LDFLAGS | (vuoto) | Flag linker (-L) |
LDLIBS | (vuoto) | Librerie (-l) |
AR | ar | Archiviatore |
RM | rm -f | Comando di rimozione |
MAKE | make | Riferimento ricorsivo a make |
SHELL | /bin/sh | Shell usata per le recipe |
MAKEFLAGS | (vuoto) | Flag passate a sub-make |
.c → .o esegue: $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@. Basta definire le variabili e Make sa già come compilare.Le variabili automatiche sono settate da Make per ogni regola eseguita. Disponibili solo all'interno delle recipe.
| Variabile | Significato | Esempio |
|---|---|---|
$@ | Il target | prog |
$< | La prima dipendenza | main.c |
$^ | Tutte le dipendenze (senza duplicati) | main.o utils.o |
$+ | Tutte le dipendenze (con duplicati) | main.o utils.o main.o |
$? | Dipendenze più recenti del target | file modificati dopo l'ultimo build |
$* | Lo stem del pattern match | main in %.o: %.c |
$(@D) | Directory del target | build in build/prog |
$(@F) | Filename del target | prog in build/prog |
$(<D) | Directory della prima dipendenza | src in src/main.c |
$(<F) | Filename della prima dipendenza | main.c in src/main.c |
# Senza variabili automatiche (ripetitivo) prog: main.o utils.o gcc main.o utils.o -o prog # Con variabili automatiche (generico e manutenibile) prog: main.o utils.o $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) # $@ = prog, $^ = main.o utils.o # Pattern rule con $< e $@ %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ # $< = il file .c corrispondente, $@ = il file .o target
| Operatore | Nome | Comportamento |
|---|---|---|
= | Recursive | Espansione ritardata (lazy): il valore viene espanso ogni volta che la variabile è usata |
:= | Simple | Espansione immediata: il valore viene espanso al momento dell'assegnazione |
::= | Simple (POSIX) | Identico a :=, sintassi POSIX standard |
?= | Conditional | Assegna solo se la variabile non è già definita |
+= | Append | Aggiunge al valore esistente (separato da spazio) |
!= | Shell | Assegna l'output di un comando shell (GNU Make 4.0+) |
# Recursive (=) — espansione ritardata A = $(B) B = hello # $(A) vale "hello" — B viene espanso quando si usa A # Simple (:=) — espansione immediata A := $(B) B := hello # $(A) vale "" — B era vuoto quando A è stato assegnato # Conditional (?=) CC ?= gcc # CC = gcc solo se non già definito (es. da ambiente o command line) # Append (+=) CFLAGS = -Wall CFLAGS += -Wextra CFLAGS += -g # CFLAGS = "-Wall -Wextra -g" # Shell (!=) GIT_HASH != git rev-parse --short HEAD # Equivalente a: GIT_HASH := $(shell git rev-parse --short HEAD)
:= per la maggior parte delle variabili (più prevedibile e veloce). Usare = solo quando serve espansione ritardata. Usare ?= per dare default sovrascrivibili dall'utente.| # | Origine | Esempio |
|---|---|---|
1 | override nel Makefile | override CFLAGS += -g |
2 | Command line | make CFLAGS="-O2" |
3 | Makefile | CFLAGS = -Wall |
4 | Ambiente | export CFLAGS="-O3" |
5 | Default di Make | CC = cc |
# Senza override: "make CFLAGS=-O2" sovrascrive il Makefile CFLAGS = -Wall -g # Con override: -g viene SEMPRE aggiunto, anche con override da CLI override CFLAGS += -g # Esportare variabili ai sub-make export CC CFLAGS LDFLAGS # Esportare tutto export # Non esportare una variabile specifica unexport SECRET_KEY
# Variabili che valgono solo per un target (e le sue dipendenze) debug: CFLAGS += -DDEBUG -O0 debug: prog release: CFLAGS += -O2 -DNDEBUG release: prog # Ora: "make debug" compila con -DDEBUG -O0 # "make release" compila con -O2 -DNDEBUG
Le pattern rules usano % come wildcard per definire regole generiche applicabili a più file. Sostituiscono le vecchie suffix rules (.c.o:).
# Regola generica: qualsiasi .c → .o %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ # Regola generica: qualsiasi .cpp → .o %.o: %.cpp $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ # Con directory di output build/%.o: src/%.c | build $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
| Pattern | Comando implicito |
|---|---|
%.o da %.c | $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ |
%.o da %.cpp | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ |
% da %.o | $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ |
%.o da %.s | $(AS) $(ASFLAGS) $< -o $@ |
# Applica il pattern solo a un elenco specifico di target OBJS := main.o utils.o io.o $(OBJS): %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # Sintassi: targets: target-pattern: prereq-pattern # Utile quando hai più pattern rules e vuoi evitare ambiguità
# Disabilitare una regola implicita specifica %.o: %.s # Disabilitare TUTTE le regole implicite MAKEFLAGS += --no-builtin-rules .SUFFIXES:
GNU Make offre numerose funzioni richiamabili con $(funzione argomenti) o ${funzione argomenti}.
| Funzione | Descrizione | Esempio |
|---|---|---|
$(subst da,a,testo) | Sostituzione testuale | $(subst .c,.o,main.c) → main.o |
$(patsubst pat,repl,testo) | Sostituzione con pattern | $(patsubst %.c,%.o,main.c utils.c) |
$(strip testo) | Rimuove spazi iniziali/finali e duplicati | $(strip a b ) → a b |
$(findstring needle,testo) | Cerca una stringa | Vuoto se non trovata |
$(filter pattern,testo) | Filtra parole che matchano | $(filter %.c,main.c lib.h) → main.c |
$(filter-out pattern,testo) | Esclude parole che matchano | $(filter-out %.h,main.c lib.h) → main.c |
$(sort lista) | Ordina e rimuove duplicati | $(sort c a b a) → a b c |
$(word n,testo) | Ennesima parola (1-based) | $(word 2,a b c) → b |
$(words testo) | Numero di parole | $(words a b c) → 3 |
$(firstword testo) | Prima parola | $(firstword a b) → a |
$(lastword testo) | Ultima parola | $(lastword a b c) → c |
| Funzione | Descrizione | Esempio |
|---|---|---|
$(dir nomi) | Parte directory | $(dir src/main.c) → src/ |
$(notdir nomi) | Parte filename | $(notdir src/main.c) → main.c |
$(suffix nomi) | Estensione | $(suffix main.c) → .c |
$(basename nomi) | Senza estensione | $(basename main.c) → main |
$(addsuffix sfx,nomi) | Aggiunge suffisso | $(addsuffix .c,main utils) → main.c utils.c |
$(addprefix pfx,nomi) | Aggiunge prefisso | $(addprefix src/,main.c) → src/main.c |
$(join lista1,lista2) | Concatena parola per parola | $(join a b,1 2) → a1 b2 |
$(wildcard pattern) | Glob sul filesystem | $(wildcard src/*.c) |
$(realpath nomi) | Percorso assoluto canonico | Risolve symlink |
$(abspath nomi) | Percorso assoluto | Non risolve symlink |
# foreach DIRS := src lib test SRCS := $(foreach d,$(DIRS),$(wildcard $(d)/*.c)) # if (espande then se condizione non vuota) DEBUG_FLAGS := $(if $(DEBUG),-g -O0,-O2) # or (primo valore non vuoto) CC := $(or $(CUSTOM_CC),gcc) # and (ultimo valore se tutti non vuoti, altrimenti vuoto) RESULT := $(and $(CC),$(CFLAGS),ok) # call (chiama una funzione definita dall'utente) to_upper = $(shell echo $(1) | tr a-z A-Z) NAME := $(call to_upper,hello) # HELLO # shell (esegue un comando e cattura stdout) DATE := $(shell date +%Y-%m-%d) FILES := $(shell find src -name '*.c') # value (valore non espanso di una variabile) RAW := $(value CC) # eval (espande e valuta come sintassi Makefile) $(eval $(call genera_regola,modulo1)) # error / warning / info $(error Messaggio fatale: build interrotto) $(warning Attenzione: variabile non settata) $(info Build avviato alle $(DATE))
# ifeq / ifneq — confronto tra stringhe ifeq ($(CC),gcc) CFLAGS += -Wno-unused-result else ifeq ($(CC),clang) CFLAGS += -Wno-unused-command-line-argument else CFLAGS += -W endif # ifdef / ifndef — verifica se la variabile è definita ifdef DEBUG CFLAGS += -g -O0 -DDEBUG else CFLAGS += -O2 -DNDEBUG endif # Uso dalla riga di comando: make DEBUG=1 # Confronto con stringa vuota ifeq ($(VERBOSE),) Q := @ # silenzia i comandi else Q := # mostra i comandi endif %.o: %.c $(Q)$(CC) $(CFLAGS) -c $< -o $@
UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) LDLIBS += -lrt -lpthread endif ifeq ($(UNAME_S),Darwin) CC := clang LDLIBS += -framework CoreFoundation endif # Rilevamento Windows (MinGW / MSYS) ifdef COMSPEC RM = del /Q EXE = .exe else RM = rm -f EXE = endif
# Include un altro Makefile include config.mk include $(wildcard deps/*.d) # -include (o sinclude): non dà errore se il file non esiste -include $(OBJ:.o=.d) # Uso tipico: file .d per dipendenze automatiche (vedi sezione dedicata) # Uso tipico: config.mk per variabili specifiche dell'ambiente
# Makefile principale che invoca sub-make SUBDIRS := src lib tests .PHONY: all clean $(SUBDIRS) all: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ # Dipendenze tra sottodirectory src: lib tests: src clean: for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean; done
# Struttura: # src/main.c src/utils.c # lib/parser.c lib/lexer.c # build/ (output) SRCDIR := src LIBDIR := lib BUILDDIR:= build SRC := $(wildcard $(SRCDIR)/*.c) $(wildcard $(LIBDIR)/*.c) OBJ := $(patsubst %.c,$(BUILDDIR)/%.o,$(notdir $(SRC))) TARGET := $(BUILDDIR)/prog VPATH := $(SRCDIR) $(LIBDIR) $(TARGET): $(OBJ) | $(BUILDDIR) $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(BUILDDIR)/%.o: %.c | $(BUILDDIR) $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(BUILDDIR): mkdir -p $@
# VPATH: lista di directory dove cercare i prerequisiti VPATH = src:lib:include # vpath (minuscolo): più selettivo, per pattern vpath %.c src lib vpath %.h include # Con vpath Make cerca automaticamente i .c nelle directory specificate # quando un prerequisito non viene trovato nella directory corrente
$(MAKE) -C subdir) è più semplice ma può avere problemi con le dipendenze tra directory. Il non-recursive (singolo Makefile) è più efficiente e corretto ma più complesso da gestire.| Prefisso | Significato |
|---|---|
@ | Silenzia il comando (non viene stampato) |
- | Ignora errori (continua anche se il comando fallisce) |
+ | Esegue anche con -n (dry run) — usato per sub-make |
clean: @echo "Pulizia in corso..." # @ = non stampare il comando -rm -f *.o # - = non fallire se non ci sono .o @echo "Fatto." submake: +$(MAKE) -C subdir # + = esegui anche con make -n
# Ogni riga della recipe è eseguita in una shell separata! bad: cd subdir ls # NON lista subdir! Il cd si è perso # Soluzione 1: una riga sola con ; good1: cd subdir; ls # Soluzione 2: backslash per continuazione good2: cd subdir && \ ls && \ pwd # Soluzione 3: .ONESHELL (GNU Make 3.82+) .ONESHELL: good3: cd subdir ls pwd
# $ in una recipe: bisogna raddoppiare per la shell list: for f in *.c; do echo $$f; done # $$ diventa $ per la shell, $f è la variabile shell # Un singolo $ verrebbe interpretato da Make # Accedere a variabili d'ambiente nella recipe info: @echo "User: $$USER" @echo "CC: $(CC)"
Generare manualmente le dipendenze degli header è fragile e tedioso. GCC/Clang possono generare file .d con le dipendenze, che Make include automaticamente.
CC := gcc CFLAGS := -Wall -g DEPFLAGS= -MMD -MP SRC := $(wildcard *.c) OBJ := $(SRC:.c=.o) DEP := $(SRC:.c=.d) prog: $(OBJ) $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ -include $(DEP) clean: $(RM) $(OBJ) $(DEP) prog
| Flag | Descrizione |
|---|---|
-M | Genera tutte le dipendenze (incluse system headers) |
-MM | Genera dipendenze escludendo system headers |
-MD | -M + compila normalmente + scrive in .d |
-MMD | -MM + compila normalmente + scrive in .d |
-MP | Aggiunge target phony per ogni header (evita errori se l'header viene rimosso) |
-MF file | Scrive le dipendenze nel file specificato |
-MT target | Specifica il target nella regola generata |
# main.d generato da gcc -MMD -MP main.c main.o: main.c main.h utils.h config.h main.h: utils.h: config.h: # Le regole vuote (-MP) evitano errori se un header viene cancellato
-MP è importante: senza di esso, se si cancella un header file referenziato nel .d, Make dà errore perché non trova il prerequisito. Con -MP, ogni header ha un target phony che evita l'errore.| Comando | Descrizione |
|---|---|
make -n | Dry run: mostra cosa farebbe senza eseguire |
make -d | Debug completo (molto verboso) |
make --debug=basic | Debug leggero: mostra quali target vengono ricompilati e perché |
make --debug=verbose | Come basic + file cercati per regole implicite |
make --debug=implicit | Solo ricerca regole implicite |
make --debug=jobs | Informazioni sui job paralleli |
make -p | Stampa database interno (tutte le regole e variabili) |
make -p -f /dev/null | Solo regole built-in (senza leggere il Makefile) |
make --warn-undefined-variables | Avvisa se una variabile non è definita |
# Stampare il valore di una variabile $(info CFLAGS = $(CFLAGS)) $(warning SRC = $(SRC)) # come info ma con file:linea # Target di debug print-%: @echo '$* = $($*)' # Uso: make print-CFLAGS make print-SRC make print-OBJ # Ispezionare l'origine di una variabile $(info origin CC = $(origin CC)) # Possibili valori: undefined, default, environment, # file, command line, override, automatic # Ispezionare il flavor di una variabile $(info flavor CFLAGS = $(flavor CFLAGS)) # Possibili valori: undefined, recursive, simple
# ERRORE: *** missing separator. Stop. # Causa: spazi invece di TAB nella recipe # Fix: usare TAB reali (non soft-tab del tuo editor) # ERRORE: No rule to make target 'file.h' # Causa: file .d obsoleto riferisce un header cancellato # Fix: aggiungere -MP alle DEPFLAGS, oppure make clean # ERRORE: circular dependency dropped # Causa: un target dipende (direttamente o indirettamente) da sé stesso # Il target non viene ricompilato nonostante modifiche # Causa: dipendenze incomplete (manca un header nelle dipendenze) # Fix: usare le dipendenze automatiche (-MMD -MP)
# ── Progetto C con dipendenze automatiche, build dir, debug/release ── # Compilatore e flag CC := gcc CFLAGS := -Wall -Wextra -Wpedantic -std=c17 CPPFLAGS := -I./include LDFLAGS := LDLIBS := -lm DEPFLAGS = -MMD -MP -MF $(DEPDIR)/$*.d # Directory SRCDIR := src BUILDDIR := build DEPDIR := $(BUILDDIR)/deps # File SRC := $(wildcard $(SRCDIR)/*.c) OBJ := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRC)) DEP := $(patsubst $(SRCDIR)/%.c,$(DEPDIR)/%.d,$(SRC)) TARGET := $(BUILDDIR)/prog # Debug/Release ifdef DEBUG CFLAGS += -g3 -O0 -DDEBUG else CFLAGS += -O2 -DNDEBUG endif # ── Target principali ── .PHONY: all clean rebuild help all: $(TARGET) $(TARGET): $(OBJ) | $(BUILDDIR) $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) @echo "Build completato: $@" $(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR) $(DEPDIR) $(CC) $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ # ── Directory ── $(BUILDDIR) $(DEPDIR): mkdir -p $@ # ── Pulizia ── clean: $(RM) -r $(BUILDDIR) rebuild: clean all # ── Help ── help: @echo "Target disponibili:" @echo " all - Compila il progetto (default)" @echo " clean - Rimuove i file generati" @echo " rebuild - Clean + build" @echo " help - Mostra questo messaggio" @echo "" @echo "Variabili:" @echo " DEBUG=1 - Compila con simboli di debug" @echo " CC=clang - Usa un compilatore diverso" # ── Dipendenze automatiche ── -include $(DEP)
LIB := libutils.a LIB_SRC := $(wildcard lib/*.c) LIB_OBJ := $(LIB_SRC:.c=.o) $(LIB): $(LIB_OBJ) $(AR) $(ARFLAGS) $@ $^ # Linkare il programma alla libreria prog: main.o $(LIB) $(CC) $(LDFLAGS) main.o -L. -lutils -o $@
SHLIB := libutils.so SHLIB_SRC := $(wildcard lib/*.c) SHLIB_OBJ := $(SHLIB_SRC:.c=.o) # Compilare con -fPIC per shared library lib/%.o: lib/%.c $(CC) $(CFLAGS) -fPIC -c $< -o $@ $(SHLIB): $(SHLIB_OBJ) $(CC) -shared $^ -o $@ # Linkare ed eseguire prog: main.o $(SHLIB) $(CC) $(LDFLAGS) main.o -L. -lutils -o $@ # Esecuzione: LD_LIBRARY_PATH=. ./prog
| Target | Descrizione |
|---|---|
.PHONY | I target elencati non corrispondono a file reali |
.SUFFIXES | Definisce le suffix rules (svuotare per disabilitare: .SUFFIXES:) |
.DEFAULT | Recipe per target senza regola |
.PRECIOUS | Non cancellare i file elencati in caso di errore o interruzione |
.INTERMEDIATE | I file elencati vengono cancellati automaticamente dopo l'uso |
.SECONDARY | Come .INTERMEDIATE ma non vengono cancellati |
.DELETE_ON_ERROR | Cancella il target se la recipe fallisce |
.ONESHELL | Esegue tutta la recipe in una singola shell |
.SILENT | Non stampare i comandi (come @ su ogni riga) |
.EXPORT_ALL_VARIABLES | Esporta tutte le variabili ai sub-processi |
# Sopprimere "Nothing to be done" e "is up to date" all: prog @: # : = comando nullo della shell # Forzare la ricompilazione di un target .PHONY: FORCE FORCE: version.o: FORCE # sempre ricompilato # Stampare un separatore colorato build: $(TARGET) @printf '\033[32m=== Build OK ===\033[0m\n' # Creare automaticamente le directory per gli output $(BUILDDIR)/%.o: %.c @mkdir -p $(@D) $(CC) $(CFLAGS) -c $< -o $@ # Target help auto-documentante help: ## Mostra questo messaggio @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' # Timestamp di build CPPFLAGS += -DBUILD_DATE='"$(shell date)"' CPPFLAGS += -DGIT_HASH='"$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)"' # Check se un programma è installato check-deps: @command -v $(CC) >/dev/null 2>&1 || { echo "$(CC) non trovato"; exit 1; } # Compilazione condizionale con feature flags ifdef WITH_SSL CPPFLAGS += -DWITH_SSL LDLIBS += -lssl -lcrypto endif
| Cosa | Comando / Sintassi |
|---|---|
| Compilare tutto | make oppure make all |
| Pulire | make clean |
| Build parallelo | make -j$(nproc) |
| Dry run | make -n |
| Debug build | make DEBUG=1 |
| Altro compilatore | make CC=clang |
| Verboso | make VERBOSE=1 o make V=1 |
| Valore variabile | make print-CFLAGS (con target print-%) |
| Rebuild forzato | make -B |