1. 程式人生 > >Makefile中的eval()函式用法

Makefile中的eval()函式用法

在一個論壇上找到了一個簡單易懂的解釋方法,我就不自己解釋了。
參考連結為: 點我

其實他的作用有點相當於c++中的模板,就是生成一個段makefile語句,然後還要被執行。

在參考連結中的示例程式,一開始我覺得這樣寫能執行嗎?直到我真正在自己的電腦上編輯完之後,執行make發現還是能執行的。

下面就直接無恥地制過來吧。
其實 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:

###############################################
pointer := pointed_value

define foo
var := 123
arg := $1
$$($1) := ooooo
endef

$(info $(call foo,pointer))
#$(eval $(call foo,pointer))

target:
        @echo -----------------------------
        @echo var: $(var), arg: $(arg)
        @echo pointer:
$(pointer), pointed_value: $(pointed_value) @echo done. @echo ----------------------------- ###############################################

注意上面的例子,(eval(call foo, pointer)) 那行被註釋了。先執行這個註釋了那行的 Makefile,結果如下:

var := 123
arg := pointer
$(pointer) := ooooo
-----------------------------
var: , arg:
pointer: pointed_value, pointed_value:
done.
-----------------------------

複製程式碼

注意,

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 $(call foo,pointer)) 那行註釋取消掉的話,執行結果如下:

    var := 123
    arg := pointer
    $(pointer) := ooooo
    -----------------------------
    var: 123, arg: pointer
    pointer: pointed_value, pointed_value: ooooo
    done.
    -----------------------------

複製程式碼

OK. 注意,var, arg, pointed_value 都被賦值了,這個賦值操作就是第一次求值出來的程式碼執行的結果。

所以,為什麼在寫 foo 這個巨集包的時候,要寫成

$$($1) := ooooo

呢?因為 Makefile 裡面 $ 是元字元(meta-chara…),也就是它是有特殊意義的。那在 Makefile 裡面表示”字元” $ 就得用 $$. 看第一次求值的結果就知道了,不用多說。

===================
好了,無恥完畢。