[2026] C++ Makefile Tutorial | Variables, Pattern Rules, Dependencies & Parallel Make

[2026] C++ Makefile Tutorial | Variables, Pattern Rules, Dependencies & Parallel Make

이 글의 핵심

Makefile guide for C++ projects: tabs, automatic variables, wildcards, -MMD dependencies, parallel -j, and when to prefer CMake for cross-platform builds.

Entering

Makefile is the build automation script used by Make. Increase development productivity by automating compilation and linking.

1. Makefile basics

The simplest Makefile

아래 코드는 makefile를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# Makefile
myapp: main.cpp
	g++ main.cpp -o myapp
clean:
	rm -f myapp

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# build
make
# organize
make clean

output of power:

g++ main.cpp -o myapp

Basic grammar

아래 코드는 makefile를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# Target: Dependency
# Command (be sure to start with a tab!)
target: dependencies
	command
# example
main.o: main.cpp
	g++ -c main.cpp -o main.o

component:

  • target: Name of the file or task to be created
  • Dependencies: Files needed to create the target
  • command: Shell command to execute (must start with tab)

2. Using variables

Basic variables

아래 코드는 makefile를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# variable definition
CXX = g++
CXXFLAGS = -std=c++17 -Wall -O2
INCLUDES = -I./include
LIBS = -lpthread -lm
# Use variables
myapp: main.cpp
	$(CXX) $(CXXFLAGS) $(INCLUDES) main.cpp -o myapp $(LIBS)
clean:
	rm -f myapp
.PHONY: clean

Automatic variables

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
CXXFLAGS = -std=c++17 -Wall
# automatic variable
# $@: target name
# $<: first dependency
# $^: all dependencies
myapp: main.o util.o
	$(CXX) $^ -o $@
	# $^ = main.o util.o
	# $@ = myapp
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@
# $< = main.cpp (first dependency)
# $@ = main.o (target)

Automatic variable cleanup

variablemeaningExample
$@target namemyapp
$<first dependencymain.cpp
$^All dependenciesmain.o util.o
$?Newer dependency than targetmain.o

3. Practical example

Example 1: Simple project

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Compiler settings
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra
# target
TARGET = myapp
# build
$(TARGET): main.cpp
	$(CXX) $(CXXFLAGS) main.cpp -o $(TARGET)
# execution
run: $(TARGET)
	./$(TARGET)
# organize
clean:
	rm -f $(TARGET)
# non-file target
.PHONY: clean run

How ​​to use:

make # build
make run # run after build
make clean # cleanup

Example 2: Multiple file project

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra
TARGET = myapp
# object file
OBJS = main.o calculator.o utils.o
# linking
$(TARGET): $(OBJS)
	$(CXX) $(OBJS) -o $(TARGET)
# compile
main.o: main.cpp calculator.h utils.h
	$(CXX) $(CXXFLAGS) -c main.cpp
calculator.o: calculator.cpp calculator.h
	$(CXX) $(CXXFLAGS) -c calculator.cpp
utils.o: utils.cpp utils.h
	$(CXX) $(CXXFLAGS) -c utils.cpp
# organize
clean:
	rm -f $(OBJS) $(TARGET)
.PHONY: clean

Example 3: Pattern Rules (Automation)

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra
TARGET = myapp
# Find all .cpp files
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
# linking
$(TARGET): $(OBJS)
	$(CXX) $^ -o $@
# Pattern rule: all .cpp → .o
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
	rm -f $(OBJS) $(TARGET)
.PHONY: clean

Pattern Rule Description:

  • %.o: %.cpp: Compile all .cpp files as .o
  • $<: first dependency (.cpp file)
  • $@: target (.o file)

Example 4: Library linking

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra
INCLUDES = -I./include
LDFLAGS = -lpthread -lm
TARGET = myapp
SRCS = $(wildcard src/*.cpp)
OBJS = $(SRCS:src/%.cpp=obj/%.o)
# linking
$(TARGET): $(OBJS)
	$(CXX) $^ -o $@ $(LDFLAGS)
# compile
obj/%.o: src/%.cpp
	@mkdir -p obj
	$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
clean:
	rm -rf obj $(TARGET)
.PHONY: clean

4. Advanced features

Conditional compilation

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
TARGET = myapp
# Change flags according to DEBUG variable
ifeq ($(DEBUG),1)
    CXXFLAGS = -std=c++17 -Wall -g -DDEBUG
else
    CXXFLAGS = -std=c++17 -Wall -O2 -DNDEBUG
endif
$(TARGET): main.cpp
	$(CXX) $(CXXFLAGS) main.cpp -o $(TARGET)
clean:
	rm -f $(TARGET)
.PHONY: clean

How ​​to use:

make # Release build
make DEBUG=1 # Debug build

Using the ### function 다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
CXXFLAGS = -std=c++17 -Wall
# wildcard: file pattern matching
SRCS = $(wildcard src/*.cpp)
# patsubst: pattern substitution
OBJS = $(patsubst src/%.cpp,obj/%.o,$(SRCS))
# shell: Execute shell command
$(shell mkdir -p obj)
myapp: $(OBJS)
	$(CXX) $^ -o $@
obj/%.o: src/%.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
	rm -rf obj myapp
.PHONY: clean

Automatic creation of dependencies

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

CXX = g++
CXXFLAGS = -std=c++17 -Wall
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(OBJS:.o=.d)
myapp: $(OBJS)
	$(CXX) $^ -o $@
# -MMD: Create dependency files
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -MMD -c $< -o $@
# Include dependency files
-include $(DEPS)
clean:
	rm -f $(OBJS) $(DEPS) myapp
.PHONY: clean

explanation:

  • -MMD: Create header dependencies as .d files.
  • -include: Include dependency files (no error occurs even without them)
  • Automatically recompiles when header file changes

5. Frequently occurring problems

Issue 1: Tabs vs Spaces

아래 코드는 makefile를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# ❌ Use of space (error!)
target:
    command
# ✅ Use tabs
target:
	command

Error Message:

Makefile:2: *** missing separator. Stop.

Solution:

  • Disable converting tabs to spaces in the editor settings.
  • Use Makefile mode (automatically insert tabs)

Issue 2: Missing dependencies

아래 코드는 makefile를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# ❌ No header dependency
main.o: main.cpp
	g++ -c main.cpp
# Even if util.h is changed, it will not be recompiled!
# ✅ Include header
main.o: main.cpp util.h config.h
	g++ -c main.cpp
# Automatic recompilation when util.h or config.h changes

Issue 3: Missing .PHONY

아래 코드는 makefile를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# ❌ If there is a file called clean, it will not be executed.
clean:
	rm -f *.o
# ✅ Use .PHONY
.PHONY: clean
clean:
	rm -f *.o
# Always runs even if there is a clean file

Issue 4: Parallel builds

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# Sequential build (slow)
make
# Parallel build (4 tasks simultaneously)
make -j4
# Parallel build as many CPU cores
make -j$(nproc)

Practical Tips:

  • Reduce compilation time with parallel builds
  • Parallel build is possible only when dependencies are set correctly

6. Practical example: complete project

Project structure

아래 코드는 code를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

project/
├── Makefile
├──include/
│ ├── calculator.h
│ └── utils.h
├── src/
│ ├── main.cpp
│ ├── calculator.cpp
│ └── utils.cpp
└── obj/
    └── (Created at build time)

Complete Makefile

다음은 makefile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Compiler settings
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra
INCLUDES = -I./include
LDFLAGS = -lpthread
# directory
SRC_DIR = src
OBJ_DIR = obj
INC_DIR = include
# file
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRCS))
DEPS = $(OBJS:.o=.d)
# target
TARGET = myapp
# Debug build
ifeq ($(DEBUG),1)
    CXXFLAGS += -g -DDEBUG
else
    CXXFLAGS += -O2 -DNDEBUG
endif
# Default target
all: $(TARGET)
# linking
$(TARGET): $(OBJS)
	$(CXX) $^ -o $@ $(LDFLAGS)
@echo "Build completed: $(TARGET)"
# Compile (automatically generate dependencies)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
	@mkdir -p $(OBJ_DIR)
	$(CXX) $(CXXFLAGS) $(INCLUDES) -MMD -c $< -o $@
# Include dependency files
-include $(DEPS)
# execution
run: $(TARGET)
	./$(TARGET)
# organize
clean:
	rm -rf $(OBJ_DIR) $(TARGET)
# Full rebuild
rebuild: clean all
# help
help:
@echo "Available targets:"
@echo " make - Release build"
@echo " make DEBUG=1 - Debug build"
@echo " make run - run after build"
@echo " make clean - cleanup"
@echo " make rebuild - full rebuild"
@echo " make -j4 - Parallel builds (4)"
.PHONY: all run clean rebuild help

Use example: 아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

make # Release build
make DEBUG=1 # Debug build
make -j4 # parallel build
make run # run
make clean # cleanup
make rebuild # rebuild
make help # help

organize

Key takeaways

  1. Basic syntax: target, dependency, command
  2. Variables: CXX, CXXFLAGS, automatic variables ($@, $<, $^)
  3. Pattern Rule: Automate with %.o: %.cpp
  4. Function: File processing with wildcard, patsubst
  5. Automatically create dependencies: Manage header dependencies with -MMD

Makefile vs CMake

FeaturesMakefileCMake
ComplexitylowHigh
cross platformLIMITEDExcellent
learning curvegentleSteep
Direct controlHighlow
suitable projectsmall projectbig project

Practical tips

  1. Efficient Build
  • Enable parallel build (make -j4)
  • Automatic creation of dependencies (-MMD)
  • Compilation caching with ccache
  1. Maintenance
  • Centralize settings with variables
  • Clarify target with .PHONY
  • Explain complex rules with comments
  1. Debugging
  • make -n: Prints only commands (does not execute)
  • make -d: Print debug information
  • Check the value of the @echo variable

Make command

commandDescription
makeBuild default target
make cleanrun clean target
make -j44 task parallel build
make -nOutput only commands (dry-run)
make -Bforce rebuild all targets

Next steps


... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3