4、模式规则与静态模式:%通配符的使用,静态模式规则让代码更简洁
4.1 从重复劳动中解放出来
说实话,我刚开始写 Makefile 的时候,干过不少蠢事。
比如一个项目里有十几个 .c 文件,我就老老实实写了十几条规则:
foo.o: foo.c
gcc -c foo.c -o foo.o
bar.o: bar.c
gcc -c bar.c -o bar.o
baz.o: baz.c
gcc -c baz.c -o baz.o
你想想看,这多累啊。而且一旦要加一个新文件,又得复制粘贴改一遍。我当时就在想,Make 能不能聪明一点,自动匹配这些文件?
答案是:能。用 % 通配符。
4.2 % 通配符:模式规则的核心
% 在 Makefile 里,相当于 shell 里的 *。它匹配任意长度的字符串。比如 %.o 匹配所有以 .o 结尾的文件名。
用模式规则,上面的代码可以简写成一行:
%.o: %.c
gcc -c $< -o $@
这里 $< 代表第一个依赖(也就是 .c 文件),$@ 代表目标(.o 文件)。
嗯,是不是清爽多了?
核心理解: 模式规则的本质,就是让 Make 自动推导出目标与依赖之间的对应关系。你只需要写一次规则,所有符合模式的文件都能套用。
4.3 静态模式规则:更精准的控制
模式规则虽然好用,但它有个问题:它会匹配所有符合模式的目标。有时候你只想对特定的文件列表应用规则,而不是全部。
这时候就该静态模式规则出场了。
语法是这样的:
目标列表 : 目标模式 : 依赖模式
命令
举个例子:
OBJS = foo.o bar.o baz.o
$(OBJS): %.o: %.c
gcc -c $< -o $@
这条规则的意思是:对于 OBJS 列表里的每一个 .o 文件,它依赖同名的 .c 文件,然后用 gcc 编译。
我在项目中遇到过这样的情况:项目里既有 .c 文件,也有 .cpp 文件。如果用普通的模式规则,可能会误伤。用静态模式规则,我可以精确控制哪些文件走 C 编译器,哪些走 C++ 编译器:
C_OBJS = foo.o bar.o
CPP_OBJS = main.o utils.o
$(C_OBJS): %.o: %.c
gcc -c $< -o $@
$(CPP_OBJS): %.o: %.cpp
g++ -c $< -o $@
你看,这样就不会搞混了。
4.4 模式规则 vs 静态模式规则:一张图看懂
我画了张图,帮你理清两者的区别:
4.5 实战:一个完整的例子
光说不练假把式。我们来看一个实际项目中的 Makefile:
# 源文件列表
SRCS = main.c utils.c network.c
OBJS = $(SRCS:.c=.o)
TARGET = myapp
# 默认目标
all: $(TARGET)
# 静态模式规则:编译所有 .o 文件
$(OBJS): %.o: %.c
gcc -Wall -O2 -c $< -o $@
# 链接
$(TARGET): $(OBJS)
gcc $^ -o $@
# 清理
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: all clean
这里有个小技巧:$(SRCS:.c=.o) 是 Make 的替换引用语法,它把 SRCS 里所有 .c 后缀换成 .o。这样你只需要维护源文件列表,目标文件列表自动生成。
我的习惯: 我一般会把源文件列表放在 Makefile 顶部,用 SRCS 变量管理。这样后续加文件、删文件,只需要改这一处。配合静态模式规则,整个编译流程自动适配,省心不少。
4.6 避坑指南
我曾经踩过的坑:
- % 匹配的是整个路径。比如
src/%.o: src/%.c,这里的%匹配的是src/之后的部分。如果你目标文件在build/目录,依赖在src/目录,模式规则就不好使了。这时候需要配合vpath或目录变量来处理。 - 静态模式规则的目标列表不能为空。如果
OBJS是空的,Make 会报错。我一般会在列表前加个检查,或者确保至少有一个源文件。 - 模式规则不能有多个目标模式。比如
%.o %.d: %.c这种写法是不合法的。如果你需要同时生成 .o 和 .d 文件,得用其他技巧。
4.7 进阶:多目录下的静态模式
项目大了,源文件不可能全放根目录。我习惯把源文件按模块分目录存放:
SRCS = src/main.c \
src/utils/helper.c \
src/network/tcp.c
OBJS = $(SRCS:.c=.o)
# 注意:这里的目标模式要包含路径
$(OBJS): %.o: %.c
gcc -Wall -O2 -c $< -o $@
你看,%.o 匹配的是 src/main.o、src/utils/helper.o 这样的完整路径。依赖模式 %.c 也对应匹配。Make 会自动把 % 部分替换成对应的路径前缀。
说白了,% 就是个占位符。它匹配什么,就替换成什么。这个机制非常灵活,但也容易让人迷惑。记住一点:目标里的 % 和依赖里的 % 是同一个值。
4.8 小结
模式规则和静态模式规则,是 Makefile 从「能用」到「好用」的关键一步。它们帮你摆脱重复劳动,让编译规则变得简洁、可维护。
我个人建议:
- 小项目、文件类型单一:直接用模式规则
%.o: %.c - 大项目、混合编译:用静态模式规则,精确控制每个文件列表
- 多目录项目:注意路径匹配,
%会包含路径前缀
嗯,掌握了这些,你的 Makefile 已经比大多数人的要专业了。