Skip to content

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

  1. Always use .PHONY for non-file targets
  2. Use := for variables that don't need recursion (faster)
  3. Prefix commands with @ to suppress echo when appropriate
  4. Use prefix to ignore errors selectively
  5. Generate dependency files for C/C++ projects
  6. Use pattern rules instead of suffix rules
  7. Use $(MAKE) instead of make for recursive calls
  8. Create directories with order-only prerequisites: | $(DIR)
  9. Quote variables that might contain spaces
  10. Use $(shell ...) for external commands