foreach循環函數
foreach是Makefile中用來做循環的函數,它把可以重複利用一段腳本,但是每次又有不同的條件。它類似於Unix標準Shell(/bin/sh)中的for語句,或是C-Shell(/bin/csh)中的foreach語句。它的語法是:
$(foreach var, list, text)
前兩個參數var和list,參數中的單詞逐一取出放到參數所指定的變量中,然後再執行所包含的表達式。每一次會返回一個字符串,循環過程中,的所返回的每個字符串會以空格分隔,最後當整個循環結束時,所返回的每個字符串所組成的整個字符串(以空格分隔)將會是foreach函數的返回值。
所以,最好是一個變量名,可以是一個表達式,而中一般會使用這個參數來依次枚舉中的單詞。
names := a b c d
files := $(foreach n,$(names),$(n).o)
上面的例子中,$(name)中的單詞會被挨個取出,並存到變量“n”中,“$(n).o”每次根據“$(n)”計算出一個值,這些值以空格分隔,最後作爲foreach函數的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
注意,foreach中的參數是一個臨時的局部變量,foreach函數執行完後,參數的變量將不在作用,其作用域只在foreach函數當中。
if 函數
if函數很像GNU的make所支持的條件語句——ifeq(參見前面所述的章節),if函數的語法是:
$(if ,)
或是
$(if ,,)
可見,if函數可以包含“else”部分,或是不含。即if函數的參數可以是兩個,也可以是三個。參數是if的表達式,如果其返回的爲非空字符串,那麼這個表達式就相當於返回真,於是,會被計算,否則會被計算。
而if函數的返回值是,如果爲真(非空字符串),那個會是整個函數的返回值,如果爲假(空字符串),那麼會是整個函數的返回值,此時如果沒有被定義,那麼,整個函數返回空字串。
所以,和只會有一個被計算。
補充一個ifeq的用法:
Makefile中的ifeq定義前面不能被tab製表符修飾,只能被空格。這與Makefile函數正好是相反的。
當出現語法錯誤時,務必檢查是否在ifeq前面有tab製表符
/bin/sh: -c: line 0: syntax error near unexpected token `nightly,nightly'
/bin/sh: -c: line 0: `ifeq (nightly,nightly)'
ifeq ($(1), arg1)
# text do something
endif
call函數
call函數是唯一一個可以用來創建新的參數化的函數。你可以寫一個非常複雜的表達式,這個表達式中,你可以定義許多參數,然後你可以用call函數來向這個表達式傳遞參數。其語法是:
$(call ,,,...)
當make執行這個函數時,參數中的變量,如$(1),$(2),$(3)等,會被參數,,依次取代。而的返回值就是call函數的返回值。例如:
reverse = $(1) $(2)
foo = $(call reverse, a, b)
那麼,foo的值就是“a b”。當然,參數的次序是可以自定義的,不一定是順序的,如:
reverse = $(2) $(1)
foo = $(call reverse,a,b)
此時的foo的值就是“b a”。
shell函數
shell函數也不像其它的函數。顧名思義,它的參數應該就是操作系統Shell的命令。它和反引號“`”是相同的功能。這就是說,shell函數把執行操作系統命令後的輸出作爲函數返回。於是,我們可以用操作系統命令以及字符串處理命令awk,sed等等命令來生成一個變量,如:
contents := $(shell cat foo)
files := $(shell echo *.c)
注意,這個函數會新生成一個Shell程序來執行命令,所以你要注意其運行性能,如果你的Makefile中有一些比較複雜的規則,並大量使用了這個函數,那麼對於你的系統性能是有害的。特別是Makefile的隱晦的規則可能會讓你的shell函數執行的次數比你想像的多得多。
value函數
value函數提供了一種使用變量非展開值的方式,但是它也並不能對於已經在定義中展開的結果生效。比如,你使用$()定義了一個變量,那麼就可以用value來使用變量的非展開值。value的語法是:
$(value var)
注意,這裏的var參數是變量名,而不是對變量的引用$(var)
例子:
FO=$(PATH)
all:
echo 1$(FO)
echo 2 $(value FO)
輸出是1 /bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin: (系統PATH的展開)
2 $(PATH),FOO的定義。
Value函數經常與eval函數一起結合使用。
eval函數
eval函數可以通過其他變量或函數的結果來定義新的makefile結構。Eval的參數被按照makefile的語法格式展開。展開的結果可以用來構造新的變量、目標、隱式或者顯式規則。
eval的參數被展開兩次,第一次被eval本身,第二次是第一次展開的結果被解析爲makefile語法時。因此在使用eval時需要提供額外的$引用字符。這時候也可以使用value來避免不希望的展開。
下面是一個用eval來創建目標的例子,儘管看起來有些複雜,但是在寫好它之後,就可以把generic的部分提供到一個公共文件中,再在你的個人makefile中include它,這樣子你的個人makefile就會特別簡練了。
- PROGRAMS = server client
- server_OBJS = server.o server_priv.o server_access.o
- server_LIBS = priv protocol
- client_OBJS = client.o client_api.o client_mem.o
- client_LIBS = protocol
-
- # Everything after this is generic,可以拿到其他文件用來include
- .PHONY: all
- all: $(PROGRAMS)
-
- define PROGRAM_template =
- $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
- ALL_OBJS += $$($(1)_OBJS)
- endef
-
- $(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
- $(PROGRAMS):
- $(LINK.o) $^ $(LDLIBS) -o $@
- clean:
- rm -f $(ALL_OBJS) $(PROGRAMS)
更多的eval函數實用例子可見 OpenWRT的package編譯文件,裏面大量的使用了eval模板來構建target