一文精通Makefile
全面介绍 Makefile 语法
1. 注释与空行
#
开头的部分为注释,直到行尾。- 空行会被忽略。
# 这是注释
all: # 目标 all
@echo "Building all"
2. 变量定义与展开
- 递归展开(延迟展开):
VAR = value $(OTHER)
在使用时才展开。
- 简单展开(立即展开):
VAR := value $(OTHER)
定义时立即展开右侧。
- 条件赋值:
VAR ?= default
仅在 VAR
未定义时赋值。
- 强制覆盖:
override VAR = value
即使命令行有 -DVAR=...
也覆盖。
- 多行变量(定义多行文本):
define MULTILINE
line1
line2
endef
- 引用与自动变量:
$(VAR)
或${VAR}
。- 常用自动变量:
$@
:目标名称$<
:第一个依赖$^
:所有依赖(去重)$?
:比目标新的依赖
3. 规则(Rules)
基本结构:
target: prerequisites...
<TAB>recipe line1
<TAB>recipe line2
target
:可以是文件名或伪目标。prerequisites
:依赖文件或其他目标。- Recipe 必须以 Tab 开头。
4. 模式规则(Pattern Rules)
- 通配符
%
:
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
所有 .c
→ .o
隐式转换。
- 多目标模式:
lib%.a lib%.so: %.c
# 定义多个 target 同时生成
5. 伪目标(Phony Targets)
- 声明不会生成文件的目标:
.PHONY: clean all install
- 避免与文件同名冲突。
6. 内置函数与文本处理
- 文字替换:
SRCS = a.c b.c c.c
OBJS = $(SRCS:.c=.o)
- 函数调用:
DIRS = src include
all:
$(foreach d,$(DIRS),echo Building $(d);)
- 常用函数
$(subst from,to,text))
$(patsubst pattern,replacement,text)
$(strip text)
$(findstring find,in)
$(filter pattern...,text)
$(filter-out pattern...,text)
$(sort list)
$(wildcard pattern)
7. 条件判断(Conditionals)
ifeq ($(CC),gcc)
CFLAGS += -Wall
else ifneq ($(CC),clang)
CFLAGS += -pedantic
endif
- 支持
ifeq
/ifneq
、ifdef
/ifndef
、嵌套。
8. 包含(Include)
- 在当前 Makefile 中引入其他文件:
include common.mk
-include optional.mk # 不存在时不报错
9. 内建规则与后缀规则(Suffix Rules)
.SUFFIXES
: 定义后缀列表。- 旧式写法:
.SUFFIXES: .c .o
.c.o:
$(CC) $(CFLAGS) -c $<
- 一般推荐使用模式规则替代。
10. 并行与递归递归调用
- 并行:
make -j4
- 递归调用:
subdirs = dir1 dir2
all:
@for d in $(subdirs); do \
$(MAKE) -C $$d; \
done
11. 其它重要伪目
.DEFAULT
: 未匹配规则时执行。.PRECIOUS
: 防止中断时删除中间文件。.INTERMEDIATE
: 声明中间文件。.SECONDARY
: 中间文件保留。.DELETE_ON_ERROR
: 错误时删除。
12. 可选扩展和高级技
- 自动依赖生成(GCC
-MMD -MP
) - 动态目标(
$(@D)
、$(@F)
、$(<D)
) 路径处理) - 高级流水线:
make -n
make --trace
调试 - 环境变量影响:
CC=gcc CFLAGS=-g make
示例
# Complex Makefile Example
# Covers variables, functions, rules, pattern rules, suffixes, conditionals, includes, custom targets, recursion, auto-deps
# 1. Variable Definitions
CC := gcc # simple (immediate) expansion
CXX = g++ # recursive expansion
CFLAGS += -Wall -Wextra # append
LDFLAGS ?= # default if undefined
BUILD ?= debug # default build: debug or release
# Root directories
SRCDIR := src
BUILDDIR:= build
BINDIR := bin
INCDIR := include
# 2. File Lists (using wildcard and patsubst)
SRCS := $(wildcard $(SRCDIR)/**/*.c) $(wildcard $(SRCDIR)/**/*.cpp)
OBJS := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(filter %.c,$(SRCS))) \
$(patsubst $(SRCDIR)/%.cpp,$(BUILDDIR)/%.o,$(filter %.cpp,$(SRCS)))
# 3. Auto-dependency flags
DEPFLAGS := -MMD -MP
CFLAGS += $(DEPFLAGS) -I$(INCDIR)
# 4. Build type flags
ifeq ($(BUILD),release)
CFLAGS += -O3
LDFLAGS += -s
else ifeq ($(BUILD),debug)
CFLAGS += -g -O0 -DDEBUG
endif
# 5. Phony targets
.PHONY: all clean distclean format test
# 6. Default target
all: $(BINDIR)/myapp
# 7. Linking
$(BINDIR)/myapp: $(OBJS) | $(BINDIR)
$(CC) $(LDFLAGS) -o $@ $^
# 8. Object rules (pattern rule)
$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR)
@echo "CC $< -> $@"
$(CC) $(CFLAGS) -c $< -o $@
$(BUILDDIR)/%.o: $(SRCDIR)/%.cpp | $(BUILDDIR)
@echo "CXX $< -> $@"
$(CXX) $(CFLAGS) -c $< -o $@
# 9. Create directories
$(BINDIR) $(BUILDDIR):
@mkdir -p $@
# 10. Include auto-generated dependency files
-include $(OBJS:.o=.d)
# 11. Custom commands and targets
format:
clang-format -i $(SRCS)
# 12. Recursion: build pre-reqs in subdirs
SUBDIRS := tools extras
.PHONY: prep
prep:
@$(foreach dir,$(SUBDIRS), \
$(MAKE) -C $(dir) && echo "Built $(dir)";)
# 13. Test target
test: all
@echo "Running tests..."
@$(BINDIR)/myapp --run-tests
# 14. Clean up
clean:
rm -rf $(BUILDDIR)/*.o $(BINDIR)/myapp
distclean: clean
rm -rf $(BINDIR) $(BUILDDIR) */*.d
# 15. Include additional makefiles
-include local.mk
# 16. Suffix rules (legacy, for demonstration)
.SUFFIXES: .c .cpp .o
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
.cpp.o:
$(CXX) $(CFLAGS) -c $< -o $@
评论