全面介绍 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/ifneqifdef/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 $@