到目前為止, 我們看過了 makefile 的變數以及將它們應用在內建與自訂規則中. 不過這都是基礎, 變數與巨集越複雜, GNU make 的功能就越強大. 在我們繼續任何探討前, 最好先了解 make 所包含的兩種語言. 第一種語言用來描述工作目標與必要條件所組成的依序圖. 第二種語言是個巨集語言, 用來進行文字的替換. 如 make 允許你為較長的字符序列定義簡寫, 以及在你的程式中使用該簡寫. 巨集處理器將會認出你的簡寫以及作置換程展開後的形式. 雖然你可以把 makefile 的變數想成傳統語言的變數, 不過巨集變數和傳統變數之間還是有差別. 巨集變數會被 "就地" 展開, 其所產生的文字串還可以做一步的展開.
變數名稱是大小寫有別的, 所以 cc 和 CC 所指的是不同變數. 要取的某個變數的值, 可用 $() 括住該變數名稱或使用大括號來展開變數, 例如 ${CC}. 現代化的 makefile 多半會使用小括號, 接下來也會以小括號作為範例說明. 當變數用來表示使用者在命令列或環境中所訂製的常數時, 習慣上會以全部大寫來撰寫其名稱, 單字之間並以底線符號 (_) 隔開. 至於只在 makefile檔中出現的變數, 則會全部用小寫來撰寫其名稱, 單字之間並以底線符號隔開. 參考如下範例 :
- # 常數
- CC := gcc
- MKDIR := mkdir -p
- # 內部變數
- sources = *.c
- objects = $(subst .c,.o,$(sources))
- # 函式
- maybe-make-dir = $(if $(wildcard $1),,$(MKDIR) $1)
- assert-not-null = $(if $!,,$(error Illegal null value.))
- LIBRARY = libio.a #LIBRARY
- missing_file:
- touch $(LIBRARY)
- ls -l | grep '$(LIBRARY)'
因為 grep 搜尋字串包含了跟在後面的空格, 所以無法在 ls 的輸出中找到該檔案的名稱.
變數的用途 :
一般來說, 以變數來代表外部程式是個不錯的主意. 這讓 makefile 的使用者必較容易針對他們特有的環境來進行改寫 makefile . 舉例來說一個系統常會包含 awk 的各種版本 : awk, nawk, gawk. 這個時候就可以建立一個 AWK 變數來保存 awk 程式的名稱, 讓 makefile 的使用較為容易. 變數可用來保存簡單的常數, 也可以用來存放使用者自訂的命令序列, 例如下列的設定可以用來回報尚未使用的磁碟空間 :
- DF = df
- AWK = awk
- free-space := $(DF) . | $(AWK) 'NR == 2 {print $$4}'
變數的類型 :
make 變數有兩種類型 : 經簡單展開的變數以及經遞回展開的變數. 你可以用 := 賦值運算符來定義一個簡單展開的變數 (或稱簡單變數) :
- MAKE_DEPEND := $(CC) -M
然而如果上面的變數 $(CC) 尚未定義, 則此變數展開後一般會變成這個樣子 :
$(CC) 被展開成它的值 (並未包含任何字符) , 也就是空無一物 (即空值) , 變數沒有定義並不算錯誤. 事實上此特性相當有用. 大多數的內定命令都會包含未定義的變數, 以作為使用者訂製變數的佔位符. 如果使用者並未訂製該變數, 它就會變成空無一物. 現在注意前導的空格. make 首先會剖析賦值運算符右邊的部分, 也就是 $(CC) -M 這個字串. 當變數參照被展開成空無一物, make 不會重新掃描該值以及刪除前導的空格, 於是前導的空格就留下來的.
第二種變數類型稱為經遞迴展開的變數. 你可以用 '=' 賦值運算符來定義一個經遞迴展開的變數 (會稱遞迴變數) :
- MAKE_DEPEND = $(CC) -M
- MAKE_DEPEND = $(CC) -M
- ...
- # 稍後
- CC = gcc
事實上, 遞迴變數所進行並非真的是延後賦值的動作. 每當遞迴變數被使用時, make 就會對它的右邊部分進行重新求值的動作. 如果變數被定義成簡單的變數, 比如前面的MAKE_DEPEND, 作此區別是毫無意義的, 因為右邊部分的變數也都會是簡單的變數. 但試想如果右邊部分的某個變數被用來代表一個所要執行的程式, 例如 date. 每當遞迴變數被展開, date 程式就會被執行, 而且每次展開後的值都不一樣 (假定date 的執行前後都間隔一秒) . 有的時候這個特性可能非常有用, 但也可能非常煩人.
其他的賦值類型 :
我們在前面的範例看到兩種賦值類型, 其中 '=' 用來建立遞迴變數, 而 ':=' 用來建立簡單變數. 此外 make 還提供了另外兩種賦值運算符 : '?=' 和 '+='.
'?=' 運算符稱為 附帶條件的變數賦值運算符 . 我們會把他簡稱為條件賦值. 此運算符只會在變數的值尚不存在的狀況下進行被要求的變數賦值動作. 考慮下面範例 :
- # 將所要產生的每個檔案擺到 $(PROJECT_DIR)/out 目錄
- OUTPUT_DIR ?= $(PROJECT_DIR)/out
'+=' 運算符通常稱為附加運算符. 正如其名此運算符會將文字附加到變數裡. 這似乎是沒什麼特別的, 但是當遞迴變數使用時, 它卻是一個重要特性. 尤其是賦值運算符右邊部分的值會在 "不影響變數中原有的值的狀況下" 被附加到變數理, 你可能會說 "這有什麼大不了, 附加的功能不就是這樣?". 沒錯但請繼續往下面看. 對簡單變數進行附加動作, 事情就會變得更加明顯. 考慮下面範例 :
因為簡單變數中的值會被立即展開, 所以 make 會展開 $(simple) , 附加因而產生的文字, 最後進行賦值的動作. 但是遞迴變數會導致一個問題. 如果將 '+=' 運算符實作成下面這個樣子, 是不被允許的 :
* makefile_3-1-3 內容 :
- recursive = $(recursive) new_stuff
- test:
- echo $(recursive)
所以使用 '+=' 被特別實作成可將文字附加到遞迴變數, 以及做正確的事. 此運算符對於想收集到值遞增到變數的人特別有用.
沒有留言:
張貼留言