我所認識的GNU make(3) -- 變量

在Makefile中,變量的值都是一個字符串,所以在Makefile中,變量更像是C語言中的宏定義,當使用到這個變量的時候簡單地將這個變量的值(字符串)替代變量所在的位置。變量可以存在於target,prerequisite和recipe中的任何地方,在Makefile中,定義變量的方式和C語言類似,在使用變量的時候,都需要在這個變量的前面加一個‘$'符號(如果你想表示一個'$"符號,則需要用兩個'$'表示,即$$),不知道爲什麼選這個符號,可能是當時Stuart Feldman這位大神當時在開發make時比較缺money吧,或者是想跟shell中的變量引用保持一致吧。有時候,爲了顯示更加清晰會用圓括號或者花括號將'$'符號後面的變量名用圍起來。

一、普通變量

Makefile中的變量可以分爲以下兩種類型,它們的區別在於變量的展開方式。

1. 循環展開變量,這種類型的變量在需要對其進行展開的時候,如果該變量的值會使用到其他變量,那麼引用到的其他變量的值也將會一同被展開,直到沒有可繼續展開的變量。這種類型的變量用簡單的等號’=‘或者define指令來進行定義。這種變量的好處就是你可以在定義變量的時候繼續引用其他的變量也不需要擔心make無法解析出該變量的值,但這也是它的不足,有時候你只是希望這個變量只是以你定義它時的那個字符串展開,即保留着引用其他變量的形式,這時候你就只能使用簡單展開變量了;

2. 簡單展開變量,這種類型的變量當使用的時候,它就會進行簡單的展開,其展開形式和你定義它時使用的字符串完全一樣,即使裏面包含了其他的變量,make並不會將這些變量當作真正的變量看待,而只是簡單的字符串。也就是說當make看到簡單展開變量的時候,只會將其進行第一層的展開,如果你想要將其完全展開的話,則需要對簡單展開變量進行多次的展開。這種類型的變量可以用冒號加等號的形式定義,即':='或'::='。

一些常用的變量賦值符號:

= 循環展開變量賦值符號
:=或::= 簡單展開變量賦值符號
?= 只有當這個變量還沒有被定義的時候這個賦值纔會生效,並且被賦值的變量屬於循環展開變量
!= shell賦值運算符
+= 用於添加文本到某一個變量中

當然還可以用define指令去定義變量,這種方法可以使變量的值中包含換行字符,這種方法好像不是很常用,可以自己去參考GNU make manual.

當make程序啓動的時候,所有make看到的系統環境變量make都會按照原來系統環境變量的名字創建一個名字和變量值完全一樣的make變量,並且如果你在Makefile中有對這些變量重新賦值或者更改,那麼對這些變量的更改將覆蓋其原來的值。也就是說你可以在Makefile中對這些看似環境變量的值進行更改而不要擔心會對系統環境進行更改,因爲更改的其實是make變量。

Makefile中變量一般都是全局變量的,這些變量不僅僅作用於當前的Makefile中,還可以通過prerequisite繼承延續到其他的Makefile中,如果你不想要當前Makefile中的變量繼承到其他的Makefile的話,則可以在定義變量的時候給變量加一個private的標記。在Makefile中,變量的值一般都是全局變量,除非這個變量是目標指定變量(target-specific variable)或者自動變量。目標指定變量就是說你可以根據不同的目標對某一個變量進行賦值,並且該變量只有在制定的目標的規則裏面纔有效(包括規則的prerequisite,及其prerequisite的prerequisite),其實就是某一條規則的局部變量,只是變量名會和其他規則的局部變量相同,但實際上它們在不同的規則裏面是項目獨立的變量,互不干擾。這樣你就可以根據不同的目標給變量賦不同的值,定義目標指定變量的形式如下:

target ...: target-specific variable assignment
如果你想目標指定變量不影響到該規則的prerequisite,那麼你可以將這種變量標記爲private,這樣這個變量只有local的規則可以看見,它的prerequisite是看不見這個變量的。

類似的你可以定義模式指定變量,其形式如下:

pattern ...: pattern-specific variable-assignment
這種變量和目標指定變量類似,只是其作用的範圍是匹配於某種模式的規則。

變量的一個實用小功能:

替換,即你可以通過$(var:a=b)這樣的形式將變量var中所有以a字符結尾的文件名都替換成b,如假設變量var是當前目錄下所有c文件,並且每個.c文件都需要編譯生成相應的.o文件,則可以通過$(var:c=o)就可以簡單得得到所有想要的.o文件名,而不需要逐個編輯。

另外GNU make裏面還提供了很多字符串操作的函數,其實很多就是對變量的值(字符串)的一些操作,make中函數的引用跟變量的引用很相似,其形式爲

$(function arguments) or ${function arguments}

如果你在執行mke程序的時候在shell命令行裏面順便去賦值某個變量,那麼這個賦值動作將會覆蓋Makefile中對這個變量的賦值,即make將會忽略Makefile中對這個變量的更改,除非你在Makefile中定義變量的時候在其前面加上override標記。這種功能應該挺少用到,所以這就提到爲止。

另外make還會自己定義一些具有特殊意義的變量,如果想了解更多這些變量可以查詢GNU make manual的6.14章節。


二、自動變量
自動化變量使得Makefile編輯起來更加的智能和簡潔,只是剛開始的時候看這些自動變量確實不方便,跟看天書似的。自動變量是make自己定義很更新的,每當make執行一條新的規則的時候,make都會根據這條規則的target和prerequisite對這些自動變量進行計算和更新,所以自動變量類似於上面說到的標記爲private的目標指定變量,但是其作用範圍更加嚴格,其作用範圍是每條規則本身的recipe,在target和prerequisite中都無效。以下是make中的自動變量:

$@          target的文件名。如果這個target是一個archive成員,那麼'$@'就是那個archive文件名。如果在模式規則中,則'$@'就會被賦值成所有可以導致這條規則被執行的target文件名序列。
$% 當target是archive的時候,$%就是相應的archive member
$< 第一個prerequisite的名字。如果target通過隱含規則得到它的recipe,那麼$<就是隱含規則添加的第一個prerequisite的名字。
$? 所有比target更新的prerequisite名字序列,名字之間用空格隔開。
$^ 所有prerequisite的名字序列,名字之間用空格隔開,並且make會自動將其中重複的名字去掉,即每個名字最多隻有一份。
$+ 該變量和$^完全相同,除了它不會對這些名字序列去重,所以相同的名字可能出現多次。
$| 所有順序(order-only)prerequisite的名字序列,中間以空格隔開。
$* 隱含規格匹配的stem

注:

1. archive文件,一般是指將多個程序和數據打包到一起的文件,如軟件包,archive文件所包含的子文件就叫做archive成員,archive成員和archive文件一般由ar程序來處理,其目的是創建一些library之類的文件。某一個archive成員可以作爲make規則中的target或者prerequisite,其出現的形式爲: archive(member);這東西在Makefile裏面好像並不是很常見。

模式規則:匹配於某種模式的規則,將在隱含規則中做解釋。

stem:即模式匹配中匹配的那一部分字符串。

上面所述的這些自動變量其所包含的文件其實不僅僅包含了文件的文件名,還包含了該文件的目錄名,所以可以通過通過$(@D)來獲取$@這個變量的目錄,即在變量的名字後面加一個D,或通過$(@F)來獲取$@這個變量的文件名部分,即在變量名的後面添加一個F。


寫這個是本着大家一起學習探討的目的,閱過的請留下您的寶貴意見,我會及時回覆。

--The Magic That Brings Hardware To Life.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章