makefile規則

Invoker發表於2019-05-11

makefile中的規則

概述

規則的一般形式:

target: <normal-prerequisites> | <order-only-prerequisites> <; commands>
    <commands>

makefile的構成主體是各種規則。無論是什麼形式的規則,都會描述一個三元組:目標、必要條件、命令。一個規則可以同時描述一個或者多個甚至無數個工作目標(其實是無數個規則構成的模式),多目標的規則可以視為多個規則的疊加描述,都可拆分成單一工作目標的規則。工作目標是某個檔名,或者某個“動作”,但不能為空。必要條件也可以是具體的檔案或者“動作”,可以為空。命令是發生更新時交給子shell執行的命令,可以多條,每條交給一個shell,如果要把所有的命令都給一個shell執行,要把它們整成一行或用連行符號

目標和必要條件中的變數會立即擴充套件,而命令不會擴充套件。(make會在所有的變數和命令都被載入到資料庫之後,在將要執行命令之前擴充套件命令中的變數。)如果在定義了.SECONDEXPANSION這個目標,之後的規則中的必要條件都會進行兩次擴充套件。第二次擴充套件發生在make讀入所有的變數和規則之後,在執行規則之前。

當make讀入makefile後,需要根據必要條件的型別和時間戳與目標的時間戳,決定是否採取更新動作(把命令交給子shell執行)。
必要條件分為兩種:
普通必要條件。更新目標之前先更新普通必要條件。只要該必要條件的時間戳大於或等於目標的時間戳(尚不存在的檔案時間戳可以認為是負無窮或者某個古老的時間點),就會導致更新動作;
“order-only“必要條件,(只執行一次的必要條件,|之後的必要條件)。Make要更新這個必要條件時會檢查是否存在該檔案,如果存在,就不會繼續更新這個必要條件(不管他的時間戳)也不會因此更新目標;如果不存在,則檢查在此次執行make的過程中是否執行過以該必要條件為目標的規則,如果沒有則執行一次,否則結束對此必要條件的更新。

下面是經常用到的例子,跟新目標檔案之間要求目標資料夾已存在,但總不能每條跟新目標檔案的規則裡都執行mkdir -p,這樣雖然也能幹活,但看著難受。更好的辦法是讓目標檔案order-only依賴與資料夾,然後針對資料夾的更新動作是mkdir -p,這樣mkdir就只執行一次了。

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)

$(OBJS): | $(OBJDIR)
$(OBJDIR):
    mkdir $(OBJDIR)

具體規則,指定具體的工作目標,如果必要條件中有時間戳靠後的檔案,則執行命令。這是最常見的規則。模式規則中,目標使用的是萬用字元而不是具體的檔名,這讓make對與模式相符的工作目標應用該規則。隱含規則可以是模式規則,也可以是內建於make的字尾規則。

具體規則

具體規則以特定的檔案作為工作目標和必要條件。每個規則可以由多個工作目標。意味著每個工作目標都依賴後面的必要條件。
規則不必一次性定義完全。每當make看到一個工作目標,就會將該工作目標和必要條件加入依存圖。如果工作目標已經存在,任何額外的必要條件會被新增到依存圖中。如果一個工作目標多次制定了命令(不是一次命令由多條命令組成),則取最後一次命令作為更新動作,忽略之前的命令,make會給出警告。

假想工作目標:工作目標可以不是真實的檔案,表示一個動作,成為假想工作目標。它總是未更新的,很“舊”,所以每次該規則的命令都會被執行。為了防止該動作可能與某個檔案重名,可用.PHONY:宣告。

模式規則

背景:

  • 在包含很多檔案的大型程式中,手動指定每條規則將會不切實際。而且,這些命令很可能是重複的程式碼,如果修改某處可能引起多處修改,產生難以維護的問題。
  • 許多程式在讀取輸入檔案以及產生輸出檔案時都會依照慣例。

模式

模式規則中的百分號字元%大體上等效於unix中的星號*,它可以代表任意多個字元。百分比字元可以放在模式的任何地方,但只能出現一次。當make搜尋要使用的模式規則時,它首先會查詢符合的模式規則工作目標,將百分號代表的部分替換到必要條件中來檢查模式規則的必要條件。

例如:

%: %.mod
    $(COMPILE.mod) -o $@ -e $@ $^

%: %.cpp
    $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@

%: %.sh
    cat $< >$@
    chmod a+x $@

靜態模式規則

靜態模式規則與一般模式規則的唯一差別是指定了工作目標的匹配集合。

$(OBJECTS): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

字尾規則

字尾規則是舊時沒有模式規則時的替代品,已棄用。

隱含規則

隱含規則是內建於make的模式規則。要檢視make內建了那些規則,可以使用-p, --print-data-base命令顯示。此命令還可以看到內建的變數定義。

當make檢查一個工作目標時,如果找不到它的具體規則,就會使用隱含規則。
使用不帶命令的模式規則可以遮蔽相應的隱含規則。

例如:

hello:
hello: hello.c

都會自動執行相應的編譯過程。

%: %.o

可以遮蔽從.o檔案連結可執行程式的規則。

雜項

規則中可以使用shell的模式匹配,而其他地方要使用wildcard函式。

規則中的必要條件不必一次描述完全,可以分為多次,但是對於同一規則的命令,只取最後一次描述中的命令。比如:

hello : hello.c
    echo hello
    
hello : include.h
    gcc -o hello hello.c

以hello為目標的規則中有兩個必要條件hello.cinclude.h,此規則的命令只有gcc -o hello hello.c