Linux Make的使用以及命令安裝詳解

  對(duì)于GNU Make或許很多Windows開(kāi)發(fā)的程序員并不是很了解,因?yàn)閃indows中的很多集成開(kāi)發(fā)環(huán)境(IDE)都幫我們做了這件事。但是作為一個(gè)專業(yè)從事Linux嵌入式開(kāi)發(fā)的程序員就必須要了解GNU Make,會(huì)不會(huì)使用GNU Make從一定角度上反應(yīng)了一個(gè)人是否具備大型工程能力。本文主要圍繞Make命令展開(kāi),介紹Linux下Make的使用以及Makefile的語(yǔ)法和使用Make進(jìn)行源碼安裝。

  一、什么是GNU Make

  GNU Make是一個(gè)控制從程序的源文件中生成程序的可執(zhí)行文件和其他非源文件的工具。

  Make可以從一個(gè)名為Makefile的文件中獲得如何構(gòu)建程序的知識(shí),該文件列出了每個(gè)非源文件以及如何從其他文件計(jì)算它。當(dāng)你編寫(xiě)一個(gè)程序時(shí),你應(yīng)該為它編寫(xiě)一個(gè)Makefile文件,這樣就可以使用Make來(lái)編譯和安裝這個(gè)程序。

  二、如何獲取Make

  Make可以在GNU的主要FTP服務(wù)器上找到:http : //ftp.gnu.org/gnu/make/ (通過(guò)HTTP)和 ftp://ftp.gnu.org/gnu/make/ (通過(guò)FTP)。它也可以在GNU鏡像列表上找到; 請(qǐng)盡可能GNU的鏡像列表。

  三、為什么需要Make

  任何一種技能或知識(shí)都是源之于某種社會(huì)需求,那為什么要用Make呢?當(dāng)項(xiàng)目源文件很少的時(shí)候,我們也許還可以手動(dòng)使用gcc命令來(lái)進(jìn)行編譯,但是當(dāng)項(xiàng)目發(fā)展到一個(gè)龐大的規(guī)模時(shí),再手動(dòng)敲gcc命令去編譯就變得不可能的事情。所以呢,在這樣的歷史背景下,就出現(xiàn)了一位大牛(斯圖亞特·費(fèi)爾德曼),在1977年貝爾實(shí)驗(yàn)室制作了這樣一個(gè)軟件,它的名字就叫做Make。所以實(shí)際開(kāi)發(fā)中,我們?cè)诰幾g大型項(xiàng)目的時(shí)候往往會(huì)使用Make進(jìn)行編譯,為此我們還需要了解Make軟件所依賴的Makefile規(guī)則文件。

  四、Makefile

  Makefile 文件需要按照某種語(yǔ)法進(jìn)行編寫(xiě),文件中需要說(shuō)明如何編譯各個(gè)源文件并連接生成可執(zhí)行文件,并要求定義源文件之間的依賴關(guān)系。Makefile的語(yǔ)法還是略微有些復(fù)雜,因篇幅有限,本文只能簡(jiǎn)述Makefile的編寫(xiě)原則。

  (1)Makefile的組成部分

  Makefile包含五個(gè)東西:顯示規(guī)則,隱式規(guī)則,變量定義,文件指示,注釋。

  <1>顯式規(guī)則,顯式規(guī)則說(shuō)明了,如何生成一個(gè)或多的的目標(biāo)文件。這是由Makefile的書(shū)寫(xiě)者明顯指出,要生成的文件,文件的依賴文件,生成的命令。

  <2>隱式規(guī)則,由于我們的make有自動(dòng)推導(dǎo)的功能,所以隱晦的規(guī)則可以讓我們比較粗糙地簡(jiǎn)略地書(shū)寫(xiě)Makefile,這是由make所支持的。

  <3>變量的定義,在Makefile中我們要定義一系列的變量,變量一般都是字符串,這個(gè)有點(diǎn)你C語(yǔ)言中的宏,當(dāng)Makefile被執(zhí)行時(shí),其中的變量都會(huì)被擴(kuò)展到相應(yīng)的引用位置上。

  <4>文件指示,其包括了三個(gè)部分,一個(gè)是在一個(gè)Makefile中引用另一個(gè)Makefile,就像C語(yǔ)言中的include一樣;另一個(gè)是指根據(jù)某些情況指定Makefile中的有效部分,就像C語(yǔ)言中的預(yù)編譯#if一樣;還有就是定義一個(gè)多行的命令。有關(guān)這一部分的內(nèi)容,我會(huì)在后續(xù)的部分中講述。

  <5>注釋,Makefile中只有行注釋,和UNIX的Shell腳本一樣,其注釋是用“#”字符,這個(gè)就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字符,可以用反斜框進(jìn)行轉(zhuǎn)義,如:“/#”。

  (2) Makefile的規(guī)則

  我們先來(lái)粗略地看一看Makefile的規(guī)則。

  target... : prerequisites ...

  command

  ...

  ...

  -------------------------------------------------------------------------------

  target也就是一個(gè)目標(biāo)文件,可以是Object File,也可以是執(zhí)行文件。還可以是一個(gè)標(biāo)簽(Label),對(duì)于標(biāo)簽這種特性,在后續(xù)的“偽目標(biāo)”章節(jié)中會(huì)有敘述。

  prerequisites就是,要生成那個(gè)target所需要的文件或是目標(biāo)。

  command也就是make需要執(zhí)行的命令。(一定要以Tab鍵作為開(kāi)頭)

  這是一個(gè)文件的依賴關(guān)系,也就是說(shuō),target這一個(gè)或多個(gè)的目標(biāo)文件依賴于prerequisites中的文件,其生成規(guī)則定義在command中。說(shuō)白一點(diǎn)就是說(shuō),prerequisites中如果有一個(gè)以上的文件比target文件要新的話,command所定義的命令就會(huì)被執(zhí)行。這就是Makefile的規(guī)則。也就是Makefile中最核心的內(nèi)容。

  (3)Makefile之模式規(guī)則

  模式規(guī)則其實(shí)也是普通規(guī)則,但它使用了如%這樣的通配符。如下面的例子:

  此規(guī)則描述了一個(gè).o文件如何由對(duì)應(yīng)的.c文件創(chuàng)建。規(guī)則的命令行中使用了自動(dòng)化變量“$<”和“$@”,其中自動(dòng)化變量“$<”代表規(guī)則的依賴,“$@”代表規(guī)則的目標(biāo)。此規(guī)則在執(zhí)行時(shí),命令行中的自動(dòng)化變量將根據(jù)實(shí)際的目標(biāo)和依賴文件取對(duì)應(yīng)值。

  其含義是,字指出了從所有的.c文件生成相應(yīng)的.o文件的規(guī)則。如果要生成的目標(biāo)是”a.o b.o”,那么 %.c”就是”a.c b.c”。

  在模式規(guī)則中,目標(biāo)的定義需要有“%”字符。“%”定義對(duì)文件名的匹配,表示任意長(zhǎng)度的非空字符串。在依賴目標(biāo)中同樣可以使用“%”,只是依賴目標(biāo)中“%”的取值,取決于其目標(biāo)。

  注意:模式規(guī)則中“%”的展開(kāi)和變量與函數(shù)的展開(kāi)是有區(qū)別的,“%”的展開(kāi)發(fā)生在變量和函數(shù)的展開(kāi)之后。變量和函數(shù)的展開(kāi)發(fā)生在make載入Makefile時(shí),而“%”的展開(kāi)則發(fā)生在運(yùn)行時(shí)。

  <1> 自動(dòng)化變量

  自動(dòng)化變量只應(yīng)出現(xiàn)在規(guī)則的命令中。

變量

含義

$@

表示規(guī)則中的所有目標(biāo)文件的集合。在模式規(guī)則中如果有多個(gè)目標(biāo),“$@”就是匹配于目標(biāo)中模式定義的集合

$%

僅當(dāng)目標(biāo)是函數(shù)庫(kù)文件時(shí),表示規(guī)則中的目標(biāo)成員名,如果目標(biāo)不是函數(shù)庫(kù)文件(UNIX下是.aWindows.lib),其值為空。

$<

依賴目標(biāo)中的第一個(gè)目標(biāo)名字,如果依賴目標(biāo)是以模式(即”%“)定義的,則”$<”是符合模式的一系列的文件集

$?

所有比目標(biāo)新的依賴目標(biāo)的集合,以空格分隔

$^

所有依賴目標(biāo)的集合,以空格分隔。如如果在依賴目標(biāo)中有多個(gè)重復(fù)的,則自動(dòng)去除重復(fù)的依賴目標(biāo),只保留一份

$+

同”$^”,也是所有依賴目標(biāo)的集合,只是它不去除重復(fù)的依賴目標(biāo)。

$*

目標(biāo)模式中“%”及其之前的部分

$(@D)

$@”的目錄部分(不以斜杠作為結(jié)尾),如果”$@”中沒(méi)有包含斜杠,其值為“.”(當(dāng)前目錄)

$(@F)

$@”的文件部分,相當(dāng)于函數(shù)”$(notdir $@)

$(*D)

同”$(@D)”,取文件的目錄部分

$(*F)

同”$(@F)”,取文件部分,但不取后綴名

$(%D)

函數(shù)包文件成員的目錄部分

$(%F)

函數(shù)包文件成員的文件名部分

$(

依賴目標(biāo)中的第一個(gè)目標(biāo)的目錄部分

$(

依賴目標(biāo)中的第一個(gè)目標(biāo)的文件名部分

$(^D)

所有依賴目標(biāo)文件中目錄部分(無(wú)相同的)

$(^F)

所有依賴目標(biāo)文件中文件名部分(無(wú)相同的)

$(+D)

所有依賴目標(biāo)文件中的目錄部分(可以有相同的)

$(+F)

所有依賴目標(biāo)文件中的文件名部分(可以有相同的)

$(?D)

所有被更新文件的目錄部分

$(?F)

所有被更新文件的文件名部分

  <2>$VAR和$$VAR的區(qū)別:

  makefile文件中的規(guī)則絕大部分都是使用shell命令來(lái)實(shí)現(xiàn)的,這里就涉及到了變量的使用,包括makefile中的變量和shell命令范疇內(nèi)的變量。在makefile的規(guī)則命令行中使用$var就是在命令中引用makefile的變量,這里僅僅是讀取makefile的變量然后擴(kuò)展開(kāi),將其值作為參數(shù)傳給了一個(gè)shell命令;而$$var是在訪問(wèn)一個(gè)shell命令內(nèi)定義的變量,而非makefile的變量。如果某規(guī)則有n個(gè)shell命令行構(gòu)成,而相互之間沒(méi)有用';'和'\'連接起來(lái)的話,就是相互之間沒(méi)有關(guān)聯(lián)的shell命令,相互之間也不能變量共享。

  (4)Makefile之偽目標(biāo)

  使用其原因一:避免和同名文件沖突

  在現(xiàn)實(shí)中難免存在所定義的目標(biāo)與所存在的目標(biāo)是同名的,采用Makefile如何處理這種情況呢?Makefile中的假目標(biāo)(phony target)可以解決這個(gè)問(wèn)題。

  假目標(biāo)可以使用.PHONY關(guān)鍵字進(jìn)行聲明,對(duì)于假目標(biāo),可以想象,因?yàn)椴灰蕾囉谀澄募琺ake該目標(biāo)的時(shí)候,其所在規(guī)則的命令都會(huì)被執(zhí)行。

  如果編寫(xiě)一個(gè)規(guī)則,并不產(chǎn)生目標(biāo)文件,則其命令在每次make 該目標(biāo)時(shí)都執(zhí)行。

  例如:

  clean:

  rm *.o temp

  因?yàn)?rm"命令并不產(chǎn)生"clean"文件,則每次執(zhí)行"make clean"的時(shí)候,該命令都會(huì)執(zhí)行。如果目錄中出現(xiàn)了"clean"文件,則規(guī)則失效了:沒(méi)有依賴文件,文件"clean"始終是最新的,命令永遠(yuǎn)不會(huì)執(zhí)行;為避免這個(gè)問(wèn)題,可使用".PHONY"指明該目標(biāo)。如:

  .PHONY : clean

  這樣執(zhí)行"make clean"會(huì)無(wú)視"clean"文件存在與否。

  已知phony 目標(biāo)并非是由其它文件生成的實(shí)際文件,make 會(huì)跳過(guò)隱含規(guī)則搜索。這就是聲明phony 目標(biāo)會(huì)改善性能的原因,即使你并不擔(dān)心實(shí)際文件存在與否。

  完整的例子如下:

  .PHONY : clean

  clean :

  rm *.o temp

  使用其原因二:提高執(zhí)行make的效率

  當(dāng)一個(gè)目標(biāo)被聲明為偽目標(biāo)后,make在執(zhí)行此規(guī)則時(shí)不會(huì)試圖去查找隱含規(guī)則來(lái)創(chuàng)建這個(gè)目標(biāo)。這樣也提高了make的執(zhí)行效率,同時(shí)我們也不用擔(dān)心由于目標(biāo)和文件名重名而使我們的期望失敗。

  (5)Makefile的賦值

  [=]和[:=]符號(hào)的區(qū)別。

  =

  可以先使用后定義,這就導(dǎo)致makefile在全部展開(kāi)后才能決定變量的值。

  有可能出現(xiàn)循環(huán)遞歸,無(wú)法暫開(kāi)的問(wèn)題。

  :=

  必須先定義然后再使用,在當(dāng)前的位置就可以決定變量的值。

  ?=

  相當(dāng)于選擇疑問(wèn)句,如果前面的變量沒(méi)被賦值,那就做賦值操作

  +=

  相當(dāng)于遞加操作

  (6)Makefile之執(zhí)行過(guò)程

  1. 依次讀取變量“MAKEFILES”定義的makefile文件列表

  2. 讀取工作目錄下的makefile文件(根據(jù)命名的查找順序“GNUmakefile”,“makefile”,“Makefile”,首先找到那個(gè)就讀取那個(gè))

  3. 依次讀取工作目錄makefile文件中使用指示符“include”包含的文件

  4. 查找重建所有已讀取的makefile文件的規(guī)則(如果存在一個(gè)目標(biāo)是當(dāng)前讀取的某一個(gè)makefile文件,則執(zhí)行此規(guī)則重建此makefile文件,完成以后從第一步開(kāi)始重新執(zhí)行)

  5. 初始化變量值并展開(kāi)那些需要立即展開(kāi)的變量和函數(shù)并根據(jù)預(yù)設(shè)條件確定執(zhí)行分支

  6. 根據(jù)“終極目標(biāo)”以及其他目標(biāo)的依賴關(guān)系建立依賴關(guān)系鏈表

  7. 執(zhí)行除“終極目標(biāo)”以外的所有的目標(biāo)的規(guī)則(規(guī)則中如果依賴文件中任一個(gè)文件的時(shí)間戳比目標(biāo)文件新,則使用規(guī)則所定義的命令重建目標(biāo)文件)

  8. 執(zhí)行“終極目標(biāo)”所在的規(guī)則

  五、使用Make進(jìn)行源碼安裝

  (1)正常的編譯安裝/卸載:

  源碼的安裝一般由3個(gè)步驟組成:配置(configure)、編譯(make)、安裝(make install)。

  configure文件是一個(gè)可執(zhí)行的腳本文件,它有很多選項(xiàng),在待安裝的源碼目錄下使用命令./configure –help可以輸出詳細(xì)的選項(xiàng)列表。

  其中--prefix選項(xiàng)是配置安裝目錄,如果不配置該選項(xiàng),安裝后可執(zhí)行文件默認(rèn)放在/usr /local/bin,庫(kù)文件默認(rèn)放在/usr/local/lib,配置文件默認(rèn)放在/usr/local/etc,其它的資源文件放在/usr /local/share,比較凌亂。

  如果配置了--prefix,如:

  $ ./configure --prefix=/usr/local/test

  安裝后的所有資源文件都會(huì)被放在/usr/local/test目錄中,不會(huì)分散到其他目錄。

  使用--prefix選項(xiàng)的另一個(gè)好處是方便卸載軟件或移植軟件;當(dāng)某個(gè)安裝的軟件不再需要時(shí),只須簡(jiǎn)單的刪除該安裝目錄,就可以把軟件卸載得干干凈凈;而移植軟件只需拷貝整個(gè)目錄到另外一個(gè)機(jī)器即可(相同的操作系統(tǒng)下)。

  當(dāng)然要卸載程序,也可以在原來(lái)的make目錄下用一次make uninstall,但前提是Makefile文件有uninstall命令(nodejs的源碼包里有uninstall命令,測(cè)試版本v0.10.35)。

  (2)卸載:

  如果沒(méi)有配置--prefix選項(xiàng),源碼包也沒(méi)有提供make uninstall,則可以通過(guò)以下方式可以完整卸載:

  找一個(gè)臨時(shí)目錄重新安裝一遍,如:

  $ ./configure --prefix=/tmp/to_remove && make install

  然后遍歷/tmp/to_remove的文件,刪除對(duì)應(yīng)安裝位置的文件即可(因?yàn)?tmp/to_remove里的目錄結(jié)構(gòu)就是沒(méi)有配置--prefix選項(xiàng)時(shí)的目錄結(jié)構(gòu))。

  當(dāng)下載了源碼就可以按照此種方法,就可以進(jìn)行軟件的安裝和卸載。

  六、總結(jié)

  關(guān)于Makefile的用法,我們今天就討論到這里,對(duì)于一個(gè)Linux程序員來(lái)說(shuō)Makefile的作用和重要。對(duì)于程序的編譯以及程序員對(duì)項(xiàng)目的了解有很大的幫助。

the end

評(píng)論(0)