Linux之make的用法講解
一個工程中的原始檔不計數,其按型別、功能、模組分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些檔案需要先編譯,哪些檔案需要後編譯,哪些檔案需要重新編譯,甚至於進行更復雜的功能操作,因為makefile就像一個Shell指令碼一樣,其中也可以執行作業系統的命令。
makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟體開發的效率。make是一個命令工具,是一個解釋makefile中指令的命令工具,一般來說,大多數的IDE都有這個命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成為了一種在工程方面的編譯方法。下面讓我們一起來看看如何使用make工程管理器和makefile檔案。
在 Linux環境下使用 GNU 的 make工具能夠比較容易的構建一個屬於你自己的工程,整個工程的編譯只需要一個命令就可以完成編譯、連線以至於最後的執行。不過這需要我們投入一些時間去完成一個或者多個稱之為 Makefile 檔案的編寫。此檔案正是 make 正常工作的基礎。
make 是一個命令工具,它解釋 Makefile 中的指令(應該說是規則)。在 Makefile檔案中描述了整個工程所有檔案的編譯順序、編譯規則。
準備知識:編譯,連結,靜態庫,共享庫
編譯:把高階語言所書寫的程式碼轉換成機器可識別的指令,此時還不能夠被執行,編譯器通過檢查高階語言的語法,函式和變數的宣告是否正確!如果正確則產生中間目標檔案(目標檔案在Liunx中預設字尾為“.o”)
連結:將多.o 檔案,或者.o 檔案和庫檔案連結成為可被作業系統執行的可執行程式
靜態庫:又稱為文件檔案(Archive File) 。它是多個.o檔案的集合。Linux中靜態庫檔案的字尾為“.a”
共享庫:也是多個.o 檔案的集合,但是這些.o 檔案時有編譯器按照一種特殊的方式生成(共享庫已經具備了可執行條件)
在執行 make 之前,需要一個命名為 Makefile 的特殊檔案(本文的後續將使用Makefile 作為這個特殊檔案的檔名)來告訴 make 需要做什麼(完成什麼任務),該怎麼做。
當使用make 工具進行編譯時,工程中以下幾種檔案在執行make 時將會被編譯(重新編譯):
1.所有的原始檔沒有被編譯過,則對各個 C 原始檔進行編譯並進行連結,生成最後的可執行程式;
2.每一個在上次執行 make 之後修改過的 C 原始碼檔案在本次執行make 時將會被重新編譯;
3.標頭檔案在上一次執行make 之後被修改。則所有包含此標頭檔案的 C 原始檔在本次執make 時將會被重新編譯。
Makefile規則介紹
一個簡單的 Makefile 描述規則組成:
TARGET... : PREREQUISITES...
COMMAND
...
...
target:規則的目標。通常是最後需要生成的檔名或者為了實現這個目的而必需的中間過程檔名。可以是.o檔案、也可以是最後的可執行程式的檔名等。另外,目標也可以是一個make執行的動作的名稱,如目標“clean”(目標“clean”不是一個檔案,它僅僅代表執行一個動作的標識。),我們稱這樣的目標是“偽目標”。
prerequisites:規則的依賴。生成規則目標所需要的檔名列表。通常一個目標依賴於一個或者多個檔案。
command:規則的命令列。是規則所要執行的動作(任意的shell 命令或者是可在shell 下執行的程式)。它限定了make 執行這條規則時所需要的動作。
一個規則可以有多個命令列,每一條命令佔一行。注意:每一個命令列必須以[Tab] 字元開始,[Tab]字元告訴 make 此行是一個命令列。make 按照命令完成相應的動作。
這也是書寫 Makefile 中容易產生,而且比較隱蔽的錯誤。
命令就是在任何一個目標的依賴檔案發生變化後重建目標的動作描述。一個目標可以沒有依賴而只有動作(指定的命令)。比如Makefile 中的目標“clean”,此目標沒有依賴,只有命令。它所定義的命令用來刪除 make 過程產生的中間檔案(進行清理工作)。
在 Makefile 中“規則”就是描述在什麼情況下、如何重建規則的目標檔案,通常規則中包括了目標的依賴關係(目標的依賴檔案)和重建目標的命令。make 執行重建目標的命令,來建立或者重建規則的目標(此目標檔案也可以是觸發這個規則的上一個規則中的依賴檔案)。規則包含了檔案之間的依賴關係和更新此規則目標所需要的命令。
一個 Makefile 檔案中通常還包含了除規則以外的很多東西(後續我們會一步一步的展開)。一個最簡單的Makefile 可能只包含規則。規則在有些 Makefile 中可能看起來非常複雜,但是無論規則的書寫是多麼的複雜,它都符合規則的基本格式。
make 程式根據規則的依賴關係,決定是否執行規則所定義的命令的過程我們稱之為執行規則。
簡單的示例
一個簡單的Makefile,來描述如何建立最終的可執行檔案“edit”,此可執行檔案依賴於8個C原始檔和3個頭檔案。Makefile檔案的內容如下:
#sample Makefile
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
首先書寫時,可以將一個較長行使用反斜線(\ )來分解為多行。但需要注意:反斜線之後不能有空格(這也是大 家最容易犯的錯誤,錯誤比較隱蔽)。
在完成了這個Maekfile以後;需要建立可執行程式“edit”,所要做的就是在包含此Makefile的目錄(當然也在程式碼所在的目錄)下輸入命令“make”。刪除已經此目錄下之前使用“make”生成的檔案(包括那些中間過程的.o檔案),也只需要輸入命令“make clean”就可以了。
make如何工作
預設的情況下,make執行的是Makefile中的第一個規則,此規則的第一個目標稱之為“最終目的”或者“終極目標”(就是一個Makefile最終需要更新或者建立的目標)。
上例的 Makefile,目標“edit”在 Makefile 中是第一個目標,因此它就是make 的
“終極目標”。當修改了任何C 原始檔或者標頭檔案後,執行 make 將會重建終極目標
“edit”。
當在shell 提示符下輸入“make”命令以後。make 讀取當前目錄下的 Makefile 檔案,並將 Makefile 檔案中的第一個目標作為其執行的“終極目標”,開始處理第一個規則(終極目標所在的規則)。在上例中,第一個規則就是目標“edit”所在的規則。規則描述了“edit”的依賴關係,並定義了連結.o 檔案生成目標“edit”的命令; make在執行這個規則所定義的命令之前,首先處理目標“edit”的所有的依賴檔案(例子中的那些.o 檔案)的更新規則(以這些.o 檔案為目標的規則)。對這些.o 檔案為目標的規則處理有下列三種情況:
1. 目標.o 檔案不存在,使用其描述規則建立它;
2. 目標.o 檔案存在,目標.o 檔案所依賴的.c 原始檔、.h 檔案中的任何一個比目標.o檔案“更新”(在上一次 make 之後被修改)。則根據規則重新編譯生成它;
3. 目標.o 檔案存在,目標.o 檔案比它的任何一個依賴檔案(的.c 原始檔、.h 檔案) “更新”(它的依賴檔案在上一次make 之後沒有被修改),則什麼也不做。
這些.o 檔案所在的規則之所以會被執行,是因為這些.o 檔案出現在“終極目標”的依賴列表中。在 Makefile 中一個規則的目標如果不是“終極目標”所依賴的(或者“終極目標”的依賴檔案所依賴的),那麼這個規則將不會被執行,除非明確指定執行這個規則(可以通過 make 的命令列指定重建目標,那麼這個目標所在的規則就會被執行,例如 “make clean”)。在編譯或者重新編譯生成一個.o 檔案時,make 同樣會去尋找它的依賴檔案的重建規則(是這樣一個規則:這個依賴檔案在規則中作為目標出現),在這裡就是.c 和.h 檔案的重建規則。在上例的 Makefile 中沒有哪個規則的目標是.c或者.h 檔案,所以沒有重建.c 和.h 檔案的規則
完成了對.o 檔案的建立(第一次編譯)或者更新之後,make 程式將處理終極目標“edit”所在的規則,分為以下三種情況:
1. 目標檔案“edit”不存在,則執行規則以建立目標“edit”。
2. 目標檔案“edit”存在,其依賴檔案中有一個或者多個檔案比它“更新”,則根據規則重新連結生成“edit”。
3. 目標檔案“edit”存在,它比它的任何一個依賴檔案都“更新”,則什麼也不做。
上例中,如果更改了原始檔“insert.c”後執行make,“insert.o”將被更新,之後終極目標“edit”將會被重生成;如果我們修改了標頭檔案“command.h”之後執行“make”,那麼“kbd.o”、“command.o”和“files.o”將會被重新編譯,之後同樣終極目標“edit”也將被重新生成。
指定變數
“objects”作為一個變數,它代表所有的.o檔案的列表。在定義了此變數後,我們就可以在需要使用這些.o檔案列表的地方使用“$(objects)”來表示��,而不需要羅列所有的.o檔案列表。
make如何解析makefile檔案
GUN make 的執行過程分為兩個階段。
第一階段:讀取所有的 makefile 檔案(包括“MAKIFILES”變數指定的、指示符“include”指定的、以及命令列選項“-f(--file)”指定的 makefile 檔案),內建所有的變數、明確規則和隱含規則,並建立所有目標和依賴之間的依賴關係結構連結串列。
第二階段:根據第一階段已經建立的依賴關係結構連結串列決定哪些目標需要更新,並使用對應的規則來重建這些目標。
總結
make 的執行過程如下:
1.依次讀取變數“MAKEFILES”定義的 makefile 檔案列表
2.讀取工作目錄下的 makefile檔案(根據命名的查詢順序“GNUmakefile”, “makefile”,“Makefile”,首先找到那個就讀取那個)
3.依次讀取工作目錄 makefile 檔案中使用指示符“include”包含的檔案
4.查詢重建所有已讀取的 makefile 檔案的規則(如果存在一個目標是當前讀取的 某一個makefile 檔案,則執行此規則重建此 makefile 檔案,完成以後從第一步開始重新執行)
5.初始化變數值並展開那些需要立即展開的變數和函式並根據預設條件確定執行分支
6.根據“終極目標”以及其他目標的依賴關係建立依賴關係連結串列
7.執行除“終極目標”以外的所有的目標的規則(規則中如果依賴檔案中任一個 檔案的時間戳比目標檔案新,則使用規則所定義的命令重建目標檔案)
8.執行“終極目標”所在的規則
說明:
執行一個規則的過程是這樣的:
對於一個存在的規則(明確規則和隱含規則)首先,make程式將比較目標檔案和所有的依賴檔案的時間戳。如果目標的時間戳比所有依賴檔案的時間戳更新(依賴檔案在上一次執行make之後沒有被修改),那麼什麼也不做。否則(依賴檔案中的某一個或者全部在上一次執行make後已經被修改過),規則所定義的重建目標的命令將會被執行。這就是make工作的基礎,也是其執行規制所定 義命令的依據。