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 变量与模式知识体系 自动变量 $@ → 目标文件名 $< → 第一个依赖 $^ → 所有依赖(去重) $* → 模式匹配主干 通配符 % → Makefile模式匹配 (用于规则目标/依赖) * → Shell通配符 (用于命令/wildcard) 字符串处理函数 patsubst → 模式替换 notdir/dir → 路径处理 filter/filter-out → 筛选 wildcard → 文件收集 组合应用:模式规则 + 自动变量 + 函数 示例:OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) 规则:%.o: %.c → gcc -c $< -o $@

这张图把三个核心模块串起来了。自动变量负责「规则内部」的自我引用,通配符负责「规则之间」的模式匹配,字符串函数负责「变量层面」的数据处理。三者配合,就能写出既简洁又强大的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的基石,搞懂了它们,后面的章节你会越学越轻松。

可可精品资料库