我們也說說Android.mk(3) - 宏
傳統(tǒng)上我們一直稱這種東西為makefile中的變量,其實(shí)本質(zhì)上就是一個(gè)宏,只是做的是字符串替換。我們何如就把它叫做宏呢。
宏的命名
makefile的宏可以包含字符、數(shù)字、下劃線。需要注意的一點(diǎn)是,宏就是宏,反正是要做字符串替換的,所以名字以數(shù)字開頭是沒問題的。makefile本身也不是用于數(shù)值處理的,以字符串為主。
使用一個(gè)宏的時(shí)候需要使用$符號(hào),所以如果字符串中要使用shell變量,需要用$$。
從中可以看到,我們前面學(xué)到的函數(shù),其實(shí)本質(zhì)上也就是宏,只不過是帶有了參數(shù)的宏。
宏的賦值
= 和 :=
宏可以用其他宏的值來定義自己。最強(qiáng)大的是用=運(yùn)算符。makefile是定義式的語言,不是按順序一條一條執(zhí)行的,所以,可以使用在這一行還沒有定義的宏來為當(dāng)前宏賦值,反正就是個(gè)宏展開么。
但是,這樣如果造成的循環(huán)引用,就會(huì)引發(fā)意想不到的錯(cuò)誤。在這種情況下,使用:=運(yùn)算符,它是只允許引用在它之前定義的變量的。
?=
如果不知道定義沒定義,可以使用?=來賦值,如果未定義,則定義。如果已經(jīng)定義了,就什么也不做。
+=
如果之前未定義,則相當(dāng)于=。如果之前有:=定義,則將新值按:=的規(guī)則添加到原值后面。
對(duì)宏進(jìn)行比較
有4個(gè)關(guān)鍵字用于對(duì)宏的比較:
- ifeq:判斷相等
- ifneq:判斷不相等
- ifdef:判斷非空,相當(dāng)于ifneq(<參數(shù)>,)
- ifndef:如果為空,相當(dāng)于ifeq(<參數(shù)>,)
例:
.PHONY : all7
ifeq ($(DEX2OAT_HOST_INSTRUCTION_SET_FEATURES),)
DEX2OAT_HOST_INSTRUCTION_SET_FEATURES := default
endif
all7 :
@echo $(DEX2OAT_HOST_INSTRUCTION_SET_FEATURES)
輸出:
$ make all7
default
請(qǐng)注意,ifeq, else, endif這些語句之前不能有Tab。
上面的例子是從Android.oat.mk中改造的。其實(shí),跟下面的是等價(jià)的:
ifndef DEX2OAT_HOST_INSTRUCTION_SET_FEATURES
DEX2OAT_HOST_INSTRUCTION_SET_FEATURES := default
endif
定義多行的宏
可以通過使用define / endef來定義多行的大宏。
我們看一個(gè)例子:
# $(1): compiler - default, optimizing, jit or interpreter.
# $(2): wrapper.
# $(3): dex2oat suffix.
define create-core-oat-host-rule-combination
$(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3))
$(call create-core-oat-host-rules,$(1),pic,,$(2),$(3))
ifneq ($(HOST_PREFER_32_BIT),true)
$(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3))
$(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3))
endif
endef
這個(gè)宏其實(shí)是個(gè)函數(shù)了。
eval函數(shù)
eval函數(shù)用于在宏定義或者是分支、循環(huán)結(jié)構(gòu)中使用其他makefile語句時(shí)。
我們看一個(gè)在foreach中使用eval的例子:
$(foreach m,$(ALL_MODULES), \
$(eval r := $(ALL_MODULES.$(m).REQUIRED)) \
$(if $(r), \
$(eval r := $(call module-installed-files,$(r))) \
$(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
$(eval h_m := $(filter $(HOST_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
$(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \
$(eval h_r := $(filter $(HOST_OUT_ROOT)/%, $(r))) \
$(eval t_m := $(filter-out $(t_r), $(t_m))) \
$(eval h_m := $(filter-out $(h_r), $(h_m))) \
$(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \
$(if $(h_m), $(eval $(call add-required-deps, $(h_m),$(h_r)))) \
) \
)
vpath宏
vpath宏用于指定makefile搜索源文件的目錄,這個(gè)倒是有點(diǎn)變量的意思了
引用其他makefile
makefile基本上就是字符串替換,我們當(dāng)然也可以通過include命令將一系列的其他makefile引用進(jìn)當(dāng)前的makefile.
例:
include art/build/Android.common_build.mk
自動(dòng)宏
自動(dòng)宏就是由make工具已經(jīng)定義好的變量。
$@ 目標(biāo)文件集
$@是指整個(gè)目標(biāo)的文件集
例:
$(built_odex) : $(dir $(LOCAL_BUILT_MODULE))% : $(built_dex)
$(hide) mkdir -p $(dir $@) && rm -f $@
$(add-dex-to-package)
$(hide) mv $@ $@.input
$(call dexpreopt-one-file,$@.input,$@)
$(hide) rm $@.input
endif
$<
依賴的第一個(gè)文件名,可以和$@一起共用。
例:
$(built_odex) : $(my_prebuilt_src_file)
$(call dexpreopt-one-file,$<,$@)
調(diào)試與錯(cuò)誤處理
輸出消息
makefile的命令,默認(rèn)都會(huì)輸出出來??梢杂聾來隱藏顯示。
通過echo命令,可以輸出消息。一般都是用@echo,免得看見兩次。
幾條命令一起執(zhí)行
如果這條命令用到上條命令的結(jié)果,需要在上條命令后面加分號(hào)
錯(cuò)誤處理
如果makefile遇到命令出錯(cuò),就會(huì)中止當(dāng)前的依賴執(zhí)行??梢酝ㄟ^在命令之前加上“-”來忽略錯(cuò)誤。
我們也可以將忽略錯(cuò)誤的目標(biāo)以.IGNORE來聲明。
如:
.IGNORE : all7