1. 程式人生 > >makefile之變數

makefile之變數

一、變數定義

makefile中的變數其實本身是字串,它的值同樣是字串。在引用變數時,變數會被其值所取代(類似於C語言中的巨集變數,是嚴格按文字替換來進行的)。

二、變數引用

對於變數的引用,推薦只使用$(變數名)${變數名}這兩種方式。

三、變數的分類

makefile中的變數根據獲得其值方式的不同大致可以分為兩類:遞迴擴充套件變數recursively expanded variable)和簡單擴充套件變數simply expanded variables)。它們主要的區別在於兩點:1.定義方式的不同。2.引用時的展開方式不同。

遞迴擴充套件變數:遞迴擴充套件變數是用”=“或者指示符define定義的變數。

特點:這種型別的變數所引用的其它變數在定義時是不會被展開的,只有在make程式在生成某個目標使用到這個變數時,其中的變數引用才會被遞迴地全部展開,直到沒有任何變數引用為止。

優點:

1. 可以引用在其之後定義的變數。例如:

那麼在後續規則中使用CFLAGS變數的地方,會將變數include_dirs展開,即最後CFLAGS的值為-Ifoo -lbar -O,而不是-O。

缺點:

    1. 如果引用自身就會導致遞迴死迴圈(最新版本的make都會發現這種錯誤而提示錯誤退出)。

    2. 如果變數定義中有引用函式,那麼每次擴充套件時都會呼叫該函式,這勢必影響make的執行效率。此外,還會使得wildcard和shell函式返回不可預期的結果。

簡單擴充套件變數:簡單擴充套件變數使用“:=”或者“::=”定義的變數。

特點:這種型別的變數在定義時就會一次性完成所有引用變數的擴充套件和引用函式的執行,即變數的值在定義時就已經確定。

優點:

    1. make程式執行效率高,語法更簡潔。

    2. 允許使用變數原有的值來重新定義自己。例如: CFLAGS := $(CFLAGS) -O,完全沒有問題的。

缺點:

1. 不可以引用在其之後定義的變數。例如:

    CFLAGS := $(include_dirs) -O

    include_dirs := -lfoo -lbar

因為include_dirs的定義出現在CFLAGS之後,因此在CFLAGS的定義中,變數include_dirs的值為空,那麼CFLAGS的值為-O,而不是-lfoo -lbar -O,這是與遞迴擴充套件變數最大的區別。

綜合以上兩者的比較,建議在makefile中使用簡單擴充套件變數。因為簡單擴充套件變數的使用方式和絕大多數程式語言中的變數使用方式基本上相同。它可以使一個比較複雜的makefile在一定程度上具有可預測性。而且這種變數允許我們利用之前所定義的值來重新定義它(比如使用某一個函式來對它以前的值進行處理並重新賦值),此方式在makefile中經常用到。儘量避免和減少遞迴擴充套件變數的使用。

三、條件賦值操作符(conditional variable assignment operator)

?=操作符在makefile中被稱為條件賦值操作符,其只有在變數沒有定義的情況下才會對變數進行定義並賦值。例如

FOO ?= bar

完全等同於

ifeq ($(origin FOO), undefined)

    FOO = bar

endif

再來具體看一個例子如下:

輸入make -s,結果如下:

i2c_support=yes
i2c_support is undefined

如果去掉第三行的註釋,make -s,結果如下:

i2c_support=
i2c_support has no right value

從這個例子可以得出如下結論:

(1)?=只有在變數沒有定義的情況下才會定義變數並賦值

(2)ifdef var表示式只有在var的值不為空(只要賦值符號後面有值即可)的情況下才為真。

四、追加賦值操作符

我們可以使用“+=”來給變數追加值。例如:

等同於如下過程:

只不過前者更簡潔。

關於使用追加操作符”+=“,我們只需瞭解下面三點就OK。

(1)如果被追加的變數之前是沒有定義過的,那麼此時“+=”相當於“=”,即變數預設被make認為是遞迴擴充套件變數。

(2)如果變數是簡單擴充套件變數,那麼“+=”會首先展開變數的值,然後在末尾新增需要追加的值,並使用“:=”重新給變數賦值。過程如下:

等同於:

(3)如果變數是遞迴擴充套件變數,那麼“+=”不會對變數中的任何引用進行展開,而是嚴格按照文字方式替換,然後在末尾新增需要追加的值,並使用“=”重新給變數賦值。但是,等等,這樣不是會出現引用自身從而導致出現無限迴圈的問題嗎?不用擔心,make會通過引入中間變數的方式解決這個問題。過程如下:

等同於:

可以看到,make通過引入從未使用過的temp變數解決無限迴圈的問題。

重要結論:通過(2)(3)兩點我們發現,“+=”操作符會繼承變數的型別(即不會改變變數的屬性)。

可以通過以下一個簡單例子來理解“+=”操作符的這種繼承特性:

例如當前makefile中有如下內容:

定義遞迴擴充套件變數CFLAGS,並且引用變數includes。根據遞迴擴充套件變數的特性,知道定義CFLAGS時不會發生任何擴充套件,即此時可以實際沒有定義變數includes,只需要保證在第一次引用CFLAGS之前定義includes即可。

那麼下面的兩種追加方式有到底有什麼區別呢,會引發什麼問題呢?

              

在這裡使用“:=”操作符最大的問題是導致CFLAGS變數從遞迴擴充套件變數變成了簡單擴充套件變數。導致的直接後果就是make會擴充套件“$(CFLAGS) -pg”,因為變數includes還未定義,因此最終擴充套件結果是“-O -pg”。如果在這之後定義includes變數,已經不會影響CFLAGS了。相反,使用“+=”操作符就不會出現這種問題,因為它會保留變數的引用。