函數功能:函數「eval」是一個比較特殊的函數。使用它能夠在Makefile中構造一個可變的規則結構關係(依賴關係鏈),其中可使用其它變量和函數。函數「eval」對它的參數進行展開,展開的結果做爲Makefile的一部分,make能夠對展開內容進行語法解析。展開的結果能夠包含一個新變量、目標、隱含規則或者是明確規則等。也就是說此函數的功能主要是:根據其參數的關係、結構,對它們進行替換展開。html
返回值:函數「eval」的返回值時空,也能夠說沒有返回值。
函數說明:「eval」函數執行時會對它的參數進行兩次展開。第一次展開過程發是由函數自己完成的,第二次是函數展開後的結果被做爲Makefile內容時由make解析時展開的。明確這一過程對於使用「eval」函數很是重要。理解了函數「eval」二次展開的過程後。實際使用時,若是在函數的展開結果中存在引用(格式爲:$(x)),那麼在函數的參數中應該使用「$$」來代替「$」。由於這一點,因此一般它的參數中會使用函數「value」來取一個變量的文本值。程序員
其實 eval 在函數式語言裏面很常見。LISP 系語言的解釋器,最終執行的是一個 apply - eval 遞歸(有人也喜歡叫 apply - eval 循環,可是其實是遞歸求值)。因此 eval 就是求值的意思。實際上,不僅是 LISP,能夠說任意解釋器,最終都是 apply - eval 遞歸。bash 裏面也有 eval. 只不過,在 LISP 裏面,這種 apply - eval 經過其(語法...)形式,更加顯式地表達出來了,因此 LISP 裏面的 apply - eval 也就更著名。
遞歸是容易讓人暈菜的玩意,真正搞懂遞歸的程序員,其實並非想像的那麼多。好比圖靈機,圖靈機能夠接受的語言被稱爲遞歸可枚舉集。具體的數學上的定義就不扯淡了。其實這句話的意思,想一想 C 程序的執行流程就知道了,一個 main 函數,裏面調幾個函數,這幾個函數執行完了,程序就執行完了。那這個幾個函數實際上就能夠看着爲一個可枚舉的集合的成員。而每個函數,均可以寫成遞歸的形式,緣由很簡單,由於任意判斷和循環均可以寫成遞歸的形式。
扯遠了,說回來。關於 Makefile 裏面的 eval, 以及我本帖所舉的那個 Makefile 手冊裏面的長篇大論,說什麼二次求值什麼的,其實並無真正說清楚。我想了一個極其簡單的例子來講清楚 Makefile 裏面的 eval.
Makefile:bash
1 ############################################### 2 pointer := pointed_value 3 4 define foo 5 var := 123 6 arg := $1 7 $$($1) := ooooo 8 endef 9 10 $(info $(call foo,pointer)) 11 #$(eval $(call foo,pointer)) 12 13 target: 14 @echo ----------------------------- 15 @echo var: $(var), arg: $(arg) 16 @echo pointer: $(pointer), pointed_value: $(pointed_value) 17 @echo done. 18 @echo ----------------------------- 19 20 ###############################################
注意上面的例子,$(eval $(call foo, pointer)) 那行被註釋了。先執行這個註釋了那行的 Makefile,結果以下:app
注意,
var := 123
arg := pointer
$(pointer) := ooooo
這幾行就是 $(call foo,pointer) 的結果(或者說,調用 foo 這個 "函數"(由於 Makefile 中正式的名字叫作宏包) 的返回值)。同時注意到, var, arg, pointed_value 都是空值,由於我實際上只是經過 $(info ) 函數將替換了參數後的 foo 函數體,或者說 $(call foo, pointer) 的返回值打印到標準輸出而已($1 就是 pointer, 調用函數,就直接替換下參數而已),因此,這幾行代碼並無真正執行。
注意了,這個 $(call foo,pointer) 就是 Makefile 對 foo 函數的第一次求值。上面看到了,實際上求值出來的結果仍是 Makefile 代碼。
那麼問題就來了。既然求值出來的結果仍是 Makefile 代碼,那這段代碼又要怎麼運行呢?答案就是再包一個 eval, 因此 eval 就是第二次求值了。
所以,若是將 $(eval $(all foo,pointer)) 那行註釋取消掉的話,運行結果以下:函數
OK. 注意,var, arg, pointed_value 都被賦值了,這個賦值操做就是第一次求值出來的代碼運行的結果。
那麼,爲何在寫 foo 這個宏包的時候,要寫成$$($1) := ooooo呢?spa
由於 Makefile 裏面 $ 是元字符(meta-chara...),也就是它是有特殊意義的。那在 Makefile 裏面表示"字符" $ 就得用 $$. 看第一次求值的結果就知道了,不用多說。
.net
參考: http://bbs.chinaunix.net/thread-2321462-1-1.htmlunix