1. 程式人生 > 其它 >【hadoop出口計算】trino(presto)+clickhouse

【hadoop出口計算】trino(presto)+clickhouse

Makefile 檔案描述了 Linux 系統下 C/C++ 工程的編譯規則,它用來自動化編譯 C/C++ 專案一旦寫編寫好 Makefile 檔案,只需要一個 make 命令,整個工程就開始自動編譯,不再需要手動執行 GCC 命令

一箇中大型 C/C++ 工程的原始檔有成百上千個,它們按照功能、模組、型別分別放在不同的目錄中,Makefile 檔案定義了一系列規則,指明瞭原始檔的編譯順序、依賴關係、是否需要重新編譯等。

參考:Makefile教程

1.Makefile檔案是什麼?

Windows環境:如果你是在 Windows 下作開發的話不需要去考慮這個問題,因為 Windows 下的整合開發環境(IDE)已經內建了 Makefile,或者說會自動生成 Makefile,我們不用去手動編寫。

Linux環境:Linux 中卻不能這樣,需要我們去手動的完成這項工作。不懂 Makefile,就操作不了多檔案程式設計,就完成不了相對於大的工程專案的操作。

Makefile 可以簡單的認為是一個工程檔案的編譯規則,描述了整個工程的編譯和連結等規則。其中包含了那些檔案需要編譯,那些檔案不需要編譯,那些檔案需要先編譯,那些檔案需要後編譯,那些檔案需要重建等等。編譯整個工程需要涉及到的,在 Makefile 中都可以進行描述。換句話說,Makefile 可以使得我們的專案工程的編譯變得自動化,不需要每次都手動輸入一堆原始檔和引數

以 Linux 下的C語言開發為例來具體說明一下,多檔案編譯生成一個檔案,編譯的命令如下所示:

gcc -o outfile name1.c name2.c ...

outfile 要生成的可執行程式的名字,nameN.c 是原始檔的名字。

這是我們在 Linux 下使用 gcc 編譯器編譯 C 檔案的例子。如果我們遇到的原始檔的數量不是很多的話,可以選擇這樣的編譯方式。如果原始檔非常的多的話,就會遇到下面的這些問題:

1) 編譯的時候需要連結庫的的問題。

2) 編譯大的工程會花費很長的時間。

對於這樣的問題我們 Makefile 可以解決,Makefile 支援多執行緒併發操作,會極大的縮短我們的編譯時間,並且當我們修改了原始檔之後,編譯整個工程的時候,make 命令只會編譯我們修改過的檔案,沒有修改的檔案不用重新編譯,也極大的解決了我們耗費時間的問題。

2.Makefile檔案中包含哪些規則?

Makefile 描述的是檔案編譯的相關規則,它的規則主要是兩個部分組成,分別是依賴的關係和執行的命令,其結構如下所示:

targets : prerequisites
command

相關說明如下:

  • targets:規則的目標,可以是 Object File(一般稱它為中間檔案),也可以是可執行檔案,還可以是一個標籤;
  • prerequisites:是我們的依賴檔案,要生成 targets 需要的檔案或者是目標。可以是多個,也可以是沒有;
  • command:make 需要執行的命令(任意的 shell 命令)。可以有多條命令,每一條命令佔一行。Makefile 中的任何命令都要以tab鍵開始。

舉例:

test:test.c
    gcc -o test test.c

上述程式碼實現的功能就是編譯 test.c 檔案,通過這個例項可以詳細的說明 Makefile 的具體的使用。

其中 test 是的目標檔案,也是我們的最終生成的可執行檔案。依賴檔案就是 test.c 原始檔,重建目標檔案需要執行的操作是gcc -o test test.c。這就是 Makefile 的基本的語法規則的使用。

簡單的概括一下Makefile 中的內容,它主要包含有五個部分,分別是:

1) 顯式規則

顯式規則說明了,如何生成一個或多的的目標檔案。這是由 Makefile 的書寫者明顯指出,要生成的檔案,檔案的依賴檔案,生成的命令。

2) 隱晦規則

由於我們的 make 命名有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地簡略地書寫 Makefile,這是由 make 命令所支援的。

3) 變數的定義

在 Makefile 中我們要定義一系列的變數,變數一般都是字串,這個有點像C語言中的巨集,當 Makefile 被執行時,其中的變數都會被擴充套件到相應的引用位置上。

4) 檔案指示

其包括了三個部分,一個是在一個 Makefile 中引用另一個 Makefile,就像C語言中的 include 一樣;另一個是指根據某些情況指定 Makefile 中的有效部分,就像C語言中的預編譯 #if一樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在後續的部分中講述。

5) 註釋

Makefile 中只有行註釋,和 UNIX 的 Shell 指令碼一樣,其註釋是用“#”字元,這個就像 C/C++中的“//”一樣。如果你要在你的 Makefile 中使用“#”字元,可以用反斜框進行轉義,如:“\#”。

3.Makefile的工作流程

當我們在執行 make 這條命令的時候,make 就會去當前檔案下找要執行的編譯規則,也就是 Makefile 檔案。我們編寫 Makefile 的時候可以使用的檔案的名稱 :"GNUmakefile" 、"makefile" 、"Makefile" ,make 執行時會去尋找 Makefile 檔案,找檔案的順序也是這樣的。

工作流程

清除工作目錄中的過程檔案

我們在使用的時候會產生中間檔案會讓整個檔案看起來很亂,所以在編寫 Makefile 檔案的時候會在末尾加上這樣的規則語句:

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

其中 "*.o" 是執行過程中產生的中間檔案,"test" 是最終生成的執行檔案。我們可以看到 clean 是獨立的,它只是一個偽目標(在《Makefile偽目標》的章節中詳細介紹),不是具體的檔案。不會與第一個目標檔案相關聯,所以我們在執行 make 的時候也不會執行下面的命令。在shell 中執行 "make clean" 命令,編譯時的中間檔案和生成的最終目標檔案都會被清除,方便我們下次的使用。

4.Makefile萬用字元的使用

Makefile 是可以使用 shell 命令的,所以 shell 支援的萬用字元在 Makefile 中也是同樣適用的。 shell 中使用的萬用字元有:"*","?","[...]"。具體看一下這些萬用字元的表示含義和具體的使用方法。

5.Makefile變數的定義和使用

Makefile 檔案中定義變數的基本語法如下:

變數的名稱=值列表

它並沒有像其它語言那樣定義變數的時候需要使用資料型別。變數的名稱可以由大小寫字母、阿拉伯數字和下劃線構成。等號左右的空白符沒有明確的要求,因為在執行 make 的時候多餘的空白符會被自動的刪除。至於值列表,既可以是零項,又可以是一項或者是多項。如:VALUE_LIST = one two three

呼叫變數的時候可以用 "$(VALUE_LIST)" 或者是 "${VALUE_LIST}" 來替換,這就是變數的引用。

舉例:

OBJ=main.o test.o test1.o test2.o
test:$(OBJ)
      gcc -o test $(OBJ)

變數的基本賦值

知道了如何定義,下面我們來說一下 Makefile 的變數的四種基本賦值方式:

  • 簡單賦值 ( := ) 程式語言中常規理解的賦值方式,只對當前語句的變數有效。
  • 遞迴賦值 (=) 賦值語句可能影響多個變數,所有目標變數相關的其他變數都受影響。
  • 條件賦值 ( ?= ) 如果變數未定義,則使用符號中的值定義變數。如果該變數已經賦值,則該賦值語句無效。
  • 追加賦值 ( += ) 原變數用空格隔開的方式追加一個新值。

6.Makefile自動化變數

自動化變數可以理解為由 Makefile 自動產生的變數。

在模式規則中,規則的目標和依賴的檔名代表了一類的檔案。規則的命令是對所有這一類檔案的描述。我們在 Makefile 中描述規則時,依賴檔案和目標檔案是變動的,顯然在命令中不能出現具體的檔名稱,否則模式規則將失去意義。

那麼模式規則命令中該如何表示檔案呢?就需要使用“自動化變數”,自動化變數的取值根據執行的規則來決定,取決於執行規則的目標檔案和依賴檔案。下面是對所有的自動化變數進行的說明:

我們在執行 make 的時候,make 會自動識別命令中的自動化變數,並自動實現自動化變數中的值的替換,這個類似於編譯C語言檔案的時候的預處理的作用。

7.Makefile目標檔案搜尋(VPATH和vpath)

如果所有的原始檔都是存放在與 Makefile 相同的目錄下,只要依賴的檔案存在,並且依賴規則沒有問題,執行 make命令整個工程就會按照對我們編寫規則去編譯,最終會重建目標檔案。

如果需要的檔案是存在於不同的路徑下,在編譯的時候要去怎麼辦呢(不改變工程的結構)?這就用到了 Makefile 中為我們提供的目錄搜尋檔案的功能。

常見的搜尋的方法的主要有兩種:一般搜尋VPATH和選擇搜尋vpath。乍一看只是大小寫的區別,其實兩者在本質上也是不同的。

VPATH 和 vpath 的區別:

  • VPATH 是變數,更具體的說是環境變數,Makefile 中的一種特殊變數,使用時需要指定檔案的路徑;
  • vpath 是關鍵字,按照模式搜尋,也可以說成是選擇搜尋。搜尋的時候不僅需要加上檔案的路徑,還需要加上相應限制的條件。

VPATH的使用

在 Makefile 中可以這樣寫:

VPATH := src

我們可以這樣理解,把 src 的值賦值給變數 VPATH,所以在執行 make 的時候會從 src 目錄下找我們需要的檔案。

當存在多個路徑的時候我們可以這樣寫:VPATH := src car

或者是VPATH := src:car

多個路徑之間要使用空格或者是冒號隔開,表示在多個路徑下搜尋檔案。搜尋的順序為我們書寫時的順序,拿上面的例子來說,我們應該先搜尋 src 目錄下的檔案,再搜尋 car 目錄下的檔案。

vpath的使用

VPATH 是搜尋路徑下所有的檔案,而 vpath 更像是添加了限制條件,會過濾出一部分再去尋找。

具體用法:

1) vpath PATTERN DIRECTORIES
2) vpath PATTERN
3) vpath

( PATTERN:可以理解為要尋找的條件,DIRECTORIES:尋找的路徑 )

首先是用法一,命令格式如下:

vpath test.c src

可以這樣理解,在 src 路徑下搜尋檔案 test.c。

使用什麼樣的搜尋方法,主要是基於編譯器的執行效率。

使用 VPATH 的情況是路徑下的檔案較少,或者是搜尋的檔案不能使用萬用字元表示,這些情況下使用VPATH最好。

如果存在某個路徑的檔案特別的多或者是可以使用萬用字元表示的時候,就不建議使用 VPATH 這種方法,為什麼呢?因為 VPATH 在去搜索檔案的時沒有限制條件,所以它會去檢索這個目錄下的所有檔案,每一個檔案都會進行對比,搜尋和我們目錄名相同的檔案,不僅速度會很慢,而且效率會很低。我們在這種情況下就可以使用 vpath 搜尋,它包含搜尋條件的限制,搜尋的時候只會從我們規定的條件中搜索目標,過濾掉不符合條件的檔案,當然查詢的時候也會比較的快。

8.Makefile路徑搜尋使用案例

9.Makefile隱含規則

所謂的隱含規則就是需要我們做出具體的操作,系統自動完成。編寫 Makefile 的時候,可以使用隱含規則來簡化Makefile 檔案編寫。

test:test.o
    gcc -o test test.o
test.o:test.c

我們可以在 Makefile 中這樣寫來編譯 test.c 原始檔,相比較之前少寫了重建 test.o 的命令。但是執行 make,發現依然重建了 test 和 test.o 檔案,執行結果卻沒有改變。這其實就是隱含規則的作用。

因為完整的檔案是:

test:test.o
    gcc -o test test.o
test.o:test.c
  gcc -o test.o test.c

在某些時候其實不需要給出重建目標檔案的命令,有的甚至可以不需要給出規則。例項:

test:test.o
    gcc -o test test.o

執行的結果是相同的。

注意:隱含條件只能省略中間目標檔案重建的命令和規則,但是最終目標的命令和規則不能省略。

隱含規則的具體的工作流程:

make 執行過程中找到的隱含規則,提供了此目標的基本依賴關係。確定目標的依賴檔案和重建目標需要使用的命令列。隱含規則所提供的依賴檔案只是一個基本的(在C語言中,通常他們之間的對應關係是:test.o 對應的是 test.c 檔案)。當需要增加這個檔案的依賴檔案的時候要在 Makefile 中使用沒有命令列的規則給出。

10.Makefile ifeq、ifneq、ifdef和ifndef(條件判斷)

需要解決的問題:要根據判斷,分條件執行語句。
條件語句的作用:條件語句可以根據一個變數的值來控制 make 執行或者時忽略 Makefile 的特定部分,條件語句可以是兩個不同的變數或者是常量和變數之間的比較。

條件語句使用優點:Makefile 中使用條件控制可以做到處理的靈活性和高效性。

ifeq (ARG1, ARG2)

else

endif

11.Makefile偽目標

所謂的偽目標可以這樣來理解,它並不會建立目標檔案,只是想去執行這個目標下面的命令。偽目標的存在可以幫助我們找到命令並執行。

使用偽目標有兩點原因:

  • 避免我們的 Makefile 中定義的只執行的命令的目標和工作目錄下的實際檔案出現名字衝突。
  • 提高執行 make 時的效率,特別是對於一個大型的工程來說,提高編譯的效率也是我們所必需的。

我們先來看一下第一種情況的使用。如果需要書寫這樣一個規則,規則所定義的命令不是去建立檔案,而是通過 make 命令明確指定它來執行一些特定的命令。例項:

clean:
    rm -rf *.o test

規則中 rm 命令不是建立檔案 clean 的命令,而是執行刪除任務,刪除當前目錄下的所有的 .o 結尾和檔名為 test 的檔案。當工作目錄下不存在以 clean 命令的檔案時,在 shell 中輸入 make clean 命令,命令 rm -rf *.o test 總會被執行 ,這也是我們期望的結果。

如果當前目錄下存在檔名為 clean 的檔案時情況就會不一樣了,當我們在 shell 中執行命令 make clean,由於這個規則沒有依賴檔案,所以目標被認為是最新的而不去執行規則所定義的命令。因此命令 rm 將不會被執行。

為了解決這個問題,刪除 clean 檔案或者是在 Makefile 中將目標 clean 宣告為偽目標

將一個目標宣告稱偽目標的方法是將它作為特殊的目標.PHONY的依賴,如下:

.PHONY:clean

這樣 clean 就被宣告成一個偽目標,無論當前目錄下是否存在 clean 這個檔案,當我們執行 make clean 後 rm 都會被執行。而且當一個目標被宣告為偽目標之後,make 在執行此規則時不會去試圖去查詢隱含的關係去建立它。這樣同樣提高了 make 的執行效率,同時也不用擔心目標和檔名重名而使我們的編譯失敗。

在書寫偽目標的時候,需要宣告目標是一個偽目標,之後才是偽目標的規則定義。目標 "clean" 的完整書寫格式如下:

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

12.Makefile常用字串處理函式

函式的呼叫和變數的呼叫很像。引用變數的格式為$(變數名),函式呼叫的格式如下:

$(<function> <arguments>) 或者是 ${<function> <arguments>}

其中,function 是函式名,arguments 是函式的引數,引數之間要用逗號分隔開。而引數和函式名之間使用空格分開。呼叫函式的時候要使用字元“$”,後面可以跟小括號也可以使用花括號。這個其實我們並不陌生,我們之前使用過許多的函式,比如說展開萬用字元的函式 wildcard,以及字串替換的函式 patsubst ,Makefile 中函式並不是很多

字串處理函式,這些都是我們經常使用到的函式,下面是對函式詳細的介紹。

1. 模式字串替換函式,函式使用格式如下:

$(patsubst <pattern>,<replacement>,<text>)

函式說明:函式功能是查詢text 中的單詞是否符合模式 pattern,如果匹配的話,則用 replacement 替換。返回值為替換後的新字串。例項:

OBJ=$(patsubst %.c,%.o,1.c 2.c 3.c)
all:
    @echo $(OBJ)

執行 make 命令,我們可以得到的值是 "1.o 2.o 3.o",這些都是替換後的值。

注:all通常也是一個.PHONY目標【偽目標】

2. 字串替換函式,函式使用格式如下:

$(subst <from>,<to>,<text>)

函式說明:函式的功能是把字串中的 form 替換成 to,返回值為替換後的新字串。

3. 去空格函式,函式使用格式如下:

$(strip <string>)

函式說明:函式的功能是去掉字串的開頭和結尾的字串,並且將其中的多個連續的空格合併成為一個空格。返回值為去掉空格後的字串。

4. 查詢字串函式,函式使用格式如下:

$(findstring <find>,<in>)

函式說明:函式的功能是查詢 in 中的 find,如果我們查詢的目標字串存在。返回值為目標字串,如果不存在就返回空。

5. 過濾函式,函式使用格式如下:

$(filter <pattern>,<text>)

函式說明:函式的功能是過濾出 text 中符合模式 pattern 的字串,可以有多個 pattern 。返回值為過濾後的字串。

6. 反過濾函式,函式使用格式如下:

$(filter-out <pattern>,<text>)

函式說明:函式的功能是功能和 filter 函式正好相反,但是用法相同。去除符合模式 pattern的字串,保留符合的字串。返回值是保留的字串。

7. 排序函式,函式使用格式如下:

$(sort <list>)

函式說明:函式的功能是將<list>中的單詞排序(升序)。返回值為排列後的字串。注意:sort會去除重複的字串

8. 取單詞函式,函式使用格式如下:

$(word <n>,<text>)

函式說明:函式的功能是取出函式<text>中的第n個單詞。返回值為我們取出的第 n 個單詞。

13.Makefile常用檔名操作函式

在編寫 Makefile 的時候,很多情況下需要對檔名進行操作。例如獲取檔案的路徑,去除檔案的路徑,取出檔案字首或字尾等等。

當遇到這樣的問題的時手動修改是不太可能的,因為檔案可能會很多,而且 Makefile 中操作檔名可能不止一次。所以 Makefile 給我們提供了相應的函式去實現檔名的操作。

注意:下面的每個函式的引數字串都會被當作或是一個系列【多個】的檔名來看待。

1. 取目錄函式,函式使用格式如下:

$(dir <names>)

函式說明:函式的功能是從檔名序列 names 中取出目錄部分。如果 names 中有 "/" ,取出的值為 最後一個反斜槓之前的部分。如果沒有反斜槓將返回“./”。例項:

OBJ=$(dir src/foo.c hacks)
all:
    @echo $(OBJ)

執行 make 命令,我們可以得到的值是“src/ ./”。提取檔案 foo.c 的路徑是 "/src" 和檔案 hacks 的路徑 "./"。

2. 取檔案函式,函式使用格式如下:

$(notdir <names>)

函式說明:函式的功能是從檔名序列 names 中取出非目錄的部分【檔名】。非目錄的部分是最後一個反斜槓之後的部分。返回值為檔案非目錄的部分。

3. 取字尾名函式,函式使用格式如下:

$(suffix <names>)

函式說明:函式的功能是從檔名序列中 names 中取出各個檔案的字尾名。返回值為檔名序列 names 中的字尾序列,如果檔案沒有後綴名,則返回空字串。

4. 取字首函式,函式使用格式如下:

$(basename <names>)

函式說明:函式的功能是從檔名序列 names 中取出各個檔名的字首部分【除了字尾的其他所有字元】。返回值為被取出來的檔案的字首名,如果檔案沒有字首名則返回空的字串。

5. 新增字尾名函式,函式使用格式如下:

$(addsuffix <suffix>,<names>)

函式說明:函式的功能是把字尾 suffix 加到 names 中的每個單詞後面。返回值為新增上字尾的檔名序列。

6. 新增字首名函式,函式使用格式如下:

$(addperfix <prefix>,<names>)

函式說明:函式的功能是把字首 prefix加到 names中的每個單詞的前面。返回值為新增上字首的檔名序列。我們可以使用這個函式給我們的檔案新增路徑。

7. 連結函式,函式使用格式如下:

$(join <list1>,<list2>)

函式說明:函式功能是把 list2 中的單詞對應的拼接到 list1 的後面。如果 list1 的單詞要比 list2的多,那麼,list1中多出來的單詞將保持原樣,如果 list1 中的單詞要比 list2中的單詞少,那麼 list2 中多出來的單詞將保持原樣。返回值為拼接好的字串。

8. 獲取匹配模式檔名函式,命令使用格式如下:

$(wildcard PATTERN)

函式說明:函式的功能是列出當前目錄下所有符合模式的 PATTERN 格式的檔名。返回值為空格分隔並且存在當前目錄下的所有符合模式 PATTERN 的檔名。

14.Makefile中的其它常用函式

1、$(foreach <var>,<list>,<text>)

函式的功能是:把引數<list>中的單詞逐一取出放到引數<var>所指定的變數中,然後再執行<text>所包含的表示式。

每一次<text>會返回一個字串,迴圈過程中,<text>所返回的每個字串會以空格分割,最後當整個迴圈結束的時候,<text>所返回的每個字串所組成的整個字串(以空格分隔)將會是 foreach 函式的返回值。

所以<var>最好是一個變數名,<list>可以是一個表示式,而<text>中一般會只用<var>這個引數來一次列舉<list>中的單詞。

2、$(if <condition>,<then-part>)或(if<condition>,<then-part>,<else-part>)

3、$(call <expression>,<parm1>,<parm2>,<parm3>,...)

call 函式是唯一一個可以用來建立新的引數化的函式。我們可以用來寫一個非常複雜的表示式,這個表示式中,我們可以定義很多的引數,然後你可以用call函式來向這個表示式傳遞引數。

4、$(origin <variable>)

origin 函式不像其他的函式,它並不操作變數的值,它只是告訴你這個變數是哪裡來的。

15.Makefile命令的編寫

命令回顯

通常 make 在執行命令列之前 把要執行的命令輸出到標準輸出裝置,我們稱之為 "回顯",就好像我們在 shell 環境下輸入命令執行時一樣。

OBJ=test main list
all:
    echo $(OBJ)

執行make 或者make all,輸出:

如果規則的命令列以字元“@”開始,則 make 在執行的時候就不會顯示這個將要被執行的命令。典型的用法是在使用echo命令輸出一些資訊時。

OBJ=test main list
all:
    @echo $(OBJ)

執行時將會得到test main list這條輸出資訊。

命令的執行

當規則中的目標需要被重建的時候,此規則所定義的命令將會被執行,如果是多行的命令,那麼每一行命令將是在一個獨立的子 shell 程序中被執行。因此,多命令列之間的執行命令時是相互獨立的,相互之間不存在依賴

在 Makefile 中書寫在同一行中的多個命令屬於一個完整的 shell 命令列,書寫在獨立行的一條命令是一個獨立的 shell 命令列。

因此:

在一個規則的命令中命令列 “cd”改變目錄不會對其後面的命令的執行產生影響。就是說之後的命令執行的工作目錄不會是之前使用“cd”進入的那個目錄。

如果達到這個目的,就不能把“cd”和其後面的命令放在兩行來書寫。而應該把這兩個命令放在一行上用分號隔開。這樣才是一個完整的 shell 命令列。

foo:bar/lose
    cd bar;gobble lose >../foo

如果想把一個完整的shell命令列書寫在多行上,需要使用反斜槓 (\)來對處於多行的命令進行連線,表示他們是一個完整的shell命令列

16.Makefile include檔案包含

包含其他檔案使用的關鍵字是 "include",和 C 語言包含標頭檔案的方式相同。

當 make 讀取到 "include" 關鍵字的時候,會暫停讀取當前的 Makefile,而是去讀 "include" 包含的檔案,讀取結束後再繼讀取當前的 Makefile 檔案。"include" 使用的具體方式如下:

include <filenames>

filenames 是 shell 支援的檔名(可以使用萬用字元表示的檔案)。

include 通常使用在以下的場合:

  • 在一個工程檔案中,每一個模組都有一個獨立的 Makefile 來描述它的重建規則。它們需要定義一組通用的變數定義或者是模式規則。通用的做法是將這些共同使用的變數或者模式規則定義在一個檔案中,需要的時候用 "include" 包含這個檔案。
  • 當根據原始檔自動產生依賴檔案時,我們可以將自動產生的依賴關係儲存在另一個檔案中。然後在Makefile 中包含這個檔案。

17.Makefile巢狀執行make

在一個大的工程檔案中,不同的檔案按照功能被劃分到不同的模組中,也就說很多的原始檔被放置在了不同的目錄下。每個模組可能都會有自己的編譯順序和規則,如果在一個 Makefile 檔案中描述所有模組的編譯規則,就會很亂,執行時也會不方便,所以就需要在不同的模組中分別對它們的規則進行描述,也就是每一個模組都編寫一個 Makefile 檔案,這樣不僅方便管理,而且可以迅速發現模組中的問題。這樣我們只需要控制其他模組中的 Makefile 就可以實現總體的控制,這就是 make 的巢狀執行。

如何來使用呢?舉例說明如下:

subsystem:
    cd subdir && $(MAKE)

這個例子可以這樣來理解,在當前目錄下有一個目錄檔案 subdir 和一個 Makefile 檔案,子目錄 subdir 檔案下還有一個 Makefile 檔案,這個檔案是用來描述這個子目錄檔案的編譯規則。使用時只需要在最外層的目錄中執行 make 命令,當命令執行到上述的規則時,程式會進入到子目錄中執行 make。這就是巢狀執行 make,我們把最外層的 Makefile 稱為是總控 Makefile。

export的使用

使用 make 巢狀執行的時候,變數是否傳遞也是我們需要注意的。如果需要變數的傳遞,那麼可以這樣來使用:

export <variable>

如果不需要那麼可以這樣來寫:

unexport <variable>

<variable>是變數的名字,不需要使用 "$" 這個字元。如果所有的變數都需要傳遞,那麼只需要使用 "export" 就可以,不需要新增變數的名字。

18.巢狀執行make的案例

一個 MP3 player 的應用程式

19.make命令引數和選項大彙總

20.Makefile目標型別大彙總

規則中的目標形式是多種多樣的,它可以是一個或多個的檔案、可以是一個偽目標,這是我們之前講到過的,也是經常使用的。其實規則目標還可以是其他的型別,下面是對這些型別的詳細的說明。

強制目標

空目標檔案

特殊的目標

多規則目標

21.Makefile變數的高階用法

高階使用方法有兩種:一種是變數的替換引用,一種是變數的巢狀引用。

1、變數的替換引用

我們定義變數的目的是為了簡化我們的書寫格式,代替我們在程式碼中頻繁出現且冗雜的部分。它可以出現在我們規則的目標中,也可以是我們規則的依賴中。

我們使用的時候會經常的對它的值(表示的字串)進行操作。遇到這樣的問題我們可能會想到我們的字串操作函式,比如 "patsubst" 就是我們經常使用的。

但是我們使用變數同樣可以解決這樣的問題,我們通過下面的例子來具體的分析一下。

例項:

foo:=a.c b.c d.c
obj:=$(foo:%.c=%.o)
All:
    @echo $(obj)

這段程式碼實現的功能是字串的字尾名的替換,把變數 foo 中所有的以 .c 結尾的字串全部替換成 .o 結尾的字串。我們在 Makefile 中這樣寫,然後再 shell 命令列執行 make 命令,就可以看到打印出來的是 "a.o b.o d.o" ,實現了檔名字尾的替換。

表示式中使用了 "%" 這個字元,這個字元的含義就是自動匹配一個或多個字元。在開發的過程中,我們通常會使用這種方式來進行變數替換引用的操作。

2、變數的巢狀使用

變數的巢狀引用的具體含義是這樣的,我們可以在一個變數的賦值中引用其他的變數,並且引用變數的數量和和次數是不限制的。下面我們通過例項來說明一下。

例項:

foo:=test
var:=$(foo)
All:
    @echo $(var)

這種用法是最常見的使用方法,打印出 var 的值就是 test。我們可以認為是一層的巢狀引用。

22.Makefile控制函式error和warning

Makefile 中提供了兩個控制 make 執行方式的函式。其作用是當 make 執行過程中檢測到某些錯誤時為使用者提供訊息,並且可以控制 make 執行過程是否繼續。這兩個函式是 "error" 和 "warning",我們來詳細的介紹一下這兩個函式。

$(error TEXT...)

函式說明如下:

  • 函式功能:產生致命錯誤,並提示 "TEXT..." 資訊給使用者,並退出 make 的執行。需要說明的是:"error" 函式是在函式展開時(函式被呼叫時)才提示資訊並結束 make 程序。因此如果函數出現在命令中或者一個遞迴的變數定義時,讀取 Makefile 時不會出現錯誤。而只有包含 "error" 函式引用的命令被執行,或者定義中引用此函式的遞迴變數被展開時,才會提示知名資訊 "TEXT..." 同時退出 make。
  • 返回值:空
  • 函式說明:"error" 函式一般不出現在直接展開式的變數定義中,否則在 make 讀取 Makefile 時將會提示致命錯誤。

例項:

ERROR1=1234
all:
    ifdef ERROR1
    $(error error is $(ERROR1))
    endif     

make 讀取解析 Makefile 時,如果所起的變數名是已經定義好的"ERROR1",make 將會提示致命錯誤資訊 "error is 1234" 並儲存退出。

$(warning TEXT...)

函式說明如下:

  • 函式功能:函式 "warning" 類似於函式 "error" ,區別在於它不會導致致命錯誤(make不退出),而只是提示 "TEXT...",make 的執行過程繼續。
  • 返回值:空
  • 函式說明:用法和 "error" 類似,展開過程相同。

23.Makefile中常見的錯誤資訊

make 執行過程中所產生錯誤並不都是致命的,特別是在命令列之前存在 "-"、或者 make 使用 "-k" 選項執行時。

make 執行過程的致命錯誤都帶有字首字串 "***"。錯誤資訊都有字首,一種是執行程式名作為錯誤字首(通常是 "make");另外一種是當 Makefile 本身存在語法錯誤無法被 make 解析並執行時,字首包含了 Makefile 檔名和出現錯誤的行號。

Makefile中常見的錯誤資訊

Make

Make 命令教程

程式碼變成可執行檔案,叫做編譯(compile);先編譯這個,還是先編譯那個(即編譯的安排),叫做構建(build)。

Make是最常用的構建工具,誕生於1977年,主要用於C語言的專案。但是實際上 ,任何只要某個檔案有變化,就要重新構建的專案,都可以用Make構建。

make只是一個根據指定的Shell命令進行構建的工具。它的規則很簡單,你規定要構建哪個檔案、它依賴哪些原始檔,當那些檔案有變動時,如何重新構建它。