Makefile中的常用函數 - foreach、if、call、shell、value、eval

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函數很像GNUmake所支持的條件語句——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函數把執行操作系統命令後的輸出作爲函數返回。於是,我們可以用操作系統命令以及字符串處理命令awksed等等命令來生成一個變量,如:

      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就會特別簡練了。

  1. PROGRAMS = server client
  2. server_OBJS = server.o server_priv.o server_access.o
  3. server_LIBS = priv protocol
  4. client_OBJS = client.o client_api.o client_mem.o
  5. client_LIBS = protocol

  6. # Everything after this is generic,可以拿到其他文件用來include
  7. .PHONY: all
  8. all: $(PROGRAMS)

  9. define PROGRAM_template =
  10. $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
  11. ALL_OBJS += $$($(1)_OBJS)
  12. endef

  13. $(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
  14. $(PROGRAMS):
  15. $(LINK.o) $^ $(LDLIBS) -o $@
  16. clean:
  17. rm -f $(ALL_OBJS) $(PROGRAMS)

     更多的eval函數實用例子可見 OpenWRT的package編譯文件,裏面大量的使用了eval模板來構建target

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