程式扎記: [GNU Make] 規則 : 內定規則

標籤

2010年9月20日 星期一

[GNU Make] 規則 : 內定規則

前言 : 
GNU 3.80 版具有 90個內定規則. 內定規則不是樣式規則的形式就是後綴規則形式. 這些內建規則可以應用到 C, C++, Pascal 等語言, 有些規則則是應用在這些語言的支援程式上, 比如 cpp, as, yacc 等及 dvi 工具. 
如果你有用到這些工具, 你可能會發現, 內建規則中也許已經有你所需要的東西. 欲檢視 make 內建了哪些規則, 可以使用 --print-data-base (或 -p) 命令選項. 這將會產生1千列左右的輸出. 在輸出中 make 會印出變數的定義並且會為每個變數前置一列註解, 以說明其用途. 舉例來說, 一個變數可以是環境變數, 預設值, 自動變數等等. 變數之後, make 會印出規則. 若這個規則是 GNU make 的內建, 會使用如下的格式 : 

  1. %: %.C  
  2. #  commands to execute (built-in):  
  3.         $(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@  

這個規則若是在 makefile 中定義, 則會使用如下的格式 : (註解中會包含檔名與列號) 
  1. %.html: %.xml  
  2. # commands to execute (from 'Makefile' , line 168)  
  3.         $(XMLTO) $(XMLTO_FLAGS) html-nochunks $<  


內建規則的使用 : 
當 make 檢查一個工作目標時, 如果找不到可以更新它的自訂規則, 就會使用內定規則. 內定規則的使用很容易 : 當你將工作目標加入 makefile 時, 只要不指定命令稿就行了. 這會使 make 搜尋內定規則以滿足工作目標的需求. 通常就是你所要的結果. 但某些罕見的情況可能內建規則會誤會你的要求, 但這裡不進行討論. 我們已經看過 make 在嘗試更新工作目標時將規則鍊結在一起的例子 ([GNU Make] 如何撰寫一個簡單的 makefile), 我們會在此再次進行說明. 當 make 試著更新一個工作目標, 它會搜尋內定規則, 試圖找到與工作目標相符的工作目標樣式. 對每個與工作目標相符的工作目標樣式來說, make 將會尋找相符的必要條件. 也就是說比對工作目標樣式後, make 會立即尋找必要條件 (多半是原始檔). 如果找到相符的必要條件, 就會應用對應的規則. 有些工作目標樣式可能具有多個可能的原始檔. 舉例來說, 一個 .o 檔可能來自 .c, .cc, .cpp 等檔案. 但如果搜尋過可能規則後還是找不到原始檔案, 結果會怎樣? 此時 make 將會在搜尋規則一次, 不過這一次會把原始檔的比對當成是在更新一個新的工作目標. 透過遞回地進行這樣的搜尋動作, make 可以找到一串用來更新工作目標的規則鍊. 
我們可以在 "如何建立一個簡單的 Makefile" 中看到這個現象, 在 lexer.o 範例中, 即使 .c 這個中間檔不存在, 透過調用 .l 到 .c 的規則以及 .c 到 .o 的規則, make 仍舊能夠更新 lexer.o 這個工作目標. 鏈結規則的過程中所產生的檔案稱為中間檔, make 會對它們進行特別的處理. 首先因為中間檔不會在工作目標出現, 所以 make 不會更新中間檔. 其次 make 建造中間檔本身是更新工作目標的副作用, 所以 make 在結束執行之前會刪除這些中間檔. 

規則的結構 : 
內建規則具有標準的結構, 好讓它們容易定製. 現在我們將來討論此主題, 以及探討"定置" 這方面的議題. 下面是從 C 原始檔來更新目的檔的規則 : 
  1. %.o: %.c  
  2.         $(COMPILE.c) $(OUTPUT_OPTION) $<  

這個規則的 "定製" 完全取決於其所用到的變數. 我們在此處看到了兩個變數, 其中尤以 COMPILE.c 是由多個其他變數所定義而成 : 
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
CC = gcc
OUTPUT_OPTION = -o $@
 

你只要變更 CC 變數設定就可以更換 C 編譯器. 此外還包括用來設定編譯選項的變數 (CFLAGS), 用來設定預處理器選項 (CPPFLAGS) 以及用來設定架構選項的變數 (TARGET_ARCH). 
在內建規則中使用變數的目的, 就是讓規則的定製盡可能的簡單化. 因此當你在 makefile 檔中設定這些變數時, 務必謹慎以對. 如果設定這些變數時隨意為之, 將會破壞終端使用者定製它們的能力. 例如你在 makefile 檔中作了如下的賦值動作 : 
CPPFLAGS = -I project/include 

如果使用者想在命令列上加入 CPP 的定義, 他們一般會像這樣來調用 make : 
$ make CPPFLAGS=-DDEBUG 

如果真的這麼做了, 將會意外刪除 (想必是) 編譯時所需要的 -I 選項. 命令列上所設定的變數將會蓋掉變數原有的值. 所以不當地在 makefile 中設定 CPPFLAGS 將會破壞大多數使用者預期的定製結果. 要避免此問題, 你可以使用如下方式重新定義編譯變數 : 
COMPILE.c = $(CC) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) $(TARGET_ARCH) -c
INCLUDES = -I project/include
 

Ps. 你也可以使用附加形式的賦值動作. 

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!