Makefile中的Tab缩进
这学期选了OOP课,写了条笔记。
makefile中的缩进的问题,要从makefile的基本结构说起:
target: prerequisite
recipe (shell commands)
注意这个缩进是个tab,而且只能是tab,不能是空格。在makefile中,tab和空格是严格区分开的。每一句recipe(就是要执行的shell命令)的开头,都必须有一个tab。而makefile中的其他东西,例如target: prerequisite
、ifeq
、变量赋值等等,前面一般不能有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