我所认识的GNU make(2) -- make规则

make规则的基本形式

target : prerequisites

        recipe

       

       

基本规则包括3方面targetprerequisitesrecipetarget就是这条规则的人生意义了,prerequisites就是要实现这个target的必要条件,一般就是指必须有哪些源文件或者目标文件才能实现这个targetrecipe就是实现这一target的方法,一般就是一些shell脚本。一条规则告诉make什么时候target需要更新,并且如何更新,当然这些规则都是编辑在Makefile里面的。

 

一、target

 

在一个Makefile里面可以定义多条make规则,从而实现多种目的,但是每次通过make程序调用只能实现一个终极的target,如果你在执行make的时候没有明显地指明make的终极target(如 make build_all),而只是简单地输入make命令,那么make将会以它在Makefile中找到的第一个target作为它这次执行的最终target。当然这个终极target可能会将其他规则的target作为自己的prerequisite。这样就意味着只能实现一个终极的target并不代表着只能执行一条规则,如果prerequisite中包含了其他的规则的target,那么那些被作为prerequisitetarget所在的规则也将可能被执行。这里说可能是因为,make程序判断某一条规则的recipe是否需要执行是根据prerequisite的更新时间和target的更新时间来判断的,即如果prerequisite中存在有比target更新的文件,那么make就会认为target已经过时了,需要重新执行recipe生成所要的target

 

如果你的target是不可能存在的一个文件时,这种规则的recipe肯定会被执行,这种类型的target可以用来完成某种操作,其作用类似于其他编程语言中的函数。如经常有Makefile就会定义clean这样的target用于完成对编译链接产生的中间文件的清理操作。

 

Makefile中一般会存在两种target,一种就是正常的target,另外一种则叫做phony targetPhony target的字面意思就是假的target,当遇到phony target的时候make会直接去执行recipe,而不检查target是否存在,target是否比prerequisite更新。这样的target存在有两个意义:

1. 避免target和当前文件夹中存在的文件重名。因为如果target和当前文件夹下的某个文件重名的话,make会错将这个文件当作之前make执行这个规则时生成的目标文件,会根据这个文件和prerequisite的更改时间关系去决定是否执行recipe,这样可能会造成一些无法预测的结果;

2. 性能提升,当make的目标是phony target的时候,make将会忽略可能的隐含规则,而直接去执行recipe。隐含规则需要make的智能推导,一般智能推导都会需要较多的运算,所以直接忽略隐含规则可以带来性能上的提升。而且省去隐含规则或会让Makefile调试起来更加清晰明了。

 

另外还有一些Build-intarget,这些target都有固定的名字和特殊的用途,可以到manual里面的4.8节去具体查找。

 

二、prerequisite

 

prerequisite就是实现这个target所必须要的文件列表,可以是任何文件,并且只要其中的一个文件在上一次target生成之后更改,make程序就认为这个target已经过时了,需要将recipe传给shell,重新执行一遍。当然,如果target这个文件不存在的时候,recipe就肯定会被执行。对于phony target来说,make不会去考虑prerequisite的更新时间,而总是直接去执行recipe,所以phony target也就没有填写prerequisite的必要了,这就是为什么一般情况下phony targetprerequisite都会是空着的。如果有个规则只有target而没有prerequisite的话,如果这时候target文件存在,这时候make就会认为这个target是最新的,而不去执行recipe

 

当工程比较大的时候,一般文件都会分开放在很多个文件夹里面,当你在当前目录下执行make的时候,make程序会先去当前目录下寻找在targeprerequisite中所列出来的文件。如果找不到,则会继续去VPATH变量中所包含的目录下去寻找,直到找到想要的文件或者遍历完所有的目录。VPTAH是个变量,所以你可以根据自己的需求对VPATH进行更改。或者也可以利用vpath(大小写敏感)指令和模式匹配告诉make哪种类型的文件到哪个目录下寻找。

有时候一个源文件包含了很多个头文件,如果我们希望如果任何头文件如果有变化的话,那么这个源文件就需要被重新编译,这时候就需要将所有的头文件都写到相应规则的prerequisite中去,导致prerequisite列表很长,而且如果每个源文件都这样的话维护起来特别不方便,现在的编译器可以自动得将这些头文件自动添加到prerequisite序列中,只要你加一个-M选项如:cc -M main.c。另外如果你不希望将系统头文件(如stdio.h, time.h等)被包含在这个prerequisite序列中,只希望自己编辑的头文件包含在其中,那么可以使用-MM选项即cc-MM main.c

 

三、recipe

 

recipe其实就是make程序传递给shell的命令, 能看懂shell脚本的就没什么问题了。make对这一部分的操作就是在将这些shell命令传给shell之前对其中引用到的一些Makefile变量进行展开。另外需要注意几点:

1. 正常情况下,当makerecipeshell命令传递给shell执行的时候,shell都会将要执行的命令回显出来,但如果你在shell命令之前加‘@’字符,那么这条命令在执行时将不会回显出来,这就是为什么Makefile中那些打印字符串信息都命令都会以‘@’字符开头,如@echo hello

2. recipe的每一行都必须以一个特殊的字符开始 (除非你将recipe直接放在prerequisite后面,并用分号和prerequisite隔开),make才会认为这一行是recipe的一部分,这个特殊字符存放在在.RECIPEPREFIX这个内置的变量中,当然你可以根据自己的喜好去更改这个设置,但是没看到有任何必要;

3. 默认情况下recipe中命令是并行执行的,即每一行命令都会传给一个独立的sub-shell,然后这些sub-shell各自独立执行分配给自己的命令,并行执行并且互不干扰,(如你用cd命令切换到某个目录下,其他命令行并不会跑到那个目录下去执行)这样并行可以提升make的执行性能。但是,这也会带来很多问题,特别是对于那些对上下文关系敏感的recipe。为了让recipe的命令能够顺序执行,通常情况下,我们会在某一行命令的后面加一个反斜杠,让该行命令行和下一行连接成一行,如果整个recipe里的命令行后面都加了反斜杠,那么整个recipe就相当于只有一行命令,并且只传给一个sub-shell,这就相当于串行执行recipe了。在很多Makefile里面经常能看到很多命令行后面都会添加反斜杠就是这个原因了。

4. 可以通过设置-j选项来设置执行recipe时的并行度,但是这个看样子用起来挺复杂的,目前还没有看到哪个地方有用到,所以也就没有深究,有兴趣的可以好好研究一下GNUmake manual,当然里面涉及到挺多Linux/Unix进行并行的东西,需要一定的功底才能真正理解。

5. 正常情况下,如果执行某个recipe的命令行的时候出现错误的话,make就会退出,但是有些错误是可预知的,这时候可以通过在命令行前面添加一个‘-’号,这样就可以告诉make程序忽略这条命令执行所产生的错误,而继续执行下面的命令。

6. 当一个工程比较大的时候,如果你想要调用一个子模块的Makefile时,可以在recipe里面执行切换目录和调用make程序的命令来执行子目录Makefile文件所规定的规则,并且不同位置的Makefile之间可以通过exportunexport指令来打开和关闭变量的传递。

7. 空的recipe存在是为了防止make自动去推导一些隐含的recipe,所以空的recipe还是有存在的意义的。

 

 

另外对于make规则还需要注意到是:

1. 如果一条规则中包含了多个target,那么这条规则等价于多条简单的规则,每个规则只包含一个target,并且拥有相同的prerequisiterecipe,所以对于这样的规则,有多少个target那么recipe也将被执行多少次,如果recipe中使用到了自动变量,那么每次执行的效果就很可能会不一样。

2. 如果多条规则的target相同的话,那么这几条规则的将会合并成一条规则,其中的prerequisite将会被合并。由于每个target只能使用一个recipe,所以如果其中有不止一条规则拥有recipe,那么make将会使用最靠后的那个recipe,并且打印出一条错误信息。

 

GNU make manual里面还提到一些其他的规则,如在targetprerequisite之间的双冒号(double-colon)使得每个相同target的规则各自独立,顺序prerequisite等,这些都不怎么用,所以这里就不具体说了。

 

 

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

--The Magic That Brings Hardware To Life.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值