到目前為止, 我們所看到的大部分變數都會明確的定義在 makefile 檔中, 其實變數的使用可以更複雜. 舉例來說, 我們曾看到過變數被定義在 make 命令列之上, 事實上 make 的變數可以有以下底下幾種來源 :
- 檔案 :
- 命令列 :
每個命令引數中所包含的等號 (=), 就是一個變數的賦值運算符. 命列上每個變數賦值運算符右邊必須是一個單獨的 shell 引數. 如果變數的值包含空格, 則必須為引數加上括號, 或是規避空格. 命令列上變數的賦值結果將會蓋掉環境變數以及 makefile 檔中的賦值結果, 你可以使用 := 或 = 賦值運算符將命令列引數設成簡單或是遞回變數. 此外如果使用 override 指令, 你還可以要求 make 採用 makefile 的賦值結果, 而不要採用命令列的賦值結果. 當然你只應該在非常緊迫情況下, 忽略使用者所要求賦值動作.
環境 :
當 make 啟動時, 所有來自環境的變數都會被自動定義成 make 的變數. 這些變數具有非常低的優先權, 所以來自 makefile 檔或是命令列引數的賦值動作將會覆蓋環境變數的值. 不過你可以使用 --environment-overrides (或是 -e) 命令列選項, 讓環境變數覆蓋定相對應的 makefile 變數.
當 makefile 被遞回調用時, 將有若干來自上層 make 變數會透過環境變數傳遞給下層的 make. 預設上只有原先就來自環境變數會被匯到下層環境中. 不過你只要使用 export 指令就可以讓任何變數被匯出到環境之中. 條件賦值運算符與環境變數互動良好. 假如你已經在 makefile 檔中設定了一個預設的輸出目錄, 但是你希望使用者輕易能改寫此預設值, 此時條件賦值將會是最佳解決方案 :
* Makefile 內容 :
- # Presume Output path to be $(PROJECT_DIR)/out
- PROJECT_DIR=.
- OUTPUT_DIR?= $(PROJECT_DIR)/out
- hello: hello.c
- gcc hello.c -o $(OUTPUT_DIR)/hello
這樣 make 只會在變數 OUTPUT_DIR 尚未定義的狀況下進行賦值動作. 此外, 使用如下較冗長方式也可以得到幾乎一樣的結果 :
* Makefile2 內容:
- # Presume Output path to be $(PROJECT_DIR)/out
- PROJECT_DIR=.
- ifndef OUTPUT_DIR
- OUTPUT_DIR?= $(PROJECT_DIR)/out
- endif
- hello: hello.c
- gcc hello.c -o $(OUTPUT_DIR)/hello
觀察上面執行結果可知, 如果變數的值已經設定, 即使是空值, 條件賦值符便會跳過賦值動作, 而 ifdef/ifndef 只會測試 "非空值". 因此我們會使用條件賦值符來取代 ifdef/ifndef 來對 OUTPUT_DIR= 賦值. 最後切記過度使用環境變數將會大大降低你的 makefile 的移植性, 因為其他使用者不太可能設定跟你完全一樣的環境變數.
自動建立 :
最後 make 會在執行第一個規則的命令稿之前就會建立自動變數. 傳統上環境變數可協助開發者管理機器之間的差異. 常見的作法就是根據 makefile 檔中所參照的環境變數來建立開發環境 (原始檔樹, 二元輸出樹, 以及程式庫). makefile 將會以環境變數指向每個目錄樹的根目錄. 如果能夠從 PROJECT_SRC 變數參照原始檔樹, 從 PROJECT_BIN 參照二元輸出樹以及從PROJECT_LIB 參照程式庫, 那麼開發者就可以依照需要將這些目錄擺到適當的地方.
這麼做有一個潛在的問題, 如果這些指向根目錄的變數, 並未進行設定, 將會導致錯誤! 一個解決的辦法就是在 makefile 檔中以條件賦值運算 ?= 提供預設值:
- PROJECT_SRC ?= /dev/$(USER)/src
- PROJECT_BIN ?= $(patsubst %/src, %/bin, $(PROJECT_SRC))
- PROJECT_LIB ?= /net/server/project/lib
沒有留言:
張貼留言