4、Makefile函数入门:字符串处理函数与文件名函数
说实话,很多初学者看到Makefile里的函数,第一反应就是「这玩意儿有必要学吗?」。我当年也是这么想的。直到有一次,我维护一个上百个源文件的项目,手动改了半小时路径,结果编译全挂——嗯,从那以后,我再也不敢小看这些函数了。
今天咱们就聊聊Makefile里最常用的两类函数:字符串处理函数和文件名函数。它们不复杂,但用好了,能让你的Makefile从「能跑」变成「优雅」。
4.1 字符串处理三剑客:subst、patsubst、strip
先说说字符串处理。Makefile本质上就是文本处理,你想想看,变量里存的都是字符串,不处理它们处理谁?
4.1.1 subst —— 简单替换
subst 是「substitute」的缩写,意思就是替换。它的语法很简单:
$(subst 被替换的字符串, 替换成的字符串, 原始文本)
举个例子:
SRC := hello.c world.c
OBJ := $(subst .c,.o,$(SRC))
# OBJ 的值是 hello.o world.o
我在项目中遇到过一种情况:不同平台下,源文件后缀名不一样。比如Linux下是.c,Windows下是.cpp。用subst一把搞定,不用写两套规则。
subst 是严格匹配,不会做模式匹配。比如你想把 abc.c 换成 abc.o,没问题。但如果你想把所有 .c 结尾的都换成 .o,用 patsubst 更合适。
4.1.2 patsubst —— 模式替换
patsubst 全称是「pattern substitution」,模式替换。它支持通配符 %,这才是真正的生产力工具。
$(patsubst 模式, 替换模式, 原始文本)
看个实际例子:
SRC := src/main.c src/utils.c lib/helper.c
OBJ := $(patsubst %.c,%.o,$(SRC))
# OBJ 的值是 src/main.o src/utils.o lib/helper.o
这里 % 匹配了文件名中 .c 前面的部分,然后原封不动地放到 .o 前面。路径信息完整保留。
patsubst。因为它比 subst 更灵活,而且语义更清晰。你一看就知道是在做「模式替换」。
4.1.3 strip —— 去空格
strip 这名字很直白——去掉字符串首尾和中间多余的空格。语法最简单:
$(strip 字符串)
举个例子:
VAR := hello world !
CLEAN := $(strip $(VAR))
# CLEAN 的值是 "hello world !"
你可能觉得这功能没啥用。但我告诉你,变量展开时经常莫名其妙多出空格,尤其是用 foreach 或者 shell 函数的时候。我曾经排查过一个编译错误,折腾了两小时,最后发现是变量末尾多了一个空格,导致链接时文件路径不对。从那以后,我养成了习惯:凡是拼接出来的路径变量,先 strip 一下。
4.2 文件名函数:dir、notdir、suffix、basename
处理文件路径,是Makefile的日常操作。这四个函数,我愿称之为「路径四件套」。
4.2.1 dir —— 取目录
dir 从文件路径中提取目录部分,包括末尾的斜杠:
$(dir src/main.c lib/utils.c)
# 结果是 "src/ lib/"
注意,如果文件就在当前目录,比如 main.c,dir 会返回 ./。这个细节容易忽略。
4.2.2 notdir —— 取文件名
notdir 和 dir 正好相反,它只取文件名部分:
$(notdir src/main.c lib/utils.c)
# 结果是 "main.c utils.c"
这个函数我用的特别多。比如你想把所有源文件放到同一个输出目录,就可以先 notdir 去掉路径,再统一加上输出路径。
4.2.3 suffix —— 取后缀
suffix 提取文件的后缀名(包括点号):
$(suffix src/main.c lib/utils.h)
# 结果是 ".c .h"
如果文件没有后缀,返回空字符串。这个函数在判断文件类型时很有用。
4.2.4 basename —— 取主文件名
basename 去掉后缀,只保留文件名主体部分:
$(basename src/main.c lib/utils.h)
# 结果是 "src/main lib/utils"
注意,它保留了路径信息,只去掉最后一个点号及其后面的内容。
src/main.c 变成 build/main.o,可以这样写:OBJ := $(addprefix build/,$(notdir $(basename $(SRC)))).o先取文件名,再去掉后缀,加上前缀,最后补上
.o。一气呵成。
4.3 知识体系一览
为了让你更直观地理解这些函数的关系,我画了一张图:
4.4 避坑指南
最后,分享几个我踩过的坑:
- 空格问题:Makefile里,逗号后面不要加空格。比如
$(subst .c, .o, $(SRC))和$(subst .c,.o,$(SRC))结果不一样。前者会在.o前面多一个空格。 - 通配符误解:
subst不支持%,别搞混了。想用模式匹配,老老实实用patsubst。 - 路径斜杠:
dir返回的目录末尾带斜杠,拼接时注意不要重复。比如$(dir src/main.c)main.o结果是src/main.o,没问题。但如果你自己加了个斜杠,就变成src//main.o了。
这些函数,说白了就是帮你省去手动拼接字符串的麻烦。刚开始可能觉得多此一举,但等你项目大了、文件多了,就知道它们有多香了。
patsubst 和 notdir,这两个最常用。其他的用到再查,慢慢就熟了。
可可精品资料库