makefile必知必會(II)
定義變量
makefile的變量定義有三種方式
1. 立即賦值 a:=b
2. 延遲賦值 a=b
3. 條件賦值 a?=b
4. 附加賦值 a+=b
它們之間的區別是,
第一種方式,會立即計算b的值,並賦值給a;
第二種方式,相當於C++和java的引用。如果後面b的值改變了,那麼a的值也會改變;
第三種方式,如果a沒有定義,則相當於a=b ,否則不執行任何操作;
第四種方式,將b的值添加到a原有的值後面,再賦值給a。
獲取變量值
$(var) //表示取變量var的值,記得當變量名多於一個字符時,使用”()”.
定義宏
define function
xxx // 具體的內容
endef
需要指出的是,雖然形式上類似函數,但是實際內容只是字符串替換,這與C中的宏函數是一樣的。
什麼時候計算變量
makefile的解析分爲兩個階段,第一階段生成規則和依賴關係,第二階段執行規則;
只有立即展開的變量會在第一階段計算,而延後展開的變量會在第二階段計算。
立即與延後展開的規則 |
||
表達式 |
何時擴展a |
何時擴展b |
a=b |
立即 |
延後 |
a?=b |
立即 |
延後 |
a:=b |
立即 |
立即 |
a+=b |
立即 |
取決於之間a的定義方式 |
define a |
立即 |
延後 |
a : b |
立即 |
立即 |
eval
有兩個特點
1. 做兩次變量展開;以$(eval var),首先會對var做一次變量展開,然後對展開後的結果,再執行一次變量展開。詳細的描述請各位參考鏈接
2. $(eval var)返回爲空。這個特性被大量使用於宏定義define中。
看下面這個例子:
define import_target
include $(1)
_PREFIXID := $(if$2, $2, TARGET)
$(_ PREFIXID)
endef
按上面定義的宏,當去計算a:=$(call import_target)時,幾乎總是會報錯。
原因是宏定義只是簡單的做字符串替換,一經替換後,原來看起來是一行語句,一下子就變成多行,從而導致makefile解析錯誤。
於是只能使用“\”將各個語句連接爲一行。
define import_target
include $(1) \
_PREFIXID := $(if $2, $2,TARGET) \
$(_ PREFIXID)
endef
這樣做相當於
include $(1) _PREFIXID := $(if $2, $2, TARGET) $(_ PREFIXID)
顯然,肯定還是報解析錯誤。
最終解決方案,將各個子語句使用$(eval )包裹,
define import_target
$(eval include $(1)) \
$(eval _PREFIXID := $(if $2,$2, TARGET)) \
$(_ PREFIXID)
endef
解析時,makefile先對$(1)做展開,假設結果爲xxx,這是第一次;然後執行include xxx,這是第二次展開。執行完後,整個$(eval include $(1))表達式返回值爲空。這樣解析錯誤解決了,而且 import_target的返回結果又正好是_ PREFIXID的值。
call
格式:$(call func,param1, param2,..)
實現自定義函數調用。func使用define定義。 call會自動將param1的值賦值給func中的$1變量,將param2賦值給$2變量,依次類推。
小心空格
變量賦值a:= b, 不會將b前面的空格賦值給a
大部分函數調用,特別是$(call func, param) 如果參數前面有空格,則會將空格連同參數一起傳入。因此要特別小心。
使用$(strip var)是個好習慣,可以避免不小心引入前置或者後置的空格導致的問題。