自動化構建工具make與Makefile用法示例詳解

1. 什么是 make / Makefile?
make 是一個自動化構建工具,它根據(jù)一個叫做 Makefile 的文件中的規(guī)則,自動編譯和鏈接程序。更本質一點說,make是一個命令,makefile是一個文件。Makefile 定義了文件之間的依賴關系以及如何生成目標文件。
注意:
Makefile文件名首字母可以大寫也可以小寫,但通常使用Makefile或makefile。
2. Makefile 的基本結構
依賴關系與依賴方法
一個典型的規(guī)則如下:
target: dependencies
recipe- 第一行:
target是要生成的目標文件,dependencies是生成該目標所依賴的文件。 - 第二行:
recipe是生成目標的命令(必須以 Tab 鍵開頭)。
示例:
myproc:myproc.c
gcc -o myproc myproc.c
myproc是目標文件,依賴myproc.c。- 依賴方法:
gcc -o myproc myproc.c。
make 的執(zhí)行規(guī)則
make命令默認掃描當前目錄下的Makefile,并執(zhí)行 第一個 目標。- 如果依賴文件的時間戳比目標文件新(即內容被修改過),則重新生成目標;否則跳過。
- 這樣設計可以避免重復編譯未修改的代碼,提高構建效率。
3. 如何判斷文件是否被修改?
之前說過,文件=內容+屬性
文件的三個時間戳(stat 命令)

在 Linux 中,每個文件都有三個時間戳:
| 時間戳 | 含義 |
|---|---|
| Access | 最近一次訪問時間(讀文件) |
| Modify | 最近一次內容修改時間 |
| Change | 最近一次屬性修改時間 |
注意:
- 修改文件內容時,Modify 和 Change 都會更新(因為文件大小等屬性變了)。
- 單獨修改屬性(如
chmod)只會更新 Change。- Access 時間不會每次都更新(為了減少磁盤 I/O),通常訪問多次后才刷新一次。
make 的判斷依據(jù)
make 只根據(jù) Modify 時間來判斷文件內容是否發(fā)生了更改。
如果依賴文件的 Modify 時間晚于目標文件,則重新生成。
使用 touch 修改時間
touch 命令除了可以創(chuàng)建空文件,還可以將所有時間戳更新為當前時間:
touch filename # 將 filename 的 Access、Modify、Change 都改為當前時間

利用這個特性,可以強制讓 make 認為文件被修改過,從而總是執(zhí)行某個目標。
touch可以統(tǒng)一更改時間到當前時刻
為什么講這些呢?
因為這些是關鍵字PHONY的原理
PHONY可以使程序可以總是被執(zhí)行,下圖中的clean就是如此,原理就是通過touch修改時間
所以我們使用make clean的時候可以總是被執(zhí)行

如果改成下圖這樣,make也可以總是被執(zhí)行,但為了節(jié)省編譯時間,通常不會這樣做

4. 偽目標(.PHONY)
為什么需要偽目標?
有時候我們想執(zhí)行一些不生成具體文件的操作(例如 clean 刪除中間文件)。但 clean 本身不是一個真實的文件,如果恰好當前目錄下有一個名為 clean 的文件,make clean 就會認為目標已經(jīng)存在且無依賴,從而跳過執(zhí)行。
使用 .PHONY 聲明偽目標
.PHONY: clean
clean:
rm -f *.o hello.PHONY告訴 make:clean不是一個真實文件,無論是否存在,都執(zhí)行它的命令。- 偽目標的原理本質上是 make 強制把它的依賴時間視為“總是比目標新”,從而總是執(zhí)行。
能否讓普通目標也總是執(zhí)行?
可以,但不推薦。例如:
hello: hello.c
gcc hello.c -o hello
touch hello # 更新 hello 的時間戳,這樣下次 make 時會認為 hello 比依賴新,從而不編譯這樣會導致每次 make 都重新編譯,失去了增量編譯的優(yōu)勢。因此,一般只對 clean 等輔助目標使用 .PHONY。
5. Makefile 的推導規(guī)則(自動推導)
拆分寫法(不常用,僅用于理解)
上述的命令可以拆分成下圖(一般不這樣寫,為了講解原理而拆分)
makefile會從上向下掃描,沒有就入棧,有就出棧執(zhí)行

在實際開發(fā)中,我們一般這樣寫

其中$(BIN) 和 (SRC)替換成(SRC) 替換成(SRC)替換成@和$^
$@表示目標文件。$^表示所有依賴文件。

如果不想回顯命令,可以在前邊加上@

如果想看起來直觀一點,可以在命令后輸出一行,指定內容
make 會從上向下掃描,發(fā)現(xiàn)需要 hello.o,則先去執(zhí)行生成 hello.o 的規(guī)則,再返回執(zhí)行 hello 的規(guī)則。
簡化寫法(常用)
實際開發(fā)中,我們通常會使用變量和自動化變量來簡化:
BIN = hello
SRC = hello.c
$(BIN): $(SRC)
gcc -o $@ $^
隱藏命令回顯
在命令前加 @ 可以阻止 make 輸出該命令本身:
$(BIN): $(SRC)
@gcc $^ -o $@
@echo "編譯完成"處理多個源文件

%.o: %.c是一個模式規(guī)則,表示所有.o文件都依賴對應的.c文件。$<表示第一個依賴文件(即對應的.c文件)。
6. Makefile 的終極形態(tài)(通用模板)
說明:
wildcard函數(shù):展開當前目錄下所有.c文件。SRC:.c=.o是變量替換語法,將所有.c后綴替換為.o。- 這個模板可以自動適應任意數(shù)量的
.c文件,無需手動列出。

SRC=$(shell ls *.c) SRC=$(wildcard *.c) #兩種方式都可以 顯示當前目錄下的所有.c文件 OBJ=$(SRC:.c=.o) #把所有SRC內部的.c文件替換成.o文件
總結
| 概念 | 說明 |
|---|---|
| 依賴關系 | 目標文件依賴哪些源文件或中間文件 |
| 依賴方法 | 生成目標的具體命令,前面必須有一個 Tab 鍵 |
| 時間戳 | make 根據(jù)文件的 Modify 時間判斷是否需要重新編譯 |
| 偽目標 | 用 .PHONY 聲明,總是執(zhí)行其命令(如 clean) |
| 自動化變量 | $@(目標)、$^(所有依賴)、$<(第一個依賴) |
| 模式規(guī)則 | %.o: %.c 表示所有 .o 依賴同名 .c |
| 函數(shù) | wildcard、shell 等用于動態(tài)獲取文件列表 |
掌握這些內容,你就可以編寫靈活、高效的 Makefile,實現(xiàn)項目的自動化構建。
新編譯 |
| 偽目標 | 用.PHONY聲明,總是執(zhí)行其命令(如clean) |
| 自動化變量 |$@(目標)、$^(所有依賴)、$<(第一個依賴) |
| 模式規(guī)則 |%.o: %.c表示所有.o依賴同名.c|
| 函數(shù) |wildcard、shell等用于動態(tài)獲取文件列表 |
掌握這些內容,你就可以編寫靈活、高效的 Makefile,實現(xiàn)項目的自動化構建。
到此這篇關于自動化構建工具make與Makefile用法示例詳解的文章就介紹到這了,更多相關自動化構建工具make與Makefile內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python執(zhí)行Linux系統(tǒng)命令的4種方法
這篇文章主要介紹了Python執(zhí)行Linux系統(tǒng)命令的4種方法,即在Python腳本中調用Shell命令,需要的朋友可以參考下2014-10-10
Shell腳本配合iptables屏蔽來自某個國家的IP訪問
這篇文章主要介紹了Shell腳本配合iptables屏蔽來自某個國家的IP訪問,本文利用IPdeny的IP數(shù)據(jù),然后用Shell腳本導入iptables實現(xiàn)屏蔽IP訪問,需要的朋友可以參考下2015-04-04

