Linux 应用层开发入门(七)| Makefile的使用(三)

在前面的文章中,我们已经介绍了 Makefile 的基本规则、目标与依赖关系,以及变量的定义方式。本篇将重点讲解 Makefile 中最核心、最实用的一部分——常用函数与自动变量。
这些函数在 Linux 内核、Bootloader 以及大型应用工程的 Makefile 中被大量使用,是阅读和编写复杂 Makefile 的基础。
1 Makefile 常用函数
GNU Make 提供了大量内置函数,用于字符串处理、文件名操作以及流程控制。在实际工程中,Makefile 的“灵活性”和“可扩展性”很大程度上依赖这些函数。
1.1 函数调用格式
Makefile 中函数的统一调用格式为:
$(function arguments)
说明如下:
function:函数名arguments:函数参数- 函数名与参数之间使用空格或 Tab分隔
- 如果有多个参数,参数之间用逗号
,分隔 - 空格和逗号不属于参数值的一部分
在 Linux 内核的 Makefile 体系中,函数的使用极其频繁,理解这些函数是阅读内核 Makefile 的关键前提。
2 字符串替换与分析函数
(1)subst —— 字符串替换函数
$(subst from,to,text)
功能:
在字符串
text中,将所有的from替换为to
示例:

$(subst ee,EE,feet on the street)
结果为:

该函数通常用于简单的字符串整体替换。
(2)patsubst —— 模式替换函数(极其常用)
$(patsubst pattern,replacement,text)
功能:
- 在
text中查找符合pattern的单词,并用replacement替换pattern和replacement中可以使用通配符%
示例:

$(patsubst %.c,%.o,x.c.c bar.c)
结果为:

📌 说明:
patsubst 是 Makefile 中最常用的函数之一,大量用于从 .c 文件列表生成 .o 文件列表。
(3)strip —— 去除多余空格
$(strip string)
功能:
- 去除前导和结尾空格
- 将中间连续的多个空格压缩为一个
示例:

$(strip a b c )
结果为:

(4)findstring —— 字符串查找
$(findstring find,in)
功能:
- 若在
in中找到find,返回find - 否则返回空字符串
示例:
$(findstring a,a b c) # 返回 a
$(findstring a,b c) # 返回空
(5)filter —— 条件过滤
$(filter pattern...,text)
功能:
- 从
text中筛选出符合 pattern 的单词 - 其余单词会被去除
示例:

$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
结果为:

(6)filter-out —— 反向过滤
$(filter-out pattern...,text)
功能:
- 从
text中删除符合pattern的单词 - 返回剩余内容
示例:

$(filter-out %.c %.s,foo.c bar.c baz.s ugh.h)
结果为:

(7)sort —— 排序并去重
$(sort list)
功能:
-
对单词按字母顺序排序
-
去掉重复项
示例:

$(sort foo bar lose)
结果为:

3 文件名处理函数
(1)dir —— 获取路径部分
$(dir names...)
示例:

$(dir Makefile)
结果:

(2)notdir —— 获取文件名
$(notdir names...)
示例:

$(notdir Makefile)
结果:

(3)suffix —— 获取后缀名
示例:

$(suffix test.txt)
结果:

(4)basename —— 去除后缀
示例:

$(basename test.txt)
结果:

(5)addsuffix / addprefix —— 批量添加后缀与前缀
在实际工程中,我们经常需要对一组文件名进行统一处理,例如:
-
给一批源文件名加上后缀
.c、.o -
给一批文件名前统一加上目录路径(如
src/、obj/)
为此,GNU Make 提供了两个非常实用的函数:addsuffix 和 addprefix。
①addsuffix 函数原型:
$(addsuffix suffix,names...)
参数说明:
suffix:要添加的后缀字符串names...:由空格分隔的一组文件名(可以是变量)
功能说明:
将
suffix依次附加到names...中的每一个独立单词后面,并以空格形式拼接返回。
示例:
$(addsuffix .c,foo bar)
执行过程等价于:
foo→foo.cbar→bar.c
最终结果为:
foo.c bar.c
📌 典型应用场景:
SRC := main sub util
CFILES := $(addsuffix .c,$(SRC))
展开后:
main.c sub.c util.c
该方式常用于从不带后缀的模块名生成源文件列表。
②addprefix函数原型:
$(addprefix prefix,names...)
参数说明:
prefix:要添加的前缀字符串(通常是路径)names...:由空格分隔的一组文件名
功能说明:
将
prefix依次添加到names...中每一个文件名前面,并返回新的文件名列表。
示例:
$(addprefix src/,foo bar)
执行过程等价于:
foo→src/foobar→src/bar
最终结果为:
src/foo src/bar
📌 典型应用场景:
OBJS := main.o sub.o
OBJ_FILES := $(addprefix obj/,$(OBJS))
展开后:
obj/main.o obj/sub.o
该用法在多目录工程、分离源码目录和目标目录时极其常见。
③addprefix + addsuffix 组合使用(重点)
在真实工程中,这两个函数经常组合使用,用于快速构造完整路径文件名。
示例:
FILES := main sub
SRCS := $(addsuffix .c,$(FILES))
FULL_SRCS := $(addprefix src/,$(SRCS))
最终结果为:
src/main.c src/sub.c
等价于一行写法:
FULL_SRCS := $(addprefix src/,$(addsuffix .c,$(FILES)))
4️⃣ 与 wildcard、patsubst 的区别
| 函数 | 作用特点 |
|---|---|
addsuffix | 只做字符串拼接,不检查文件是否存在 |
addprefix | 只做字符串拼接 |
wildcard | 获取真实存在的文件 |
patsubst | 按模式进行替换 |
📌 重要提示:
addsuffix和addprefix不关心文件是否真实存在,只是对字符串进行处理。
5️⃣ 小结(工程经验)
-
addsuffix:适合 统一生成后缀 -
addprefix:适合 统一添加路径 -
两者组合使用,可快速构建源文件和目标文件列表
-
是 大型 Makefile 中的高频函数
(6)wildcard —— 获取真实存在的文件
示例:

$(wildcard *.txt)
结果:

⚠️ 与 shell 中的通配符类似,但只返回真实存在的文件。
4 其他常用函数
(1)foreach —— 循环展开
$(foreach var,list,text)
执行过程:
- 先展开
list - 将 list 中的每个值依次赋给
var - 对
text进行多次展开 - 最终结果以空格拼接
示例:
dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
等价于:
files := $(wildcard a/* b/* c/* d/*)
(2)if —— 条件判断
$(if condition,then-part,else-part)
注意事项:
- 只会执行
then-part或else-part中的一个 - 不会同时执行,避免副作用(如
shell)
(3)origin —— 判断变量来源
$(origin variable)
常见返回值:
undefined:变量var从来没有定义;file:变量var在Makefile中定义;environment:变量var作为环境变量定义,选项-e没有打开;command line:变量var在命令行中定义;automatic:变量var为自动变量
该函数在调试复杂 Makefile时非常有用。
(4)shell —— 调用外部命令
$(shell command arguments)
示例:
c_src := $(shell ls *.c)
📌 返回值说明:
- 换行符会被转换为空格
- 结尾的换行符会被自动去除
5 综合示例:options 程序 Makefile 解析
示例 Makefile
src := $(shell ls *.c)
objs := $(patsubst %.c,%.o,$(src))
test: $(objs)
gcc -o $@ $^
%.o: %.c
gcc -c -o $@ $<
clean:
rm -f test *.o
自动变量说明
| 变量 | 含义 |
|---|---|
$@ | 当前目标 |
$^ | 所有依赖 |
$< | 第一个依赖 |
编译流程分析
目录内容:
main.c
sub.c
sub.h
Makefile
首次执行:
make
输出:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
修改 sub.c 后再次执行:
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
✅ 只重新编译发生变化的源文件,提高了编译效率
6 总结
- Makefile 函数是工程 Makefile 的“灵魂”
patsubst、wildcard、foreach使用最为频繁- 自动变量使规则更加通用、简洁
- 理解这些内容后,可轻松阅读内核和大型项目的 Makefile











