3. Makefile变量与模式:自动变量、通配符与字符串处理
说实话,Makefile里最让我头疼的,就是那些看起来像天书一样的符号。$@、$<、%……刚入行那会儿,我对着这些符号愣是看了半天。但后来我发现,一旦搞懂了它们,写Makefile就跟搭积木一样简单。
这一章,咱们就把这些「符号」掰开揉碎了讲清楚。
3.1 自动变量:让规则自己「认识」自己
自动变量是Makefile里最实用的特性之一。说白了,就是让规则里的命令,能自动知道「我在处理哪个目标」、「我依赖哪些文件」。
我常用的自动变量有四个:
| 变量 | 含义 | 我常用的场景 |
|---|---|---|
$@ |
目标文件名 | 编译输出、链接输出 |
$< |
第一个依赖文件 | 单个源文件编译 |
$^ |
所有依赖文件(去重) | 链接多个目标文件 |
$* |
模式匹配的「主干」部分 | 生成依赖文件、中间文件 |
举个例子你就明白了。假设我们要编译一个 main.o:
main.o: main.c
gcc -c main.c -o main.o
用自动变量改写后:
main.o: main.c
gcc -c $< -o $@
你看,$< 自动变成了 main.c,$@ 自动变成了 main.o。这样写的好处是:如果以后目标名变了,命令不用改。
$< 和 $@。这样复制粘贴规则时,几乎不用改命令部分。
3.2 通配符 % 和 *:模式匹配的艺术
通配符是Makefile里最容易混淆的地方。我刚开始也分不清 % 和 * 的区别。
先说 %: 它是Makefile的「模式匹配符」,用在规则的目标和依赖中。比如:
%.o: %.c
gcc -c $< -o $@
这条规则的意思是:任何 .o 文件,都由同名的 .c 文件编译而来。这就是所谓的「模式规则」。
再说 *: 它是Shell的通配符,用在命令部分或者 wildcard 函数里。比如:
SRCS = $(wildcard *.c)
这行代码会把当前目录下所有 .c 文件都找出来,赋值给 SRCS。
*,比如 *.o: *.c。这不会报错,但也不会按你期望的方式工作。记住:规则目标用 %,命令里用 *。
3.3 字符串处理函数:让变量操作更灵活
Makefile里有一组字符串处理函数,我几乎每个项目都会用到。它们能帮你做文件名的替换、路径的提取、字符串的过滤等操作。
我挑几个最常用的说说:
| 函数 | 作用 | 示例 |
|---|---|---|
$(patsubst pattern,replacement,text) |
模式替换 | $(patsubst %.c,%.o,$(SRCS)) |
$(notdir names...) |
去掉路径,只取文件名 | $(notdir $(SRCS)) |
$(dir names...) |
只取目录部分 | $(dir $(SRCS)) |
$(filter pattern...,text) |
过滤出匹配模式的内容 | $(filter %.c,$(SRCS)) |
$(filter-out pattern...,text) |
过滤掉匹配模式的内容 | $(filter-out %.h,$(SRCS)) |
举个例子,假设我们有这样的源文件列表:
SRCS = src/main.c src/utils.c src/device.c include/defs.h
我想生成所有 .o 文件的列表,并且只保留 src/ 目录下的:
# 先过滤出 .c 文件
C_SRCS = $(filter %.c,$(SRCS))
# 再替换后缀为 .o
OBJS = $(patsubst %.c,%.o,$(C_SRCS))
# 结果:src/main.o src/utils.o src/device.o
你看,几行代码就把文件列表处理得明明白白。
wildcard 收集源文件,用 filter 和 patsubst 做筛选和转换,最后用自动变量驱动编译规则。这套组合拳,能搞定90%的工程化构建需求。
3.4 知识体系总览
为了让你更直观地理解这些概念之间的关系,我画了一张图:
这张图把三个核心模块串起来了。自动变量负责「规则内部」的自我引用,通配符负责「规则之间」的模式匹配,字符串函数负责「变量层面」的数据处理。三者配合,就能写出既简洁又强大的Makefile。
3.5 实战:一个完整的例子
说了这么多,不如直接看一个完整的例子。这是我个人项目中常用的模板:
# 收集所有 .c 文件
SRCS = $(wildcard src/*.c)
# 生成对应的 .o 文件列表(放在 build 目录下)
OBJS = $(patsubst src/%.c,build/%.o,$(SRCS))
# 目标:最终可执行文件
app: $(OBJS)
gcc $^ -o $@
# 模式规则:编译每个 .c 文件
build/%.o: src/%.c
@mkdir -p build
gcc -c $< -o $@
# 清理
clean:
rm -rf build app
你看,这个Makefile里用到了:
wildcard收集源文件patsubst转换文件路径和后缀$@和$^处理链接命令$<和$@处理编译命令%实现模式匹配
嗯,这就是我心目中「工程化」的Makefile写法。简洁、可维护、易扩展。
patsubst 里写错过模式,导致生成的 .o 文件路径不对。后来我养成了一个习惯:先用 $(info ...) 打印变量值,确认无误后再执行编译。这个习惯帮我省了不少调试时间。
好了,这一章的内容就到这里。变量和模式是Makefile的基石,搞懂了它们,后面的章节你会越学越轻松。
可可精品资料库