Make Cheatsheet
Overview
GNU Make is a build automation tool that automatically builds executable programs and libraries from source code by reading files called Makefiles which specify how to derive the target program.
Basic Concepts
Makefile Structure
target: dependencies
command
command
Key Terms
Target # File to be created or action to be performed
Dependency # Files that target depends on
Recipe # Commands to create target
Rule # Target + dependencies + recipe
Variable # Named value that can be reused
Basic Makefile
Simple Example
# Simple C program build
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
With Variables
CC = gcc
CFLAGS = -Wall -g
OBJECTS = main.o utils.o
TARGET = program
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)
main.o: main.c
$(CC) $(CFLAGS) -c main.c
utils.o: utils.c
$(CC) $(CFLAGS) -c utils.c
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: clean
Variables
Variable Definition
# Simple assignment
CC = gcc
CFLAGS = -Wall -g
# Recursive assignment (evaluated when used)
SOURCES = $(wildcard *.c)
# Simple assignment (evaluated immediately)
OBJECTS := $(SOURCES:.c=.o)
# Conditional assignment (only if not already defined)
CC ?= gcc
# Append to variable
CFLAGS += -O2
Built-in Variables
# Automatic variables
$@ # Target name
# Make Cheatsheet
## Overview
GNU Make is a build automation tool that automatically builds executable programs and libraries from source code by reading files called Makefiles which specify how to derive the target program.
## Basic Concepts
### Makefile Structure
```makefile
target: dependencies
command
command
Key Terms
Target # File to be created or action to be performed
Dependency # Files that target depends on
Recipe # Commands to create target
Rule # Target + dependencies + recipe
Variable # Named value that can be reused
Basic Makefile
Simple Example
# Simple C program build
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
With Variables
CC = gcc
CFLAGS = -Wall -g
OBJECTS = main.o utils.o
TARGET = program
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)
main.o: main.c
$(CC) $(CFLAGS) -c main.c
utils.o: utils.c
$(CC) $(CFLAGS) -c utils.c
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: clean
Variables
Variable Definition
# Simple assignment
CC = gcc
CFLAGS = -Wall -g
# Recursive assignment (evaluated when used)
SOURCES = $(wildcard *.c)
# Simple assignment (evaluated immediately)
OBJECTS := $(SOURCES:.c=.o)
# Conditional assignment (only if not already defined)
CC ?= gcc
# Append to variable
CFLAGS += -O2
Built-in Variables
`< # First dependency $^ # All dependencies $? # Dependencies newer than target $* # Stem of pattern rule match
Example usage
%.o: %.c $(CC) $(CFLAGS) -c $< -o $@
### Environment Variables
```makefile
# Use environment variables
HOME_DIR = $(HOME)
PATH_VAR = $(PATH)
# Override environment variables
export CC = gcc
export CFLAGS = -Wall
Pattern Rules
Basic Patterns
# Pattern rule for C files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pattern rule for C++ files
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# Multiple patterns
%.o: %.c %.h
$(CC) $(CFLAGS) -c $< -o $@
Advanced Patterns
# Static pattern rule
$(OBJECTS): %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pattern with subdirectories
build/%.o: src/%.c
@mkdir -p build
$(CC) $(CFLAGS) -c $< -o $@
Functions
String Functions
# Substitution
SOURCES = main.c utils.c
OBJECTS = $(SOURCES:.c=.o)
# or
OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
# Word functions
FIRST_WORD = $(firstword $(SOURCES))
WORD_COUNT = $(words $(SOURCES))
NTH_WORD = $(word 2,$(SOURCES))
# Filter functions
C_FILES = $(filter %.c,$(SOURCES))
NOT_MAIN = $(filter-out main.c,$(SOURCES))
File Functions
# Wildcard
SOURCES = $(wildcard src/*.c)
HEADERS = $(wildcard include/*.h)
# Directory functions
SRC_DIRS = $(dir $(SOURCES))
BASE_NAMES = $(notdir $(SOURCES))
BASENAMES = $(basename $(SOURCES))
SUFFIXES = $(suffix $(SOURCES))
# Path manipulation
ABS_PATH = $(abspath relative/path)
REAL_PATH = $(realpath symlink/path)
Conditional Functions
# If function
RESULT = $(if $(DEBUG),debug,release)
# Or function
COMPILER = $(or $(CC),gcc)
# And function
VALID = $(and $(CC),$(SOURCES))
Conditional Statements
ifeq/ifneq
ifeq ($(DEBUG),1)
CFLAGS += -g -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
endif
ifneq ($(OS),Windows_NT)
CFLAGS += -fPIC
endif
ifdef/ifndef
ifdef DEBUG
CFLAGS += -g
endif
ifndef CC
CC = gcc
endif
Conditional Assignment
# Set if not already defined
CC ?= gcc
# Set based on condition
CFLAGS = $(if $(DEBUG),-g -O0,-O2)
Advanced Features
Include Files
# Include other makefiles
include config.mk
include $(wildcard *.d)
# Silent include (no error if file doesn't exist)
-include optional.mk
Dependency Generation
# Automatic dependency generation
DEPDIR := .deps
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
%.o: %.c $(DEPDIR)/%.d|$(DEPDIR)
$(CC) $(DEPFLAGS) $(CFLAGS) -c $< -o $@
$(DEPDIR):
@mkdir -p $@
DEPFILES := $(SOURCES:%.c=$(DEPDIR)/%.d)
$(DEPFILES):
include $(wildcard $(DEPFILES))
Order-Only Prerequisites
# Create directory before building objects
$(OBJECTS):|$(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
Phony Targets
Common Phony Targets
.PHONY: all clean install test help
all: $(TARGET)
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/bin/
test: $(TARGET)
./$(TARGET) --test
help:
@echo "Available targets:"
@echo " all - Build the program"
@echo " clean - Remove build files"
@echo " install - Install the program"
@echo " test - Run tests"
Multi-Target Builds
Multiple Programs
PROGRAMS = client server
COMMON_OBJS = utils.o network.o
all: $(PROGRAMS)
client: client.o $(COMMON_OBJS)
$(CC) -o $@ $^
server: server.o $(COMMON_OBJS)
$(CC) -o $@ $^
clean:
rm -f *.o $(PROGRAMS)
.PHONY: all clean
Library Building
LIBNAME = libutils
STATIC_LIB = $(LIBNAME).a
SHARED_LIB = $(LIBNAME).so
LIB_OBJS = utils.o network.o
all: $(STATIC_LIB) $(SHARED_LIB)
$(STATIC_LIB): $(LIB_OBJS)
ar rcs $@ $^
$(SHARED_LIB): $(LIB_OBJS)
$(CC) -shared -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -fPIC -c $< -o $@
Command Line Usage
Basic Commands
# Build default target
make
# Build specific target
make clean
make install
# Build with variables
make DEBUG=1
make CC=clang
# Parallel builds
make -j4
make -j$(nproc)
# Dry run (show commands without executing)
make -n
# Silent mode
make -s
# Keep going on errors
make -k
Debugging
# Print variables
make -p
# Debug mode
make -d
# Print database
make --print-data-base
# Trace execution
make --trace
Complex Examples
C++ Project
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -g
LDFLAGS = -lpthread
SRCDIR = src
OBJDIR = obj
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
OBJECTS = $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
TARGET = myprogram
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp|$(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(OBJDIR):
mkdir -p $(OBJDIR)
clean:
rm -rf $(OBJDIR) $(TARGET)
# Dependency generation
-include $(OBJECTS:.o=.d)
$(OBJDIR)/%.d: $(SRCDIR)/%.cpp|$(OBJDIR)
$(CXX) -MM -MT $(@:.d=.o) $< >`` $@
Multi-Directory Project
# Project structure
PROJECT_ROOT = .
SRC_DIRS = src src/utils src/network
INC_DIRS = include
OBJ_DIR = build
BIN_DIR = bin
# Find all source files
SOURCES = $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
OBJECTS = $(SOURCES:%.c=$(OBJ_DIR)/%.o)
TARGET = $(BIN_DIR)/myapp
# Compiler settings
CC = gcc
CFLAGS = -Wall -g $(addprefix -I,$(INC_DIRS))
LDFLAGS = -lm
.PHONY: all clean dirs
all: dirs $(TARGET)
dirs:
@mkdir -p $(OBJ_DIR) $(BIN_DIR)
@mkdir -p $(foreach dir,$(SRC_DIRS),$(OBJ_DIR)/$(dir))
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
Best Practices
Makefile Organization
# Variables at the top
CC = gcc
CFLAGS = -Wall -g
TARGET = myprogram
# Default target first
all: $(TARGET)
# Pattern rules
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Specific rules
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@
# Phony targets at the end
.PHONY: all clean install
clean:
rm -f *.o $(TARGET)
Error Handling
# Check for required tools
ifeq ($(shell which $(CC)),)
$(error Compiler $(CC) not found)
endif
# Validate variables
ifndef SOURCES
$(error SOURCES variable is not defined)
endif
# Conditional compilation
ifeq ($(shell pkg-config --exists gtk+-3.0; echo $?),0)
CFLAGS += $(shell pkg-config --cflags gtk+-3.0)
LDFLAGS += $(shell pkg-config --libs gtk+-3.0)
else
$(warning GTK+ 3.0 not found, building without GUI)
endif
Performance Tips
# Use order-only prerequisites for directories
$(OBJECTS):|$(OBJDIR)
# Minimize shell calls
SOURCES := $(shell find src -name '*.c')
# Use built-in rules when possible
# Instead of:
# %.o: %.c
# $(CC) $(CFLAGS) -c $< -o $@
# Just use the built-in rule
# Parallel-safe directory creation
$(OBJDIR):
@mkdir -p $@
Integration
With Version Control
# Get version from git
VERSION = $(shell git describe --tags --always --dirty)
CFLAGS += -DVERSION=\"$(VERSION)\"
# Include git info
GIT_COMMIT = $(shell git rev-parse HEAD)
BUILD_DATE = $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
CFLAGS += -DGIT_COMMIT=\"$(GIT_COMMIT)\" -DBUILD_DATE=\"$(BUILD_DATE)\"
With Package Managers
# pkg-config integration
CFLAGS += $(shell pkg-config --cflags libssl)
LDFLAGS += $(shell pkg-config --libs libssl)
# Check dependencies
check-deps:
@pkg-config --exists libssl||(echo "libssl not found" && exit 1)
Troubleshooting
Common Issues
# Missing separator error
# Make sure recipes use tabs, not spaces
# Target not found
make: *** No rule to make target 'foo'. Stop.
# Check target name and dependencies
# Circular dependency
make: Circular dependency dropped.
# Review dependency chain
# Debug makefile
make -p|grep target_name
make -d
Debugging Tips
# Print variable values
debug:
@echo "CC = $(CC)"
@echo "CFLAGS = $(CFLAGS)"
@echo "SOURCES = $(SOURCES)"
@echo "OBJECTS = $(OBJECTS)"
# Check if file exists
check-file:
@test -f myfile.c && echo "File exists"||echo "File missing"
Resources
- Official Manual: gnu.org/software/make/manual
- GNU Make: gnu.org/software/make
- Tutorial: makefiletutorial.com
- Best Practices: clarkgrubb.com/makefile-style-guide