Make
Makefile Cheat Sheet
Basic Syntax
target: prerequisites
command
command
- Target: The file to be created or action to perform
- Prerequisites: Files that must exist before the target can be built
- Commands: Shell commands (must be indented with a TAB, not spaces)
Simple Example
hello: hello.c
gcc -o hello hello.c
clean:
rm -f hello
Variables
# Variable definition
CC = gcc
CFLAGS = -Wall -g
SOURCES = main.c util.c
OBJECTS = $(SOURCES:.c=.o)
# Using variables
program: $(OBJECTS)
$(CC) $(CFLAGS) -o program $(OBJECTS)
# Immediate assignment (evaluated immediately)
VAR := value
# Recursive assignment (evaluated when used)
VAR = value
# Conditional assignment (only if not already set)
VAR ?= value
# Append to variable
VAR += more_value
Automatic Variables
$@ # Target name
$< # First prerequisite
$^ # All prerequisites
$? # Prerequisites newer than target
$* # Stem of pattern rule match
$(@D) # Directory part of target
$(@F) # File part of target
# Example
%.o: %.c
$(CC) -c $< -o $@
Pattern Rules
# Compile all .c files to .o files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Convert .md to .html
%.html: %.md
markdown $< > $@
# Multiple patterns
%: %.c
$(CC) $(CFLAGS) $< -o $@
Phony Targets
.PHONY: all clean install test
all: program1 program2
clean:
rm -f *.o program1 program2
install: all
cp program1 /usr/local/bin/
cp program2 /usr/local/bin/
test: all
./program1 --test
./program2 --test
Conditional Statements
# ifeq / ifneq
ifeq ($(CC),gcc)
CFLAGS += -Wextra
endif
ifneq ($(DEBUG),)
CFLAGS += -DDEBUG
endif
# ifdef / ifndef
ifdef PRODUCTION
CFLAGS += -O2
else
CFLAGS += -g
endif
ifndef VERSION
VERSION = 1.0
endif
Functions
# String substitution
SOURCES = main.c util.c
OBJECTS = $(SOURCES:.c=.o)
# or
OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
# Wildcard
SOURCES = $(wildcard *.c)
# Shell command
CURRENT_DIR = $(shell pwd)
GIT_HASH = $(shell git rev-parse --short HEAD)
# Filter
OBJS = main.o test.o util.o
MAIN_OBJS = $(filter-out test.o,$(OBJS))
# Directory/basename
DIRS = $(dir src/main.c src/util.c) # src/ src/
FILES = $(notdir src/main.c lib/util.c) # main.c util.c
# Foreach
DIRS = src lib bin
ALL_SOURCES = $(foreach dir,$(DIRS),$(wildcard $(dir)/*.c))
# Substitution reference
$(VAR:old=new) # Replace old with new in VAR
# String functions
$(subst from,to,text) # Substitute
$(patsubst pattern,replacement,text)
$(strip string) # Remove whitespace
$(findstring find,in) # Find substring
$(filter pattern,text) # Filter matching words
$(filter-out pattern,text) # Filter non-matching
$(sort list) # Sort and remove duplicates
$(word n,text) # Get nth word
$(wordlist s,e,text) # Get words s to e
$(words text) # Count words
$(firstword text) # Get first word
$(lastword text) # Get last word
Dependencies
# Simple dependencies
program: main.o util.o helper.o
$(CC) -o $@ $^
main.o: main.c util.h
util.o: util.c util.h
helper.o: helper.c
# Auto-generate dependencies
%.d: %.c
@$(CC) -MM $(CFLAGS) $< > $@.$$$$; \\
sed 's,\\($*\\)\\.o[ :]*,\\1.o $@ : ,g' < $@.$$$$ > $@; \\
rm -f $@.$$$$
-include $(SOURCES:.c=.d)
Special Targets
.PHONY: target # Target is not a file
.SILENT: target # Don't echo commands
.IGNORE: target # Ignore errors
.PRECIOUS: %.o # Don't delete intermediate files
.INTERMEDIATE: %.tmp # Delete after build
.SECONDARY: %.o # Don't delete but don't rebuild unnecessarily
.DELETE_ON_ERROR: # Delete target if command fails
.DEFAULT: # Default rule for unknown targets
@echo "No rule for $@"
.SUFFIXES: # Clear suffix list
.SUFFIXES: .c .o # Define suffix list
Command Modifiers
target:
@echo "Silent command (no echo)"
-rm file.txt # Ignore errors
+make other # Always execute (even with -n)
Multiple Targets
# Multiple targets with same recipe
file1 file2 file3: dependency
command
# Grouped targets (all built together)
file1 file2 file3: &: dependency
command-that-builds-all-three
Include Files
include config.mk
include *.mk
-include optional.mk # Don't error if missing
Recursive Make
SUBDIRS = src lib test
all:
for dir in $(SUBDIRS); do \\
$(MAKE) -C $$dir; \\
done
# Better approach
all:
$(MAKE) -C src
$(MAKE) -C lib
$(MAKE) -C test
Common Patterns
Standard C/C++ Project
CC = gcc
CFLAGS = -Wall -Wextra -std=c11 -O2
LDFLAGS =
LIBS = -lm
TARGET = myprogram
SOURCES = $(wildcard *.c)
OBJECTS = $(SOURCES:.c=.o)
DEPENDS = $(SOURCES:.c=.d)
.PHONY: all clean install
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%.d: %.c
@$(CC) -MM $(CFLAGS) $< > $@
clean:
rm -f $(OBJECTS) $(DEPENDS) $(TARGET)
install: $(TARGET)
install -m 0755 $(TARGET) /usr/local/bin/
-include $(DEPENDS)
Multi-Directory Project
SRC_DIR = src
BUILD_DIR = build
BIN_DIR = bin
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
TARGET = $(BIN_DIR)/program
all: $(TARGET)
$(TARGET): $(OBJECTS) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR) $(BIN_DIR):
mkdir -p $@
clean:
rm -rf $(BUILD_DIR) $(BIN_DIR)
.PHONY: all clean
Debugging
# Print variable value
$(info VAR = $(VAR))
$(warning This is a warning)
$(error This is an error - stops make)
# Debug target
debug:
@echo "SOURCES: $(SOURCES)"
@echo "OBJECTS: $(OBJECTS)"
@echo "CFLAGS: $(CFLAGS)"
Command Line Usage
make # Build default target
make target # Build specific target
make -n # Dry run (show commands)
make -B # Force rebuild all
make -j4 # Parallel build (4 jobs)
make -C directory # Run in directory
make VAR=value # Override variable
make --debug # Debug mode
make -f other.mk # Use different makefile
Best Practices
- Always use
.PHONY for non-file targets
- Use
:= for variables that don't need recursion (faster)
- Prefix commands with
@ to suppress echo when appropriate
- Use prefix to ignore errors selectively
- Generate dependency files for C/C++ projects
- Use pattern rules instead of suffix rules
- Use
$(MAKE) instead of make for recursive calls
- Create directories with order-only prerequisites:
| $(DIR)
- Quote variables that might contain spaces
- Use
$(shell ...) for external commands