Makefile中的tab缩进

Makefile中的Tab缩进

这学期选了OOP课,写了条笔记。

makefile中的缩进的问题,要从makefile的基本结构说起:

target: prerequisite
	recipe (shell commands)

注意这个缩进是个tab,而且只能是tab,不能是空格。在makefile中,tab和空格是严格区分开的。每一句recipe(就是要执行的shell命令)的开头,都必须有一个tab。而makefile中的其他东西,例如target: prerequisiteifeq、变量赋值等等,前面一般不能有tab。也就是说,开头有没有tab是区分“makefile中的shell命令(recipe)”和“makefile中的其他语句”的标志。二者作用不同、语法不同,可以说是两套系统,大家一定要严格区分开。

例1:变量

例如,makefile中的变量和shell中的变量,是两种不同的东西。举个例子:

VAR=foo
all:
	VAR=bar; echo $$VAR; echo $(VAR)
# 这里分号的作用:在同一行写多条shell命令

这个makefile运行后,输出的是:

VAR=bar; echo $VAR; echo foo
bar
foo

让我们逐条分辨一下这个makefile中每一个变量“VAR”的身份。

语句 功能
VAR=foo 定义一个makefile变量"VAR",赋值为"foo"
VAR=bar 执行shell命令VAR=bar,即:定义一个shell变量"VAR",赋值为"bar"
echo $$VAR 执行shell命令echo $VAR(makefile中打两个$是为了转义,
相当于在shell中打了一个$),即:输出shell变量"VAR"的值
echo $(VAR) 将$(VAR)替换成makefile变量"VAR"的值后,执行shell命令,
即:执行shell命令echo foo

请关注VAR=foo一句和VAR=bar一句的区别:正是前面有无tab的区别,导致前者是makefile变量操作,而后者是规则(rule)all: ...中的一条recipe。

例2:ifeq

另外需要注意的是,在ifeq之类的控制语句的语句体中,是不需要额外缩进的。ifeq的作用范围,由endif的位置决定;而决定是否缩进的,只有recipe和非recipe的区别。举个极端的例子:

all:
	echo $(VAR)

FLAG=TRUE
VAR=foo

ifeq (TRUE, $(FLAG))
	VAR=bar
endif

上面这个makefile中,如果倒数第二行想要操作makefile中的VAR变量的话,用tab缩进是不对的,应该直接顶格写,或者打若干空格也行(只要没有tab,效果就和没缩进一样)。

但是,如果对上面这个“错误”的代码运行make,依然会输出和顶格写相同的结果:

echo bar
bar

也就是说,尽管倒数第二行的开头有tab,VAR=bar仍然被识别成makefile变量操作,而不是shell命令——这是因为它的位置比较特殊,不属于任何规则,所以没被识别成shell命令。

但是,一旦我们在ifeq前面插入一条规则:

all:
	echo $(VAR)

FLAG=TRUE
VAR=foo

dummy:

ifeq (TRUE, $(FLAG))
	VAR=bar
endif

再运行make,输出就会变成:

echo foo
foo

这是因为VAR=bar一句这次被识别成dummy: ...这条规则中的一条recipe了。为了验证,可以运行make dummy试试,果然输出了VAR=bar,这意味着VAR=bar一句确实被当做shell命令执行了。

为了避免这样令人迷惑的事情,我们写makefile的时候,还是不要在非recipe语句的前面加tab了,直接写成:

all:
	echo $(VAR)

FLAG=TRUE
VAR=foo

dummy:

ifeq (TRUE, $(FLAG))
VAR=bar
endif

就可以保证无论有无dummy:一行,输出结果都是:

echo bar
bar
posted @ 2021-03-16 13:27  胡小兔  阅读(112)  评论(0编辑  收藏