[makefile学习笔记]有关makefile中条件判断执行细节的一些讨论

最近学习c++的工程编译,学习到makefile时出现了一些自己很不理解的地方,经过一番实验总算得到答案.考虑到网上暂时没有相关的文章,我决定分享一下自己探究的结果.

我们知道,在makefile中是可以设置条件判断的,但是在学习中搜索博客能够遇到的格式大抵是这样:

ifeq($(CC),gcc) #判断体
     libs=$(libs_for_gcc)
else
    libs=$(normal_libs)
endif
foo:$(objects) #目标,二者互不嵌套
    $(CC) -o foo $(objects) $(libs)

就是很简单的先有if-else判断,再出现目标,默认执行标签之前lf-else会被执行.但是显然实际的工程不是这样.我在阅读linux的makefile源码时显然不是这样.比如这里节选了一段linux源码的makefile:

ifdef CONFIG_MODULES  #判断体
all: modules  #内嵌目标
ifdef CONFIG_MODVERSIONS  #目标内嵌判断
  KBUILD_BUILTIN := 1
endif
else 

KBUILD_BUILTIN := .... #变量定义
KBUILD_MODULES := 1
....
prepare: #目标
    ......
help:
	.....

endif # KBUILD_EXTMOD

这里省略了内容,很明显,在if里面出现了数个嵌套的判断和目标.那这些标签究竟以什么标志结束?它们有时怎么执行的?

首先阐明结论:

①在目标内嵌套判断和在判断内嵌套目标均是允许的,但是不允许缩进.

②目标内嵌套判断,外圈的目标只要遇到新的目标声明或者变量定义就会立即结束.[即使判断体还没结束,注意变量定义和目标声明必须不缩进,否则会被识别为shell指令]

③判断内内嵌目标,内部的目标以遇到变量定义或者遇到else/endif为结束条件.

④执行一个目标之前,会执行所有的没有被嵌套的判断,即使这个判断在目标的后面.

⑤如果在某个目标引用了一个嵌套在判断的目标,而这个目标因为判断条件不满足而不可达,会报错.

但是干说显然时不好理解的,我们下面就来上案例:

实例探究

第一条很好理解,但是判断关键字(ifxx,else,endif)和目标声明都是不允许缩进的.例如以下是一个反例

ifdef as
    targ1:#不允许缩进,应将此行的tab删除
        echo asdfg
endif

执行会报错

makefile:2:commands commence before first target. stop.

第二条让我们来进行实验:目标内嵌套判断结束符的分界:外圈的目标只要遇到新的目标声明或者变量定义就会立即结束.

案例1:

targ1:
	echo "jjj" 
ifdef var1:#var1已定义
	echo "ppp" 
res:=no
	echo "lll" #报错:8: *** recipe commences before first target.  Stop.
endif

案例2:

targ1:
	@echo "jjj"
ifdef var1: #var1已定义
tf:
	@echo "lll"
endif  #最终输出:jjj

从案例1当中,我们看出,res:=no赋值语句执行后,目标targ1即告结束,此时因为echo "lll"没有了目标归属,因此会报错缺少目标声明.把第6行删除,第2,4行能够被正常执行.

从案例2当中,我们看出,因为出现了新的标签声明,因此targ1的执行即告结束,目标tf会直接被跳过不执行,直接到达endif.这也表明,在判断体内的目标,除非指明了要执行它,或者要执行的目标依赖了它,否则都会直接跳过不执行.

第三条:判断内内嵌目标,内部的目标以遇到变量定义,新的目标声明或者遇到else/endif为结束条件.有了前面的举例,大家这个应该比较清楚了.下面是例子:

ifdef var1  #var1已定义
targ1:
	echo sss
else  #targ1结束
	echo ppp 
endif

此时若var1已经定义,输出sss,而var1未定义,输出错误5: *** recipe commences before first target. Stop.后者表明echo ppp这一行被孤立了,证明此原则的正确性,但是注意到如果判断没走到这孤立的一行却可以正确执行.

第四条:执行一个目标之前,会执行所有的没有被目标嵌套的判断,即使这个判断在目标的后面.

ifdef var1   #var1已定义.判断体1
A:=1 
endif

targ1:
	echo jjj

ifdef var1 #判断体2
B:=2
endif

targ2:
	echo $(A) $(B) $(C)  
D:= 4 #此条指令用于作为targ2的结束符

ifdef var1  #判断体3
C:=3
endif

我们执行命令 make targ2 指定执行目标targ2,最终输出1 2 3,表明三个判断体全部被执行.值得注意的是,判断体3不在目标内且在目标后面,但是仍然被执行了.

第五条:如果在某个目标引用了一个嵌套在判断的目标,而这个目标因为判断条件不满足而不可达,会报错.

ifndef var1 #var1已定义,所以这个判断体不会被执行
targ1:
	echo sss
endif

targ2:targ1 #报错:No rule to make target 'targ1', needed by 'targ2'.  Stop.
	echo  lll

很显然,targ1是不可达的,而它又被targ2所需要,若指定执行make targ2,就会报错.

参考博客:

Makefile: (实验) 目标命令的结束标志 - JavaShuo

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值