目录
????????你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是 在规则中,目标的定义需要有"%"字符。"%"的意思是表示一个或多个任意字符。在依赖目标 中同样可以使用"%",只是依赖目标中的"%"的取值,取决于其目标。
????????有一点需要注意的是,"%"的展开发生在变量和函数的展开之后,变量和函数的展开发 生在 make 载入 Makefile 时,而模式规则中的"%"则发生在运行时。
????????模式规则中,至少在规则的目标定义中要包含"%",否则,就是一般的规则。目标中的 "%"定义表示对文件名的匹配,"%"表示长度任意的非空字符串。例如:"%.c"表示以".c"结 尾的文件名(文件名的长度至少为 3),而"s.%.c"则表示以"s."开头,".c"结尾的文件名(文 件名的长度至少为 5)。
???????? 如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值,也就是说,目标中的模式的"%"决定了依赖目标中"%"的样子。例如有一个模式规则如下: %.o : %.c ;
其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标 是"a.o b.o",那么"%c"就是"a.c b.c"。
???????? 一旦依赖目标中的"%"模式被确定,那么,make 会被要求去匹配当前目录下所有的文件名, 一旦找到,make 就会规则下的命令,所以,在模式规则中,目标可能会是多个的,如果有 模式匹配出多个目标,make 就会产生所有的模式目标,此时,make 关心的是依赖的文件名 和生成目标的命令这两件事。
????????下面这个例子表示了,把所有的[.c]文件都编译成[.o]文件.
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
???????? 其中,"$@"表示所有的目标的挨个值,"$.y"执行,然后生成 ".tab.c"和".tab.h"文件。(其中,""表示一个任意字符串)。如果我们的执行程 序 "foo" 依 赖 于 文 件 "parse.tab.o" 和 "scan.o" , 并 且 文 件 "scan.o" 依 赖 于 文 件 "parse.tab.h",如果"parse.y"文件被更新了,那么根据上述的规则,"bison -d parse.y" 就会被执行一次,于是,"parse.tab.o"和"scan.o"的依赖文件就齐了。(假设,"parse.tab.o" 由"parse.tab.c"生成,和"scan.o"由"scan.c"生成,而"foo"由"parse.tab.o"和"scan.o" 链接生成,而且 foo 和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足)
????????在上述的模式规则中,目标和依赖文件都是一系例的文件,那么我们如何书写一个命令 来完成从不同的依赖文件生成相应的目标?因为在每一次的对模式规则的解析时,都会是不 同的目标和依赖文件。
????????自动化变量就是完成这个功能的。在前面,我们已经对自动化变量有所提涉,相信你看 到这里已对它有一个感性认识了。所谓自动化变量,就是这种变量会把模式中所定义的一系 列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现 在规则的命令中。
???????? 下面是所有的自动化变量及其说明:
$@
????????表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于 目标中模式定义的集合。
$%
????????仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a (bar.o)",那么,
"$%"
????????就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix 下是[.a],Windows 下是[.lib]),那么,其值为空。
$<
???????? 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$很有可能不兼容于其它版本的 make,所以,你应该尽量避免使用"$*",除非是在隐含规则 或是静态模式中。如果目标中的后缀是 make 所不能识别的,那么
"$*"
????????就是空值。 当你希望只对更新过的依赖文件进行操作时,
"$?"
????????在显式规则中很有用,例如,假设有 一个函数库文件叫"lib",其由其它几个 object 文件更新。那么把 object 文件打包的比较 有效率的 Makefile 规则是: lib : foo.o bar.o lose.o win.o ar r lib $? 在上述所列出来的自动量变量中。四个变量($@、$"$(+D)" "$(+F)" 分别表示所有依赖文件的目录部分和文件部分。(可以有相同的) "$(?D)" "$(?F)" 分别表示被更新的依赖文件的目录部分和文件部分。 最后想提醒一下的是,对于"$
????????一般来说,一个目标的模式有一个有前缀或是后缀的"%",或是没有前后缀,直接就是 一个"%"。因为"%"代表一个或多个字符,所以在定义好了的模式中,我们把"%"所匹配的内 容叫做"茎",例如"%.c"所匹配的文件"test.c"中"test"就是"茎"。因为在目标和依赖目标 中同时有"%"时,依赖目标的"茎"会传给目标,当做目标中的"茎"。
???????? 当一个模式匹配包含有斜杠(实际也不经常包含)的文件时,那么在进行模式匹配时, 目录部分会首先被移开,然后进行匹配,成功后,再把目录加回去。在进行"茎"的传递时, 我们需要知道这个步骤。例如有一个模式"e%t",文件"src/eat"匹配于该模式,于是"src/a" 就是其"茎",如果这个模式定义在依赖目标中,而被依赖于这个模式的目标中又有个模式 "c%r",那么,目标就是"src/car"。("茎"被传递)
????????你可以重载内建的隐含规则(或是定义一个全新的),例如你可以重新构造和内建隐含 规则不同的命令,如:
%.o : %.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) -D$(date)
你可以取消内建的隐含规则,只要不在后面写命令就行。如:
%.o : %.s
????????同样,你也可以重新定义一个全新的隐含规则,其在隐含规则中的位置取决于你在哪里 写下这个规则。朝前的位置就靠前。