1. 程式人生 > >C語言的本質(37)——makefile之隱含規則和模式規則

C語言的本質(37)——makefile之隱含規則和模式規則

Makefile有很多靈活的寫法,可以寫得更簡潔,同時減少出錯的可能。本節我們來看看這樣一個例子還有哪些改進的餘地。

 一個目標依賴的所有條件不一定非得寫在一條規則中,也可以拆開寫,例如:

main.o: main.h stack.h maze.h
 
main.o: main.c
         gcc-c main.c

就相當於:
main.o: main.c main.h stack.h maze.h
         gcc-c main.c

如果一個目標拆開寫多條規則,其中只有一條規則允許有命令列表,其它規則應該沒有命令列表,否則make會報警告並且採用最後一條規則的命令列表。

這樣我們的例子可以改寫成:

main: main.o stack.o maze.o
         gccmain.o stack.o maze.o -o main
 
main.o: main.h stack.h maze.h
stack.o: stack.h main.h
maze.o: maze.h main.h
 
main.o: main.c
         gcc-c main.c
 
stack.o: stack.c
         gcc-c stack.c
 
maze.o: maze.c
         gcc-c maze.c
 
clean:
         -rmmain *.o
 
.PHONY: clean

這不是比原來更繁瑣了嗎?現在可以把提出來的三條規則刪去,寫成:

main: main.o stack.o maze.o
         gccmain.o stack.o maze.o -o main
 
main.o: main.h stack.h maze.h
stack.o: stack.h main.h
maze.o: maze.h main.h
 
clean:
         -rmmain *.o
 
.PHONY: clean

這就比原來簡單多了。可是現在main.o、stack.o和maze.o這三個目標連編譯命令都沒有了,怎麼編譯的呢?試試看:

$ make
cc   -c -o main.o main.c
cc   -c -o stack.o stack.c
cc   -c -o maze.o maze.c
gcc main.o stack.o maze.o -o main

現在解釋一下前三條編譯命令是怎麼來。如果一個目標在Makefile中的所有規則都沒有命令列表,make會嘗試在內建的隱含規則(Implicit Rule)資料庫中查詢適用的規則。make的隱含規則資料庫可以用make -p命令列印,打印出來的格式也是Makefile的格式,包括很多變數和規則,其中和我們這個例子有關的隱含規則有:

# default
OUTPUT_OPTION = -o [email protected]
 
# default
CC = cc
 
# default
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS)$(TARGET_ARCH) -c
 
%.o: %.c
# commands to execute (built-in):
       $(COMPILE.c) $(OUTPUT_OPTION) $<

#號在Makefile中表示單行註釋,就像C語言的//註釋一樣。CC是一個Makefile變數,用CC = cc定義和賦值,用$(CC)取它的值,其值應該是cc。Makefile變數像C的巨集定義一樣,代表一串字元,在取值的地方展開。cc是一個符號連結,通常指向gcc,在有些UNIX系統上可能指向另外一種C編譯器。

CFLAGS這個變數沒有定義,$(CFLAGS)展開是空,CPPFLAGS和TARGET_ARCH也是如此。這樣$(COMPILE.c)展開應該是cc 空 空 空 -c,去掉“空”得到cc -c,注意中間留下4個空格,所以%.o:%.c規則的命令$(COMPILE.c) $(OUTPUT_OPTION) $<展開之後是cc -c -o [email protected]$<,和上面的編譯命令已經很接近了。

[email protected]和$<是兩個特殊的變數,[email protected]的取值為規則中的目標,$<的取值為規則中的第一個條件。%.o: %.c是一種特殊的規則,稱為模式規則(Pattern Rule)。現在回顧一下整個過程,在我們的Makefile中以main.o為目標的規則都沒有命令列表,所以make會查詢隱含規則,發現隱含規則中有這樣一條模式規則適用,main.o符合%.o的模式,現在%就代表main(稱為main.o這個名字的Stem),再替換到%.c中就是main.c。所以這條模式規則相當於:

main.o: main.c
         cc    -c -o main.o main.c

隨後,在處理stack.o目標時又用到這條模式規則,這時又相當於:

stack.o: stack.c
         cc    -c -o stack.o stack.c

maze.o也同樣處理。這三條規則可以由make的隱含規則推匯出來,所以不必寫在Makefile中。

先前我們寫Makefile都是以目標為中心,一個目標依賴於若干條件,現在換個角度,以條件為中心,Makefile還可以這麼寫:

main: main.o stack.o maze.o
         gccmain.o stack.o maze.o -o main
 
main.o stack.o maze.o: main.h
main.o maze.o: maze.h
main.o stack.o: stack.h
 
clean:
         -rmmain *.o
 
.PHONY: clean

我們知道,寫規則的目的是讓make建立依賴關係圖,不管怎麼寫,只要把所有的依賴關係都描述清楚了就行。對於多目標的規則,make會拆成幾條單目標的規則來處理,例如

target1 target2: prerequisite1prerequisite2
         command$< -o [email protected]

這樣一條規則相當於:

target1: prerequisite1 prerequisite2
         commandprerequisite1 -o target1
 
target2: prerequisite1 prerequisite2
         commandprerequisite1 -o target2

注意兩條規則的命令列表是一樣的,但[email protected]的取值不同。

相關推薦

C語言本質37——makefile隱含規則模式規則

Makefile有很多靈活的寫法,可以寫得更簡潔,同時減少出錯的可能。本節我們來看看這樣一個例子還有哪些改進的餘地。 一個目標依賴的所有條件不一定非得寫在一條規則中,也可以拆開寫,例如:main.o: main.h stack.h maze.h main.o: main.

在STM32上實現NTFS4:GPT分區表的C語言實現1:主GPT表頭的實現

center mbr分區 sum 對齊 字節數 決定 容器 alt 水平 題外話:在荒廢了很久沒有更新之後……某日突然收到讀者的站內信!內容大體是詢問GPT分區表信息的讀取方式,筆者激動萬分之下,決定繼續解剖NTFS……其實GPT嚴格上不算是NTFS的內容, GPT和M

在STM32上實現NTFS5:GPT分區表的C語言實現2GPT實現以及統一方式讀取磁盤分區

tfs 下載 數據 特殊 dpt 屬性列表 handle 系統分區 成了   上一節實現了主GPT頭的信息提取,這一節繼續提取整個的GPT數據,並且將GPT分區表和MBR分區表兩種格式融合成一個模塊,使主調函數(也可以說是使用者)不需要關心磁盤的分區表類型:它太底層了,確實

嵌入式 C 語言編譯器

net DC 應用 不同 %s 翻譯 根據 oba 直接 我們在嵌入式的開發中經常會見到 GCC 和 gcc,那麽它們兩有何不同呢?GCC(GNU Compile Collection) 是指 GNU 編譯器集合,包含眾多語言的編譯器,如 C、C++、Jav

C語言教程5指標

指標:(C語言的靈魂) 記憶體的儲存是以一個位元組為一個編號,也就是8位合在一起給一個編號,不是0,1就給編號。 記憶體分為很多個單元,每個單元就會分配一個編號。 地址:記憶體單元的一個編號。而指標和地址一個概念的。也就是說指標就是地址。 普通變數:只能存放一個值

程式設計菜鳥到大佬路:C語言程式

第五天學習精要 關係運算符和邏輯表示式 關係運算符 六種關係運算符用於數值的比較:相等 ==、不等 !=、大於 >、小於 <、大於等於 >=、小於等於 <=。 比較的結果是bool型別,成立則為true,反之為false。

程式設計菜鳥到大佬路:C語言程式

第六天學習精要 if語句 條件分支結構之if 語句 有時,並非所有的程式語句都要被順序執行到,會希望滿足某種條件就執行這部分語句,滿足另一條件就執行另一部分語句,這就需要“條件分支結構”。 依次計算表示式1、表示式2…只要碰到一個表示式i為真,則執行語

程式設計菜鳥到大佬路:C語言程式

第七天學習精要 for迴圈 for迴圈語句 for迴圈一般用於將某段程式碼(語句組)重複執行若干次。 第一步:計算“表示式1”。 第二步:計算“表示式2”,若其值為true,則執行“{ }”中的語句組,然後轉到第三步;若為false,則不再執行“{}”中的

程式設計菜鳥到大佬路:C語言程式

第八天學習精要 break語句和continue語句 break語句 可以出現在迴圈體中(for、 while、 do…while迴圈均可),其作用是跳出迴圈。 在多重迴圈的情況下,break語句只能跳出直接包含它的那一重迴圈。 例題:如果兩個不同

C語言入門switch、迴圈語句

switch格式 switch格式: switch (條件表示式) { case 整數: // case可以有一個或多個 語句; break; case 整數: // case可以有一個或多個 語句;

C語言入門include、多檔案開發

include基本概念 #include <stdio.h> // 告訴系統printf函式是存在的, 告訴系統printf函式的格式(宣告printf函式) // include的作用

C語言例子3求兩個正整數的最大公約數及最小公倍數

# include <stdio.h> void main() { int x, y, num1, num2, temp; printf("請輸入兩個正整數:\n"); scanf("%d %d", &num1, &num2); if(num1 <

C語言基礎複雜宣告方式

我們本篇部落格的內容主要是解決如何閱讀C語言的宣告。比如: char a; char * b; const char * c; char * const d; char e[100]; char *f[100]; char (*g)[100]; struct

C語言入門運算子、sizeof運算子、if表示式

型別轉換、型別提升 #include <stdio.h> void test(); int main(int argc, const char * argv[]) { // 1.型別轉換 /* // int 佔用4個位元組 double

第一個CGI程序-----完全就是普通的c語言嘛‘*∩_∩*

同學 pat gree ostream 出現 targe 普通 get 方便 第一個CGI程序 ————完全就是普通的C語言嘛 ‘(*∩_∩*)′ PainterQ 2017年5月14日 上一篇博文裏面敘述了Apache的安裝和配置方法,恍恍惚惚我就擁有了自

深入淺出數據結構C語言9——多重表廣義表

不同 滿足 大學 logs 維數 我會 明顯 http 多維   在深入淺出數據結構系列前面的文章中,我們一直在討論的表其實是“線性表”,其形式如下:   由a1,a2,a3,……a(n-1)個元素組成的序列,其中每一個元素ai(0<i<n)都是一個“原子”,“

深入淺出數據結構C語言12——從二分查找到二叉樹

額外 最終 匹配 應對 點數據 隨機數 普通 釋放 三種   在很多有關數據結構和算法的書籍或文章中,作者往往是介紹完了什麽是樹後就直入主題的談什麽是二叉樹balabala的。但我今天決定不按這個套路來。我個人覺得,一個東西或者說一種技術存在總該有一定的道理,不是能解決某個

深入淺出數據結構C語言14——散列表

type unsigned size 表示 發現 blog 情況 減少 orb   我們知道,由於二叉樹的特性(完美情況下每次比較可以排除一半數據),對其進行查找算是比較快的了,時間復雜度為O(logN)。但是,是否存在支持時間復雜度為常數級別的查找的數據結構呢?答案是存在

深入淺出數據結構C語言15——優先隊列

turn github png 操作 pri 整數 過程 不難 nbsp   在普通隊列中,元素出隊的順序是由元素入隊時間決定的,也就是誰先入隊,誰先出隊。但是有時候我們希望有這樣的一個隊列:誰先入隊不重要,重要的是誰的“優先級高”,優先級越高越先出隊。這樣的數據結構我們稱

深入淺出數據結構C語言19——堆排序

-- 解決辦法 訪問 nsf 可能 bre 操作 數據塊 src   在介紹優先隊列的博文中,我們提到了數據結構二叉堆,並且說明了二叉堆的一個特殊用途——排序,同時給出了其時間復雜度O(N*logN)。這個時間界是目前我們看到最好的(使用Sedgewick序列的希爾排序時間