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