我所认识的GNU make(4)-- 隐含规则

隐含规则就是说make程序会自动根据一些当前现有的规则推导出target,prerequisite或者是recipe,甚至是整条规则。这样你在编辑Makefile的时候就不需要每条规则都写得特别详细了,因为如果每条规则都写得特别详细的话,整个Makefile就会显得特别的繁琐和冗余。例如对于.c源文件来说,通常都会将每个.c源文件编译成相应的.o文件,如果你在Makefile中没有对当前目录下的.c源文件建立相应的规则去告诉C编译器怎么去编译这个源文件,那么make就会自动调用cc这个编译器去编译每个.c源文件编译成相应的.o文件,所以在Makefile中,很多时候我们并不需要制定编译源文件的规则,更多的是侧重于编译后的连接等操作。

当你编辑一条规则的时候如果这条规则没有recipe的话,则make会自动根据你的target和prerequisite去推导需要怎样的recipe才能通过prerequisite生成相应target。例如下面这一条规则

foo.o: foo.c

如果你不编辑相应的recipe的话,make会引用相应的隐含规则去生成相应的recipe,因为prerequisite是.c文件,所以make会使用'$(CC) $(CPPFLAGS) $(CFLAGS) -c'这样的recipe去生成相应的.o文件。对于不同的源文件make会引用不同的隐含规则来,主要是根据源文件的类型来决定。另外假设当前文件中存在两个源文件foo1.c和foo2.c,如果make遇到下面这样一条规则:

foo: foo1.o foo2.o
如果foo1.o和foo2.o文件并不存在的话,make会先尝试着去先生成foo1.o和foo2.o文件,如果这个时候Makefile中也没有生成foo1.o和foo2.o的规则,那么make就会引用相应的隐含规则根据源文件尝试生成foo1.o和foo2.o文件。因为这个时候make能够找到foo1.c和foo2.c这两个源文件,所以make就知道去调用相应的cc编译器去生成foo1.o和foo2.o这两个文件,当这两个文件生成后,就可以根据书写的规则生成目标foo。这种嵌套调用隐含规则可以称作隐含规则链(chaining)。从上面可以看出,这样利用隐含规则就在编辑Makefile的时候大大地偷懒了,而且也显得Makefile更加智能化了。

如果在Makefile中没有说明生成目标文件的话,make会根据以下的文件类型顺序调用相应的隐含规则来尝试生成想要的文件,其文件类型顺序为:

.out, .a, .ln, .c, .cc, .C, .cpp, .p, .f, .F, .m, .r, .y, .l, .ym, .lm, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .tex, .texinfo, .texi, .txinfo, .w, .ch, .web, .sh, .elc, .el.

其相应的隐含编译链接规则可以通过在一个没有Makefile的文件目录下输入’make -p'命令查看,这里就不再详述。如果在同一个目录下存在有两个文件,其文件名相同,但是文件类型不同,这个时候就应该注意了,因为make只会根据上面序列中找到的第一种类型的源文件生成目标,而不理会其他类型的源文件,如在同一个目录中存在foo.c和foo.cpp这两个源文件,prerequisite中指说明是foo.o的话,make在生成foo.o文件的时候只会引用foo.c文件,而忽略foo.cpp文件。

这些隐含规则通常会用到一些内置的变量,所以我们可以通过改变这些内置变量的值来达到改变隐含规则的目的。这些变量正常情况下并不会怎么更改,具体变量可以查看GNU make的10.3章节。

另外你还可以通过编辑一些模式规则来定义自己的隐含规则。模式规则长得和普通规则一样,也是由target,prerequisite和recipe三部分组成,唯一的区别就是模式规则的target包含有'%'字符,'%'代表部位空的任意长度的字符串,并且匹配于'%'的字符串就称作是stem,如果在prerequisite中存在有'%'则其代表的字符串和target中的'%'是完全一样的,并且所有匹配的文件必须存在或者可以被make生成。这种模式规则更多的是和前面介绍到的自动变量结合起来使用,因为正常情况下,如果target或者prerequisite中存在有'%'字符的话,编辑Makefile的时候是没法知道一些文件的全名的。如下面的规则:

%.o: %.c
就相当于将当前文件目录中所有的.c源文件编译成相应的.o文件。

如果有一个target文件匹配于多于一个的模式规则,那么make会选择最佳的模式规则来生成这个target,即选择那个stem最短的规则,如果最短的stem不止一个,则make会选择那个先出现的。当make尝试着去用文件去匹配某个模式的时候,make会先忽视这个文件名前面的目录路径,等将这个文件名移动到prerequisite中去的时候就会继续将其原来的目录路径加上去。

当一个模式规则的target只有%的时候,这个规则就叫做match-anything rule,因为它匹配与任何的文件名。这虽然有用,而且make也足够智能,但是这样执行起来确实特别耗时的,因为make需要考虑的可能情况太多了,而实际上make考虑的大部分情况都是完全没有必要的,所以为了提高make的执行效率,我们会给这种match-anything规则加一些限制:

1. 首先就是在match-anything规则的target和prerequisite之间使用双冒号,使得这个规则具有terminal性质,即告诉make只有当这条规则的prerequisite存在的时候,这条match-anything规则才有效,即make不会自己去推导如何生成prerequisite中的文件;

2. 如果你不想要这条match-anything规则具有terminal性质的话,还有一种方法可以防止make花很长时间去执行match-anything规则,就是定义一些dummy规则,如这条规则:%.c: ,如果没有这条规则的话,当make遇到match-anything规则的时候,也会将所有的.c源文件也看成是目标文件,然后各种推导怎样去更新这个.c目标文件,这完全就是浪费时间,因为.c文件一般都是开发者编写的,不是靠make生成的。这时候如果有%.c: 这条规则,因为这条规则的prerequisite是空的,所以make会一直认为这些.c目标文件都是最新的,没有必要去更新,所以就能够省去很多的时间。make里面会有一些这样的内置规则,这样就可以节省很多时间。


如果你不想make自动给某一个没有recipe的规则添加一些隐含规则,那么你可以通过给这个规则一个空的recipe(如在prerequisite后面添加一个分号)来阻止make自动推导隐含规则。


具体的make搜索隐含规则可以参考GNU make manual的10.8节。


写这个是本着大家一起学习探讨的目的,阅过的请留下您的宝贵意见,我会及时回复。

--The Magic That Brings Hardware To Life.


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值