1. 程式人生 > 其它 >[make] 第三章 Makefile規則

[make] 第三章 Makefile規則

第三章 Makefile 規則


1. 規則的定義

# 第一種寫法
規則的目標 : 規則目標的依賴
[Tab]規則的命令
# 規則的目標可以是空格分開的多個檔名,也可以是一個如”clean“的標籤
# 規則的目標的檔名可以使用萬用字元
# 規則中需要使用”$“時應使用”$$“來表示,因為”$“在規則中由其他含義

# 第二種寫法
規則的目標 : 規則目標的依賴 ; 規則的命令 ...

在makefile檔案中,除了”終極目標“所在的規則以外,其他規則的順序在檔案中沒有意義。”終極目標“就是當沒有使用make命令列指定具體目標時,make預設的更新的那一個目標。它時makefile檔案中第一個規則的目標,如果第一個規則有多個目標的話,則多個目標中的第一個將會被作為make的”終極目標“。兩種情況例外:

  • 目標名以”.“開始的並且氣候不存在”/“
  • 模式規則的目標

2. 規則依賴的型別

  • 常規依賴
    • 即當依賴檔案比目標檔案新時,會重建目標,這一類依賴稱為常規依賴
  • ”order-only“依賴
    • 即當依賴檔案比目標檔案新時,可以不用更新規則的目標
    • 書寫時,”order-only“依賴使用管道符號”|“開始,作為目標的一個依賴檔案

規則列表中,管道符左邊的為常規依賴,右邊的為”order-only“依賴,如:

規則的目標 : 常規依賴 | "order-only"依賴
# 其中常規依賴可以為空
# 如果一個檔案同時在兩種依賴中出現,則該檔案作為常規依賴處理

3. 檔名使用萬用字元

Makefile中表示檔名時可以使用萬用字元,如“*”、“?”和“[...]”。Makefile中萬用字元可以出現在以下兩種場合:

  • 可以在規則的目標、依賴中,make在讀取Makefile時會自動對其進行匹配處理(萬用字元展開)

  • 可以出現在規則的命令中,萬用字元的通配處理是在shell在執行此命令時完成的

  • 除上述兩種情況外,不能直接使用萬用字元,而是需要通過函式“wildcard”來實現

如果規則的一個檔名包含萬用字元字元(“*”、“.”等字元),在使用時需要對檔名中的通配字元進行轉義(使用“\”進行轉義),此外在Makefile的變數定義中不能直接使用萬用字元,需要使用wildcard函式實現通配的目的

3.1 函式wildcard

# wildcard 的使用
objects = $(wildcard *.o)
# 上述在將變數objects進行展開時,表示為當前目錄下的所有以“.o”為字尾的檔案

$(wildcard PATTERN...)
# 上述被展開為已經存在的、使用空格分開的、匹配PATTERN模式的所有檔案列表

4. 目錄搜尋

在一個較大的工程中,一般會將原始碼和二進位制檔案安排在不同的目錄來進行區分管理,這種情況下,我們可以使用make提供的目錄搜素依賴檔案功能(在指定的若干個目錄下自動搜尋依賴檔案)。該種功能的好處在於,當工程的目錄結構發生變化後,可以在不更改Makefile的規則下,只通過更改依賴檔案的搜尋目錄,從而實現目標的構建。

4.1 一般搜尋(VPATH)

GNU make可以識別一個特殊變數“VPATH”,通過變數“VPATH”可以指定依賴檔案的搜尋路徑。當規則的依賴檔案在當前的目錄不存在時,make會在此變數所指定的目錄下去尋找這些依賴檔案。通常我們使用此變數來指定規則的依賴檔案的搜尋路徑,但實際上,“VPATH”變數所指定的是Makefile中所有檔案的搜尋路徑,包括了規則的依賴檔案和目標檔案。

VPATH = dir1:dir2:dir3
# 定義該變數時,使用空格或者冒號將多個需要所有的目錄分開
# make在搜尋時,首先搜尋當前目錄,之後再按照該變數的目錄順序進行搜尋

4.2 選擇性搜尋(關鍵字vpath)

make的關鍵字“vpath”可以為不同型別的檔案(有檔名區分)指定不同的搜尋目錄,使用方法有三種:

vpath PATTERN DIRECTORIES
# 為所有符合模式“PATTERN”的檔案指定搜尋目錄“DIRECTORIES”,多個目錄使用空格或冒號分開
# “PATTERN”需要包含模式字元“%”。“%”表示匹配一個或者多個字元,例如“%.h”表示所有以“.h”結尾的檔案

vpath PATTERN
# 清除之前為符合模式“PATTERN”的檔案設定的搜尋路徑

vpath
# 清除已被設定的檔案搜尋路徑

4.3 目錄搜尋的機制

當存在上述的搜尋目錄時的重建機制,並在搜尋目錄中發現有目標檔案時

  • 如果目標檔案需要被重建,則會在Makefile所在的目錄進行目標檔案的重建
  • 當目標檔案不需要被重建時,則什麼也不做

如果使用GPATH來替代VPATH,則上述的重建機制變為

  • 如果目標檔案需要被重建,則make會在找到的目標檔案所在的路徑對目標檔案進行更新

4.4 命令列和搜尋目錄

使用前提:make在執行時,通過目錄搜尋得到的目標的依賴檔案可能會在其他目錄(此時依賴檔案為檔案的完整路徑名),但是已經存在的規則命令卻不能發生變化。因此,書寫命令時我們必須保證當依賴檔案在其他目錄下被發現時,規則的命令能夠正確執行。

可以通過在規則的命令列使用“自動化變數”,諸如“$^”等。

  • “$^”代表所有通過目錄搜尋得到的依賴檔案的完整路徑名列表
    • 規則的依賴檔案列表中可以包含標頭檔案,當使用該自動化變數時,命令列中也會出現這些標頭檔案,由於這些標頭檔案的作用只有在make程式決定目標是否需要重建時才有意義,因此一般使用“$<”替換當前的自動化變數
  • “$@”代表規則的目標
  • “$<”代表規則中通過目錄搜尋得到的依賴檔案列表的第一個依賴檔案
VPATH = src:../headers
foo.o : foo.c head1.h head2.h
	cc -c $(CFLAGS) $^ -o $@
# 命令相當於:cc -c $(CFLAGS) [VPATH]foo.c [VPATH]head1.h [VPATH]head2.h -o foo.o

VPATH = src:../headers
foo.o : foo.c head1.h head2.h
	cc -c $(CFLAGS) $< -o $@
# 命令相當於: cc -c $(CFLAGS) [VPATH]foo.c -o foo.o

4.5 隱含的規則和搜尋目錄

通過變數“VPATH”、或者關鍵字“vpath”指定的搜尋目錄,對於隱含規則同樣有效。例如:一個目標檔案“foo.o”在 Makefile 中沒有重建它的明確規則,make 會使用隱含規則來由已經存在的“foo.c”來重建它。當“foo.c”在當前目錄下不存在時,make 將會進行目錄搜尋。如果能夠在一個可以搜尋的目錄中找到此檔案,同樣 make 會使用隱含規則根據搜尋到的檔案完整的路徑名去重建目標,編譯這個.c 原始檔。

4.6 庫檔案和搜尋目錄

在規則的依賴中使用庫檔案

foo : foo.c -lmylib
	cc $^ -o $@
# -lmylib表示在重建foo目標檔案時需要連結到的庫檔案,其中庫檔名mylib(可以是動態也可以是靜態)
# make會根據變數“.LIBPATTERNS”指定的模式來尋找mylib
# 一般變數“.LIBPATTERNS”的模式為“lib%.so lib%.a”
# make在找庫檔案時,首先查詢當前目錄和VPATH等變數指定的目錄中是否有名為“libmylib.so”的依賴檔案
# 如果沒有則會在系統提供的庫檔案目錄中去尋找,如果還沒有找到,則會採用第二種模式即查詢名為“libmylib.a”的依賴檔案,查詢路徑和上一步相同

5. Makefile偽目標

偽目標定義:當使用make命令指定此目標時,這個目標所在規則定義的命令,無論目標檔案是否存在都被無條件執行

偽目標的作用:在執行make時可以指定這個目標來執行其所在規則定義的命令,也可稱為標籤。

使用偽目標的兩點原因:

  • makefile中只執行命令的目標和工作目錄下的實際檔案出現名字衝突
  • 提高make時的效率,尤其是大型的工程

5.1 偽目標可以解決make時目標與工作目錄重名的衝突

背景介紹

clean:
	rm *.o temp

當工作目錄下沒有clean這個檔案時,輸入“make clean”即可執行“rm *.o temp”這個規則;但當工作目錄存在名為“clean”的檔案時,由於該目標沒有依賴項,make在檢查是否需要更新時,始終認為當前工作目錄下的檔案為最新的,導致其下的規則無法被執行。

執行結果

當工作目錄下沒有與目標重名時,其規則可以正常被執行

![image-20211227230350776](.assert/第三章 Makefile 規則/image-20211227230350776.png)

當工作目錄下存在與目標重名時,規則不被執行

![image-20211227230531044](.assert/第三章 Makefile 規則/image-20211227230531044.png)

使用偽目標代替

.PHONY : clean
clean:
	rm -rf *.o temp

執行結果

![image-20211227230916065](.assert/第三章 Makefile 規則/image-20211227230916065.png)

:當一個目標被宣告為一個偽目標後,make在執行此規則時不會去試圖查詢隱含規則來建立它

5.2 偽目標在make並行和遞迴執行過程中的作用

  • 偽目標可以作為依賴出現,其作用相當於程式語言中的函式呼叫

make的隱含變數“RM”,其實質為“rm -f”,可以使用“$(RM)”在makefile中直接使用

6. Makefile的特殊目標

  • .PHONY
    • 該目標的所有依賴都被認為是偽目標
    • 偽目標定義:當使用make命令指定此目標時,這個目標所在規則定義的命令,無論目標檔案是否存在都被無條件執行
  • .DEFAULT
    • Makefile中,該目標所在規則定義的命令,被用在重建那些沒有具體規則的目標(明確規則和隱含規則)。即:一個檔案作為某個規則的依賴,但卻不是另外一個規則的目標時,Make程式無法找到重建此檔案的規則,此種情況時就會執行“.DEFAULT”所指定的命令
  • .PRECIOUS
  • .INTERMEDIATE
  • .SECONDARY
    • 該目標的依賴檔案被作為中間過程檔案對待。但這些檔案不會被自動刪除。當該目標沒有依賴檔案時,表示將所有的檔案作為中間過程檔案(不會自動刪除任何檔案)
  • .DELETE_ON_ERROR
    • 如果該目標存在,則make在執行過程,如果規則的命令執行錯誤,將刪除已經被修改的目標檔案
  • .IGNORE
    • make在執行時,會忽略建立該目標依賴檔案的執行命令錯誤。當此目標沒有依賴問件時,將忽略所有命令執行的錯誤
    • 此目標的命令沒有意義
  • .LOW_RESOLUTION_TIME
  • .SILENT
    • 出現在目標“.SILENT”的依賴列表中的檔案,make 在建立這些檔案時,不打印出重建此檔案所執行的命令。同樣,給目標“.SILENT”指定命令列是沒有意義的
    • 可以用“-s”或者“-silent”選項替代此特殊目標
  • .EXPORT_ALL_VARIABLES(
    • 此目標應該作為一個簡單的沒有依賴的目標,它的功能含義是將之後所有的變數傳遞給子make 程序
  • .NOTPARALLEL
    • Makefile 中,如果出現目標“.NOPARALLEL”,則所有命令按照序列方式執行,即使存在make 的命令列引數“-j”。但在遞迴呼叫的子 make 程序中,命令可以並行執行。此目標不應該有依賴檔案,所有出現的依賴檔案將被忽略

7. 多目標

一個規則中可以有多個目標,規則所定義的命令對所有的目標有效。多目標規則意味著所有的目標具有相同的依賴檔案

自動環變數“$@”

多目標的使用場景

  • 只需要一個描述依賴關係的規則,不需要在規則中定義命令

    • target_1.o target_2.o target_3.o : command.h
      
  • 對於多個具有類似重建命令的多個目標,重建這些目標的命令並不需要是完成相同的,可以在命令中使用自動環變數“$@”來引用具體的目標,完成對其的重建

    • bigoutput littleoutput : text.g
      	generate text.g -$(subst output,,$@) > $@
      	
      # 上述規則相當於
      bigoutput : text.g
      	generate text.g -big > bigoutput
      littleoutput : text.g
      	generate text.g -little > littleoutput
      

8. 多規則目標

定義

  • 一個檔案可以作為多個規則的目標
  • 多個規則中只能有一個規則定義命令(重建此目標的命令只能出現在一個規則中,可以是多條命令)
    • 如果多個規則同時給出重建此目標的命令,make將使用最後一個規則中所定義的命令,同時提示錯誤資訊
    • 例外,使用“.”開頭的多規則目標檔案,可以在多個規則中給出多個重建命令
    • 相同的目標使用不同的規則中所定義的命令的方法為“雙冒號”規則
  • make在執行時,以這個檔案為目標的規則的所有依賴檔案將會被合成此目標一個依賴檔案列表
    • 當依賴列表中任何一個依賴檔案比目標更新時,將會執行特定的命令來重建這個目標

9. 靜態模式規則

定義

  • 規則存在多個目標
  • 不同的目標可以根據目標檔案的名字來自動構造出依賴檔案
  • 不需要具有相同的依賴檔案,但依賴檔案必須是相類似的而不是完成相同的

語法

TARGETS ...: TARGET-PATTERN: PREREQ-PATTERNS ...
	COMMANDS
    ....
    
# TARGETS: 此規則的一系列目標檔案
# ”TARGET-PATTERN“和”PREREQ-PATTERNS“:說明了如何為每一個目標檔案生成依賴檔案
# 從”TAGET-PATTERN“,即目標模式的目標名字中抽取一部分字串(稱為”莖“)
# 使用”莖“來替代依賴模式(PAEREQ-PATTERNS)中的相應部分來產生對應目標的依賴檔案

# 模式字元”%“
# 在目標模式中”%“可以匹配目標檔案的任何部分;eg: "foo.o"符合模式”%.o“,其莖為”foo“
# 每一個目標依賴檔案都是使用莖替代依賴模式中的模式字元”%“得到的;eg:"%.c", 則莖”foo“,替換後的依賴為”foo.c“

注: 在Makefile中,”$<" 表示規則中的第一個依賴檔案,"$@"表示規則中目標檔案

10. 雙冒號規則

同一個目標可以出現在多個規則中,但這些規則要麼都是單冒號規則要麼都是雙冒號規則,不存在一個目標同時出現在單冒號與雙冒號規則的可能。

對於一個目標出現在多個雙冒號規則的情況,每一個規則相互獨立,只有當目標的依賴比目標新時會執行對應的規則
單冒號規則則會將多個同目標規則的依賴進行整合為一個規則

此外,雙冒號規則的目標沒有依賴時,如果make當前目標,則該規則始終被執行
單目標規則則是在工作目錄下都目標檔案時,該規則始終不會被執行,因為此處會認為,目標檔案始終最新

11. 自動產生依賴

GCC通過“-M”選項可以自動對目標原始檔產生依賴關係
例如:main.c中有include 一些標頭檔案,當頭檔案更改時需要重新執行指令,傳統做法為:“main.o : yilai.h”
為避免繁瑣的書寫這種規則,可以使用“GCC -M main.c”自動對main.c中include的標頭檔案生成對應的依賴關係