makefile tutorial. ai generated. 2026-06-11
GNU Make Tutorial: From Zero to Productive
GNU Make is a build automation tool that determines which parts of a large program need to be recompiled and issues the commands to recompile them. It is incredibly useful for C/C++, but also for any project involving files that depend on each other (LaTeX, documentation, websites, etc.).
1. Installation
# Debian/Ubuntu sudo apt install make # macOS (already installed via Xcode Command Line Tools) xcode-select --install # Fedora/RHEL sudo dnf install make # Windows (via MSYS2, WSL, or Chocolatey) choco install make
Verify: make --version
2. Your First Makefile
Create a file named Makefile (or makefile) in your project root.
# Simple C program example program: main.o utils.o gcc -o program main.o utils.o main.o: main.c gcc -c main.c utils.o: utils.c gcc -c utils.c clean: rm -f *.o program
How to run it:
make # builds the default target ("program") make clean # removes generated files make program # explicit target
3. Makefile Syntax Basics
Rule structure:
target ... : prerequisites ... command command
- Target: usually a file to create or a phony name.
- Prerequisites: files the target depends on.
- Commands: must be indented with a tab (not spaces!).
- Make only runs commands if the target is older than any prerequisite.
Comment with #
4. Variables (Macros)
CC = gcc CFLAGS = -Wall -O2 -g LDFLAGS = -lm program: main.o utils.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@
Common automatic variables (very useful):
| Variable | Meaning | Example value |
|---|---|---|
$@ |
Target name | program |
$< |
First prerequisite | main.c |
$^ |
All prerequisites (no duplicates) | main.o utils.o |
$* |
Stem of pattern match | main (for %.o) |
$? |
Prerequisites newer than target | (changed files) |
5. Pattern Rules (The Real Power)
# Generic rule for all .o files from .c files %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # Generic rule for all executables from .o files %: %.o $(CC) -o $@ $^
With these two rules you can often delete most explicit rules.
6. Phony Targets
Tell Make that a target is not a file:
.PHONY: all clean install test all: program clean: rm -f *.o program install: program cp program /usr/local/bin/
Without .PHONY, if you ever create a file named clean, make clean would do nothing.
7. Built-in Variables and Functions
SOURCES = $(wildcard *.c) OBJECTS = $(SOURCES:.c=.o) # substitution reference EXEC = program # Functions HEADERS = $(wildcard *.h) ALL_FILES = $(SOURCES) $(HEADERS) $(EXEC): $(OBJECTS) $(CC) -o $@ $^ # String functions OBJDIR = obj $(OBJECTS): | $(OBJDIR) # order-only prerequisite $(CC) -c $(CFLAGS) $(addprefix -I,$(INCLUDES)) $< -o $@ $(OBJDIR): mkdir -p $@
8. Conditional Parts
ifeq ($(DEBUG),1) CFLAGS += -DDEBUG -g else CFLAGS += -O2 endif ifdef VERBOSE MAKEFLAGS += --debug endif
9. Including Other Makefiles
include config.mk -include deps.mk # don't fail if file missing
10. Complete Modern Example
# ======================================== # Project settings # ======================================== NAME = myapp CC = gcc CFLAGS = -Wall -Wextra -std=c11 -O2 LDFLAGS = SRC_DIR = src OBJ_DIR = obj BIN_DIR = bin SOURCES = $(wildcard $(SRC_DIR)/*.c) OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SOURCES)) # ======================================== # Targets # ======================================== .PHONY: all clean all: $(BIN_DIR)/$(NAME) $(BIN_DIR)/$(NAME): $(OBJECTS) | $(BIN_DIR) $(CC) $(LDFLAGS) -o $@ $^ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c $< -o $@ $(BIN_DIR) $(OBJ_DIR): mkdir -p $@ clean: rm -rf $(OBJ_DIR) $(BIN_DIR) # Automatic dependency generation (advanced but worth it) DEPFLAGS = -MMD -MP CFLAGS += $(DEPFLAGS) -include $(OBJECTS:.o=.d)
11. Useful Command-Line Options
make -j4 # parallel build (4 jobs) make -j$(nproc) # use all CPU cores make -n # dry-run (show commands) make -B # force rebuild everything make -f alt.mk # use different makefile make V=1 # many projects show full commands when V=1
12. Best Practices
- Always use
.PHONYfor non-file targets. - Prefer pattern rules over explicit rules.
- Put object files in a separate directory.
- Generate dependency files with
-MMD -MP. - Use variables for tools and flags (easy to override:
make CFLAGS="-g -fsanitize=address"). - Keep the Makefile readable — future you will thank you.
Quick Reference Cheat Sheet
make→ build first targetmake target→ build specific targetmake -j→ parallel$@,$<,$^→ automatic variables$(wildcard *.c)→ file glob$(patsubst %.c,%.o,$(SRC))→ text substitution.PHONY: clean→ declare phonyinclude file.mk→ include another makefile
Now go create a Makefile in one of your projects and replace your manual build scripts!
Once you're comfortable with the basics, explore advanced topics like:
- Recursive Make (usually discouraged)
- Makefile generators (CMake, Meson)
- Static pattern rules
$(foreach),$(call),$(eval)