What is make?
make is a build tool. You tell it targets (things to build), their dependencies (what they need), and recipes (how to build them). It only rebuilds what changed—so multi-file C++ projects compile fast.
A rule looks like:
target: prerequisites
<TAB>recipe command(s)
Code language: HTML, XML (xml)
(Yes, the leading character before each recipe line must be a TAB.)
Single-file example
Files: main.cpp, Makefile
Makefile
# compiler & flags
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic -O2
# default target
all: app
app: main.o
$(CXX) $(CXXFLAGS) -o $@ $^
main.o: main.cpp
$(CXX) $(CXXFLAGS) -c $<
.PHONY: clean run
clean:
$(RM) app app.exe *.o # $(RM) is a built-in "rm -f"
run: app
./app || .\app.exe
Code language: PHP (php)
Use it
make # build
make run # run (Linux/mac: ./app, Windows PowerShell: .\app.exe)
make clean # delete binaries/objects
Code language: PHP (php)
Multi-file C++ with classes
Files: main.cpp, List.h, List.cpp, Makefile
Makefile
# --- config ---------------------------------------------------
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic -O2 -MMD -MP
TARGET := ds-app
# list your source files here
SRCS := main.cpp List.cpp
OBJS := $(SRCS:.cpp=.o)
DEPS := $(OBJS:.o=.d)
# --- rules ----------------------------------------------------
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
# pattern rule: how to compile any .cpp -> .o
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# include auto-generated header dependencies
-include $(DEPS)
.PHONY: clean run debug release
run: $(TARGET)
./$(TARGET) || .\$(TARGET).exe
clean:
$(RM) $(TARGET) $(TARGET).exe $(OBJS) $(DEPS)
# quick build modes
debug: CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic -O0 -g -MMD -MP
debug: clean all
release: CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic -O2 -DNDEBUG -MMD -MP
release: clean all
Code language: PHP (php)
Why the extras?
-MMD -MP+-include $(DEPS)= auto header dependency tracking.
If you changeList.h, only the right files rebuild.- Pattern rule
%.o: %.cppavoids writing a separate rule for every file. debug/releasetargets flip optimization & debug symbols.
Typical project layout
.
├─ include/ # headers (e.g., List.h)
├─ src/ # sources (e.g., List.cpp, main.cpp)
└─ Makefile
Code language: PHP (php)
Adjust the template like this:
INCDIR := include
SRCDIR := src
SRCS := $(SRCDIR)/main.cpp $(SRCDIR)/List.cpp
CPPFLAGS := -I$(INCDIR) # add include path
Code language: PHP (php)
Everyday commands
make # build default target
make -j # build in parallel (faster on multi-core)
make clean # remove build artifacts
make debug # rebuild with -g -O0 (useful for gdb)
make run # build and run
Code language: PHP (php)
Common pitfalls (and fixes)
- “missing separator. Stop.” → Your recipe line starts with spaces. Use a TAB.
- “No rule to make target …” → File path wrong or missing from
SRCS. - Linker errors (undefined reference) → You compiled a class but forgot to include its
.oin$(OBJS)/$(SRCS). - Headers change but nothing rebuilds → Add
-MMD -MPand-include $(DEPS)(already in the template). - Windows notes
- With w64devkit or MSYS you have
rm. If not, replace thecleanrecipe with:clean: del /q $(TARGET).exe *.o *.d 2> NUL || true - Run programs in PowerShell with
.\app.exe.
- With w64devkit or MSYS you have
Tiny starter template (copy/paste)
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic -O2 -MMD -MP
TARGET := program
SRCS := main.cpp Foo.cpp Bar.cpp
OBJS := $(SRCS:.cpp=.o)
DEPS := $(OBJS:.o=.d)
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
-include $(DEPS)
.PHONY: clean run
clean: ; $(RM) $(TARGET) $(TARGET).exe $(OBJS) $(DEPS)
run: $(TARGET) ; ./$(TARGET) || .\$(TARGET).exeCode language: JavaScript (javascript)
Make syntax cheat sheet (variables, assignment, automatic vars, and %)
$(NAME) — variable expansion
$(NAME)inserts the value of variable NAME where it appears.- Same as
${NAME}; prefer$(...)in Makefiles. - Example:
CXX := g++ CXXFLAGS := -std=c++17 all: $(CXX) $(CXXFLAGS) main.cpp -o app - Substitution ref:
$(SRCS:.cpp=.o)replaces suffix.cppwith.oacross the words in$(SRCS).
Tip: In recipe lines (the shell commands), use
$$to pass a literal$to the shell:show: echo $$PATH # make expands $$ → $ so the shell sees $PATHCode language: PHP (php)
:=, =, ?=, += — assignment operators
:=simple (immediate) expansion: right side is expanded now and stored.SRC_DIR := src SRCS := $(SRC_DIR)/a.cpp $(SRC_DIR)/b.cpp # uses current SRC_DIR immediately=recursive (deferred) expansion: right side is expanded when used.SRC_DIR = src SRCS = $(SRC_DIR)/a.cpp SRC_DIR = other # Later, SRCS expands to "other/a.cpp" because it’s deferred.?=set if not already set:CXX ?= g++ # only sets if CXX wasn’t provided (e.g., from env or command line)+=append:CXXFLAGS := -std=c++17 CXXFLAGS += -Wall -Wextra
Rule of thumb: use := for predictable “compute once” values (paths, lists); use = only if you want late binding.
Automatic variables: $@, $^, $< (and friends)
These only have meaning inside a rule’s recipe:
$@→ the target name
e.g., inapp: main.o,$@isapp.$^→ all prerequisites (deduplicated)
e.g., inapp: a.o b.o a.o,$^isa.o b.o.$<→ the first prerequisite (most useful in single-dependency compile rules)
e.g., in%.o: %.cpp,$<is the current.cppfile.- (Nice to know)
$*→ the stem matched by%in a pattern rule. - (Nice to know)
$(basename $@),$(@D),$(@F)→ directory/file parts.
Example putting them together:
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
Code language: JavaScript (javascript)
% — pattern wildcard (and $* the stem)
% matches an arbitrary “stem” in pattern rules and implicit rules.
- Compile-anything pattern:
%.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@If Make needsList.o, it substitutes the stemListand usesList.cppas the prerequisite. %also appears in file lists (rarely needed for beginners), and in advanced functions. The key idea:%is the placeholder that binds to$*(the stem).
Mini example (annotated)
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -MMD -MP
TARGET := ds-app
SRCS := main.cpp List.cpp
OBJS := $(SRCS:.cpp=.o) # substitution ref
DEPS := $(OBJS:.o=.d)
all: $(TARGET)
$(TARGET): $(OBJS) # target has prerequisites
$(CXX) $(CXXFLAGS) -o $@ $^ # $@=ds-app, $^=all .o files
%.o: %.cpp # % is the pattern; stem binds to $*
$(CXX) $(CXXFLAGS) -c $< -o $@ # $< = first prereq (the .cpp)
-include $(DEPS)
.PHONY: clean
clean: ; $(RM) $(TARGET) $(TARGET).exe $(OBJS) $(DEPS)Code language: PHP (php)
