[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
| variable | meaning | Example |
|---|---|---|
$@ | target name | myapp |
$< | first dependency | main.cpp |
$^ | All dependencies | main.o util.o |
$? | Newer dependency than target | main.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.cppfiles as.o$<: first dependency (.cppfile)$@: target (.ofile)
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.dfiles.-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
- Basic syntax: target, dependency, command
- Variables:
CXX,CXXFLAGS, automatic variables ($@,$<,$^) - Pattern Rule: Automate with
%.o: %.cpp - Function: File processing with
wildcard,patsubst - Automatically create dependencies: Manage header dependencies with
-MMD
Makefile vs CMake
| Features | Makefile | CMake |
|---|---|---|
| Complexity | low | High |
| cross platform | LIMITED | Excellent |
| learning curve | gentle | Steep |
| Direct control | High | low |
| suitable project | small project | big project |
Practical tips
- Efficient Build
- Enable parallel build (
make -j4) - Automatic creation of dependencies (
-MMD) - Compilation caching with ccache
- Maintenance
- Centralize settings with variables
- Clarify target with
.PHONY - Explain complex rules with comments
- Debugging
make -n: Prints only commands (does not execute)make -d: Print debug information- Check the value of the
@echovariable
Make command
| command | Description |
|---|---|
make | Build default target |
make clean | run clean target |
make -j4 | 4 task parallel build |
make -n | Output only commands (dry-run) |
make -B | force rebuild all targets |