Makefile 编写教程
一、Makefile 核心概念
- 基本组成要素
Makefile 由 目标(Target)、依赖(Dependencies) 和 命令(Commands) 三部分组成,格式为:target: dependencies command # 必须用 Tab 缩进
- 目标:要生成的文件或操作名称(如可执行文件、伪目标)。
- 依赖:生成目标所需的文件或其他目标。
- 命令:生成目标的 Shell 指令(如编译命令)。
- 工作流程
- 执行
make
时,默认查找第一个目标(或通过make target
指定目标)。 - 检查依赖是否更新(通过文件时间戳),若依赖比目标新,则执行关联命令。
- 递归处理依赖的依赖,直至生成最终目标。
- 执行
二、基础实战:最小化示例
- 单文件编译
假设项目结构如下:project/ ├── main.c └── Makefile
main: main.c gcc -o main main.c .PHONY: clean # 声明伪目标 clean: rm -f main
- 操作演示:
make # 编译程序 ./main # 运行输出 make clean # 清理生成的文件
- 操作演示:
- 多文件编译
项目结构:project/ ├── main.c ├── utils.c └── utils.h
# 基础配置 # 定义最终生成的可执行文件名称 TARGET = app # 指定使用的编译器(这里使用 GNU C 编译器) CC = gcc # 编译选项: # -Wall: 启用所有警告信息 # -O2: 启用二级优化 # -I.: 添加当前目录到头文件搜索路径 CFLAGS = -Wall -O2 -I. # -I. 显式指定当前目录为头文件搜索路径 # 使用 wildcard 函数自动获取当前目录下所有 .c 文件 SRCS = $(wildcard *.c) # 将 .c 文件名替换为 .o 文件名(即生成对应的目标文件列表) OBJS = $(SRCS:.c=.o) # 将 .o 文件名替换为 .d 文件名(即生成对应的依赖文件列表) DEPS = $(OBJS:.o=.d) # 自动生成依赖文件 # 主目标:构建最终的可执行文件 # 依赖所有目标文件 $(OBJS) $(TARGET): $(OBJS) # 使用 gcc 链接所有目标文件,生成可执行文件 $(CC) $(CFLAGS) -o $@ $^ # $@ 表示目标文件(即 $(TARGET)) # $^ 表示所有依赖文件(即 $(OBJS)) # 编译规则:如何从 .c 文件生成 .o 文件 %.o: %.c # 使用 gcc 编译 .c 文件: # -MMD: 生成依赖关系文件(.d),但不包含系统头文件 # -MP: 为每个依赖生成一个空目标,避免删除头文件时出错 # -c: 只编译不链接 # $<: 表示第一个依赖文件(即当前的 .c 文件) # $@: 表示目标文件(即当前的 .o 文件) $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ # 包含自动生成的依赖关系 # -include 表示如果文件不存在也不报错 -include $(DEPS) # 伪目标声明(这些目标不对应实际文件) .PHONY: clean all # 默认目标(执行 make 时默认构建 all) all: $(TARGET) # 默认目标 # 清理目标:删除所有生成的文件 clean: # 删除所有目标文件、依赖文件和最终的可执行文件 rm -f $(OBJS) $(DEPS) $(TARGET)
- 关键语法解析:
wildcard
:获取匹配模式的文件列表(如*.c
)。$(SRCS:.c=.o)
:字符串替换,将.c
替换为.o
。$@
:当前目标名;$^
:所有依赖文件;$<
:第一个依赖文件。
- 关键语法解析:
三、多文件编译与多目录管理
-
项目结构
project/ ├── src/ │ ├── main.c │ └── utils.c ├── include/ │ └── utils.h └── Makefile
-
Makefile 实现
SRCDIR = src # 源码目录 INCDIR = include # 头文件目录 BUILDDIR = build # 编译输出目录 SRCS = $(wildcard $(SRCDIR)/*.c) # 自动获取 src/ 下所有 .c 文件 OBJS = $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCS)) # 生成 build/ 下的 .o 路径 $(BUILDDIR)/%.o: $(SRCDIR)/%.c @mkdir -p $(@D) # 自动创建 build/ 目录(包括子目录) $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@ TARGET = app $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ .PHONY: clean clean: rm -rf $(BUILDDIR) $(TARGET)