在 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工作的基礎,也是其執行規制所定 義命令的依據。
本文永久更新鏈接地址:
http://xxxxxx/Linuxjc/1141008.html TechArticle