コンテンツにスキップ

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