1. Makefile 基础
1.1 基本结构
target: dependencies
command
- target: 规则的目标(例如生成的文件或操作名称)。
- dependencies: 生成目标所需的文件或其他目标。
- command: 生成目标的命令(必须用
Tab
缩进)。
1.2 示例:编译 C 程序
# 定义编译器和编译选项
CC = gcc
CFLAGS = -Wall -g
# 默认目标
all: hello
# 生成可执行文件
hello: hello.o
$(CC) $(CFLAGS) -o hello hello.o
# 生成目标文件
hello.o: hello.c
$(CC) $(CFLAGS) -c hello.c
# 清理生成的文件
clean:
rm -f hello *.o
2. 核心概念
2.1 变量
-
自定义变量:
CC = gcc CFLAGS = -Wall -O2
-
自动变量:
$@
: 当前目标名$<
: 第一个依赖项$^
: 所有依赖项
%.o: %.c $(CC) $(CFLAGS) -c $< -o $@
2.2 伪目标
声明不生成实际文件的操作(如 clean
):
.PHONY: clean
clean:
rm -f *.o hello
2.3 隐式规则
Make 自动推导常见操作(如 .c
→ .o
):
# 隐式规则示例(无需显式写 hello.o 的规则)
hello: hello.o
$(CC) $(CFLAGS) $^ -o $@
3. 实战场景
3.1 多文件项目
SRCS = main.c utils.c logger.c
OBJS = $(SRCS:.c=.o)
TARGET = app
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $^ -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(TARGET) $(OBJS)
3.2 多目录结构
SRC_DIR = src
OBJ_DIR = obj
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS))
TARGET = bin/app
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $^ -o $@
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR):
mkdir -p $@
clean:
rm -rf $(OBJ_DIR) $(TARGET)
3.3 动态库构建
LIB_NAME = libmylib.so
SRCS = lib.c
OBJS = $(SRCS:.c=.o)
$(LIB_NAME): $(OBJS)
$(CC) -shared $^ -o $@
%.o: %.c
$(CC) -fPIC -c $< -o $@
install: $(LIB_NAME)
cp $(LIB_NAME) /usr/local/lib
uninstall:
rm -f /usr/local/lib/$(LIB_NAME)
3.4 条件判断
DEBUG ?= 0
ifeq ($(DEBUG), 1)
CFLAGS += -DDEBUG -O0
else
CFLAGS += -O2
endif
app: main.c
$(CC) $(CFLAGS) $< -o $@
4. 高级技巧
4.1 函数
# 获取所有 .c 文件
SRCS = $(wildcard src/*.c)
# 替换后缀生成 .o 文件列表
OBJS = $(patsubst %.c, %.o, $(SRCS))
# 检查目录是否存在
ifneq ($(wildcard $(OBJ_DIR)),)
# OBJ_DIR 存在
endif
4.2 自动依赖生成
DEPFLAGS = -MMD -MP
CFLAGS += $(DEPFLAGS)
-include $(OBJS:.o=.d) # 包含生成的依赖文件
4.3 并行构建
make -j4 # 使用 4 个线程加速构建
5. 常见错误处理
-
命令未用 Tab 缩进
target: spaces_instead_of_tab # 错误!必须用 Tab
-
忽略隐式规则
使用
-r
禁用隐式规则:make -r
-
依赖关系错误
确保依赖项完整,可通过
make --debug
调试依赖链。
6. 最佳实践
-
保持简洁
避免过度复杂的逻辑,必要时拆分多个 Makefile。
-
使用变量
集中管理编译器、路径和选项。
-
伪目标声明
为所有非文件生成目标添加
.PHONY
。 -
跨平台兼容
-
使用
ifeq
处理不同 OS 的差异:ifeq ($(OS),Windows_NT) RM = del /Q else RM = rm -f endif
7. 扩展工具
- CMake: 跨平台构建系统(生成 Makefile)。
- Bazel: 高性能构建工具。
- Ninja: 替代 Make 的快速构建工具。