變量和宏其實說的是同一東西。一個變量的內容是一個字符串,從一個變量名得到變量內容的過程叫作變量的擴展,用$()或者${}擴住變量名便可。而不想編程語言那樣,使用變量名就能引用變量的值。shell
make的變量有兩種:簡易變量和遞歸擴展的變量。變量的定義是一個賦值動做,把等號右邊的內容賦給左邊。這裏等號能夠有多種::=、=、?=,他們決定了怎樣賦值。等號兩邊的東西能夠只是字面值,或者含有變量。賦值時,變量名須要是明確的,等號左邊的內容當即擴展(沒有變量就保持不變),右邊的內容根據賦值符號決定什麼時候擴展。數據庫
用:=
或者::=
賦值運算符定義的是一個簡易擴展的變量。一旦make讀入該變量的定義語句,賦值運算符右邊部分會馬上擴展,而擴展後的文本會被存儲成該變量的值。變量名和變量內容加入到數據庫。MAKE_DEPEND := $(CC) -M
此變量通常被擴展爲gcc -M
然而,若是CC變量還沒有定義,則擴展爲:<space>-M
變量沒有定義不算錯誤。編程
用=
定義的變量。或者define
定義的變量。make
只會讀進賦值運算符後邊的部分,並將之存儲成該變量的值,但不會進行任何擴展的動做,擴展的動做會被延遲到該變量被使用的時候進行。編程語言
make還提供了另外兩種賦值運算符:?=
和+=
。ide
?=
運算符稱爲附帶條件的變量賦值運算符。此運算符只會在變量的值尚不存在是進行復制動做。+=
運算符稱爲附加運算符。此運算符會將文本附加到變量裏。對遞歸變量仍有用。
對於簡單變量,等效於simple := $(simple) new stuff
然而,對於遞歸變量recursive = $(recursive) new stuff
這種表達式是非法的,會被無限擴展。要想將某段文本附加到遞歸變量上,就須要附加運算符了。ui
make提供了工做目標的專屬變量。這些變量的定義會附加在工做目標上,且只有在該工做目標以及相應的必要條件被處理的時候,他們纔會發生做用。spa
工做目標的專屬變量語法以下:命令行
target...: variable = value target...: variable := value target...: variable ?= value target...: variable += value
若是在編譯某個源文件的時候須要單獨指定一個宏定義,然而該文件又在某個模式規則中:code
gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1 gui.o: gui.h
當make處理gui.o
這個工做目標時,CPPFLAGS
這個變量會附加-DUSE_NEW_MALLOC=1
,當處理完gui.o
這個目標以後,CPPFLAGS
會恢復它原來的值。遞歸
變量能夠被定義在makefile中,或是被makefile引入(include指令)
能夠直接在make命令行上定義或者從新定義變量: $ make CFLAG=-g CPPFLAGS='DBSD -DDEBUG'
每一個命令行參數中所包含的等號,都是一個變量賦值運算符。在命令行上,每一個變量賦值運算符的右邊部分必須是一個單獨的shell參數。若是變量的值含有空格,則必須爲參數加上括號或是引號。
命令行上變量的賦值將會覆蓋掉環境變量以及makefile中的賦值結果。若是要使makefile中的變量覆蓋命令行中的變量,能夠在makefile中的變量前加override指令。
當make啓動時,全部來自環境的變量都會被定義爲make的變量。這些變量具備很是低的優先級,makefile文件或命令行參數的賦值結果都會覆蓋環境變量的值。可是,可使用--environment-overrides(或-e)命令行選項,讓環境變量覆蓋相應的makefile變量。
當make被遞歸調用時,有若干來自上層make的變量會經過環境傳遞給下層的make。默認只有原來就來自環境的變量會被導出到下層的環境中。可使用export指令導出任何變量。
make會在執行一個規則的命令腳本以前當即建立自動變量。注意自動變量建立的時機。
variable := value
variable = value
variable ?= value
define variable = ... ... endef define variable := ... ... endef
define
的特長是能夠定義多行內容的變量。
一個變量的值由賦值符號右邊除去前導空格的全部字符組成。跟在全部字以後的空格不會被刪除。這有時會致使問題。
宏是之前對變量的另外一種稱呼。
能夠經過define
指令建立「封裝命令序列」,稱爲宏。在make中,宏只是用來定義變量的另外一種形式,此變量還能夠包含換行符。通常,將由define
定義的變量稱爲宏,由賦值運算符定義的變量稱爲變量。
define
後跟着變量名和換行,變量的主體是由跳格符開頭的命令行,最後以endef
結尾。
define create-jar @echo Creating $@... $(RM) $(TMP_JAR_DIR) $(MKDIR) $(TMP_JAR_DIR) $(CP) -r $^ $(TMP_JAR_DIR) cd $(TMP_JAR_DIR) && $(JAR) $(JARFLAGS) $@ . $(JAR) -ufm $@ $(MANIFEST) $(RM) $(TMP_JAR_DIR) endef
@
的做用:make將每條命令交給shell執行以前都會打印出此條命令,在該命令以前加@
可使make不這樣作。若是在命令中應用了一個宏,使用@將使整個宏擴展後的命令以前都加上@。
當make運行時,它會以兩個階段來完成他的工做。第一階段,make讀進makefile以及被引入的任何其餘makefile。這時,其中所定義的變量會被加載到make的內部數據庫,並創建依存圖。第二階段,make分析依存圖並判斷須要更新的目標,而後執行腳本。
當make在處理遞歸變量或者define指令的時候,會將變量裏的每一行或宏的主體存儲起來,包括換行符號,但不會予以擴展。宏定義的最後一個換行符不會做爲宏的一部分,不然,宏被擴展時會多一個換行。
當宏被擴展時,make會當即掃描被擴展的文本中是否存在宏或變量的引用,若是存在就予以擴展,如此遞歸下去。若是宏是在命令腳本里被擴展的,則宏的主體的每一行都會被插入一個跳格符。
下面是makefile中的元素什麼時候被擴展的原則:
=
和?=
的右邊會被延後到他們被使用時擴展,而且在第二階段運行。:=
右邊的部分會被當即擴展+=
的左邊部分本來被定義成一個簡單變量,+=
的右邊就會被當即擴展,不然,求值動做就會延後。延後擴展發生在它所在的表達式須要被擴展時,好比在規則的目標和必要條件中、在一個將要執行的命令中或者出如今簡易變量賦值的右邊等等。
OUTPUT_DIR := /tmp $(OUTPUT_DIR)/very_big_file: $(free-space) define free-space $(PRINTF) "Free disk space" $(DF) . | $(AWK) 'NR == 2 { print $$4 }' endef BIN := /usr/bin PRINTF := $(BIN)/printf DF := $(BIN)/df AWK := $(BIN)/awk
說明:
第一階段:make逐行讀取makefile並將變量加入內部數據庫,創建依存圖。OUTPUT_DIR
是簡單變量,它的值就是普通的字面值(若是這裏有$
引用的變量,將會進行擴展動做),放到數據庫中。
接下來是一條規則,規則的目標和條件都是當即擴展的,而命令是延後擴展的,保持不變。因此這條規則變爲:
/tmp/very_big_file: $(free-space)
以後是一個宏定義,宏名是當即擴展的,這裏只是字面值,不用擴展。宏體是延後擴展的,在使用該宏的時候才擴展。
最後4個簡易變量都是直接擴展的,將變量值加入到數據庫中。
第二階段:
按照後序遍歷規則樹進行規則的執行動做。這時要使用規則中的命令部分,對命令中的變量和宏進行擴展,並執行命令。
自動變量是一種make在處理規則時自動賦值的變量。
變量名 | 描述 |
---|---|
$@ |
工做目標的文件名 |
$% |
檔案文件成員結構中的文件名元素 |
$< | 第一個必要條件的文件名 |
$? |
時間戳在工做目標以後的全部必要條件,並以空格隔開。 |
$^ |
全部必要條件的文件名,並以空格隔開。 |
$+ | 如同$^ ,表明全部必要條件的文件名,並以空格隔開。不過$+ 包含重複的文件名。 |
$* |
工做目標的主文件名。文件名由主文件名和擴展名構成。 |
說明: 檔案文件中個別的成員可做爲工做目標或必要條件。能夠經過archive(member)
這樣的語法在檔案文件archive
中指定名爲member
的成員。若工做目標是foo.a(bar.o)
,則$%
是bar.o
而$@
是foo.a
。當工做目標不是一個檔案文件時,$%
是空的。