Variables in Make store strings (compiler names, flags, file lists). Use VAR = value to define, $(VAR) to expand.
CC = gcc CFLAGS = -O3 -Wall -g SRCS = main.c util.c helper.c OBJS = main.o util.o helper.o app: $(OBJS) $(CC) $(CFLAGS) -o app $(OBJS)
$(CC) expands to gcc, $(CFLAGS) to -O3 -Wall -g. Changing CC once updates every recipe that uses it.
Automatic variables are special—Make sets them for each recipe:
$@ target filename $< first prerequisite $^ all prerequisites (space-separated) $+ all prerequisites (with duplicates)
Example:
main.o: main.c $(CC) $(CFLAGS) -c $< -o $@
$< is main.c, $@ is main.o. The recipe becomes: gcc -O3 -Wall -g -c main.c -o main.o.
Generic rule with automatic variables:
%.o: %.c $(CC) $(CFLAGS) -c $< -o $@
This rule matches any .o depending on .c: main.o from main.c, util.o from util.c, etc. $< is the matching .c file, $@ is the .o file.
Variable assignment modes:
VAR = value # lazy (expands when used) VAR := value # immediate (expands when defined) VAR ?= value # conditional (only if not set) VAR += value # append
= is most common. := is useful when you need the value immediately (e.g., in conditionals).
Built-in variables:
CC C compiler (default: cc) CFLAGS C compiler flags LDFLAGS linker flags LDLIBS libraries to link
Make has defaults for these; override in your Makefile or on the command line: make CFLAGS=-O2.
Command-line variables:
make CFLAGS=-O2 # override in this run make CC=clang # use a different compiler
These take precedence over Makefile definitions.
String functions: Variables can use functions like patsubst (pattern substitution):
SRCS = main.c util.c OBJS = $(patsubst %.c, %.o, $(SRCS)) # main.o util.o
patsubst replaces patterns. More on functions in Makefile Functions.