4.3 文件名使用通配符
单个文件名中可以使用通配符来指定许多文件.Makefile中的通配符和Bourneshell一样,包括"*","?","[...]".例如,"*.c"代表了当前工作目录下所有的以".c"结尾的文件.
以字符"~"开始的文件名有特殊的含义.当单独使用它或者其后跟一个斜线时,它都代表了你的home目录,例如"~/bin"代表"/home/用户名/bin".当"~"后面跟了一个单词时,代表用户名为这个单词的home目录,例如"~john/bin"代表"/home/jonh/bin".
make程序会自动对目标和依赖中的通配符进行展开,而配方中的通配符是由shell负责展开的.除此之外,其他上下文中不能直接使用通配符,必须要通过函数"wildcard"来实现通配展开的功能.
通过使用反斜线"\"转义处理之后,"*"字符的通配特性可以被关闭.例如"foo\*bar"指的是一个名为"foo*bar"的文件.
4.3.1通配用法举例
通配符可以被用于规则的配方中,它是由shell负责展开.例如下面这条删除所有".o"文件的规则:
clean:
rm-f *.o
通配符也可以被用于规则的依赖中.例如一个Makefile包含了下面这条规则,执行"makeprint"将会打印当前目录下所有在上次打印之后修改过的".c"结尾的文件:
print:*.c
lpr-p $?
touchprint
这条规则中,"print"作为一个空目标文件使用,而自动变量"$?"表示依赖列表中被改变过的所有文件.
变量定义中使用的通配符不会被通配处理.因此如果你像下面这样写:
objects= *.o
那么变量"objects"的值实际上就是字符串"*.o".但是,如果你在目标或依赖中引用了这个变量,那么就会在引用的地方进行通配展开,而如果你在配方中引用了这个变量,那么shell会在执行该配方时负责进行展开.当确实需要变量"object"代表所有".o"文件列表时,应该这样写:
object:= $(wildcard *.o)
4.3.2通配符使用陷阱
这里有一个使用通配符的简单例子,但这个例子并不会达到你设想的意图.你期望基于当前目录下的所有".o"文件生成可执行文件"foo",所以你这么写:
objects= *.o
foo: $(objects)
cc-o foo $(CFLGAGS) $(objects)
变量"objects"的值实际上是字符串"*.o".在当前目录中存在".o"文件的前提下,make程序会对依赖中的通配符进行展开,这样目标"foo"的依赖就是所有已经存在的".o"文件,只要有".o"文件改变就会重建目标"foo".
但是如果你事先删除了当前目录下的所有".o"文件,依赖中的通配符在匹配不到任何文件的情况下,就不会进行通配展开并且保留原样,这样目标"foo"的依赖就是"*.o".因为没有"*.o"这个文件存在,所以make程序就会返回一个类似"Norule to make target '*.o'"的错误信息.这显然不是你想要的结果!
实际上我们是可以基于通配展开功能实现想要的结果的,只是需要使用一些高级技巧,比如使用函数wildcard或字符串替换.
4.3.3函数wildcard
在规则中,通配符会被自动展开,但是在变量定义和函数参数传入过程中,通配符将失效.如果你想在这些地方实现通配展开功能,那么就需要使用wildcard函数,就像下面这样:
$(wildcardpattern...)
在Makefile中,它被展开为已经存在的,使用空格分开的,匹配此模式的所有文件列表.如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空.显然,这跟规则中的通配符在匹配失败时的行为不一样,后者采取的是保留原样而不是忽略.
wildcard函数的一个用法是获取目录下的所有c文件,就像下面这样:
$(wildcard*.c)
通过替换c文件列表中的".c"后缀为".o"后缀,我们可以进一步得到".o"文件列表,就像下面这样:
$(patsubst%.c,%.o,$(wildcard *.c))
因此,可以使用如下内容的Makefile来将目录下的所有的".c"文件进行编译并最后链接成为一个可执行文件:
objects:= $(patsubst %.c,%.o,$(wildcard *.c))
foo: $(objects)
cc-o foo $(objects)
这里因为利用了隐式规则来编译c程序,所以不需要书写显式规则来每个编译".o"文件