Linux 应用---make及makefile的编写
Make 在我們做linux 開(kāi)發(fā)中是必不可少的一部分,它在我們編寫大型項(xiàng)目工程文件中起到非常大的作用。
???? Make工程管理器也就是個(gè)“自動(dòng)編譯管理器”,這里的“自動(dòng)”是指它能夠根據(jù)文件時(shí)間戳自動(dòng)發(fā)現(xiàn)更新過(guò)的文件而減少編譯的工作量,同時(shí),它通過(guò)讀入Makefile文件的內(nèi)容來(lái)執(zhí)行大量的編譯工作。Make將只編譯改動(dòng)的代碼文件,而不用完全編譯。
??? 而Makefile是Make讀入的唯一配置文件,makefile關(guān)系到了整個(gè)工程的編譯規(guī)則。一個(gè)工程中的源文件不計(jì)數(shù),其按類型、功能、模塊分別放在若干個(gè)目錄中,makefile定義了一系列的規(guī)則來(lái)指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作,因?yàn)閙akefile就像一個(gè)Shell腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令。
???下面我們通過(guò)兩個(gè)個(gè)實(shí)例來(lái)學(xué)習(xí)makefile的編寫:
一、Makefile編寫的基本規(guī)則
??? 我在文件夾下有如下文件
[cpp]?view plaincopy這里的代碼就不展示了,我說(shuō)一下文件包含關(guān)系:files.c需要buffer.h及defs.h ,? main.c需要defs.h? ,utils.c需要defs.h。
我把makefile文件內(nèi)容展示出來(lái):
[cpp]?view plaincopy我們開(kāi)始學(xué)習(xí)makefile編寫規(guī)范:Makefile的根本任務(wù)是根據(jù)規(guī)則生成目標(biāo)文件。
1、規(guī)則
一條規(guī)則包含三個(gè):目標(biāo)文件,目標(biāo)文件依賴的文件,更新(或生成)目標(biāo)文件的命令。
規(guī)則:???????????????
<目標(biāo)文件>:<依賴文件>
???????? <更新目標(biāo)的命令>
注意:命令行前面必須是一個(gè)“TAB鍵”,否則會(huì)編譯錯(cuò)誤!
Example??????
[cpp]?view plaincopy 目標(biāo)hello.o 依賴于hello.c,hello.h. 生成hello.o的命令時(shí)是“gcc -c hello.c -o hello.o”
 
2、終極目標(biāo)
 makefile并不會(huì)更新所有規(guī)則中的目標(biāo),它只會(huì)更新終極目標(biāo)以及終極目標(biāo)依賴的目標(biāo)。
 默認(rèn)情況下makefile的第一個(gè)目標(biāo)是終極目標(biāo),而且大家約定俗成的總是將all作為第一個(gè)目標(biāo)。環(huán)境變量MAKECMDGOALS記錄著終極目標(biāo)。
我們這里就是終極目標(biāo)
注意:終極目標(biāo)必須放在第一個(gè),其余的可以隨便放!
?
3、多規(guī)則目標(biāo)
 Makefile中,一個(gè)文件可以作為多個(gè)規(guī)則的目標(biāo),這種情形就是多規(guī)則目標(biāo)。
 多規(guī)則目標(biāo)下,以這個(gè)文件為目標(biāo)的所有規(guī)則的依賴文件將會(huì)被合并成此一個(gè)依賴文件列表,但是命令不會(huì)合并,而且實(shí)際上,這多個(gè)規(guī)則中至多只能有一個(gè)規(guī)則定義了更新命令。
 all:hello.o?????????????
 
 all:hello.h?????????????
 
等價(jià)于? all: hello.o hello.h
[cpp]?view plaincopy我們這里就是多規(guī)則目標(biāo);
?
 4、偽目標(biāo)
 一般情況下目標(biāo)文件是一個(gè)具體的文件,但有時(shí)候我們只需要一個(gè)標(biāo)簽,如目標(biāo)clean。
聲明偽目標(biāo):???
.PHONY:? <偽目標(biāo)>
偽目標(biāo)只是一個(gè)標(biāo)簽,這意味著偽目標(biāo)的時(shí)間戳總是最新的,結(jié)果就是makefile每次都會(huì)去執(zhí)行更新偽目標(biāo)的命令。
[cpp]?view plaincopy 我們這里clean就是個(gè)偽目標(biāo),我們可以看到偽目標(biāo)是沒(méi)有依賴文件的,只有用make來(lái)調(diào)用時(shí),才會(huì)執(zhí)行。
 
5、什么時(shí)候更新目標(biāo)
如果目標(biāo)不存在或者依賴文件中至少有一個(gè)文件的時(shí)間戳比目標(biāo)新,則執(zhí)行目標(biāo)更新命令。
 我們這里,如果main.c 或者files被修改時(shí),都會(huì)更新目標(biāo)。
 
6、創(chuàng)建和使用變量
1)定義變量
makefile的變量定義有四種方法:
 1. 立即賦值 a:=b
 2. 延遲賦值 a=b
 3. 條件賦值 a?=b
 4. 附加賦值 a+=b
它們之間的區(qū)別是:
 第一種方式,會(huì)立即計(jì)算b的值,并賦值給a;
 第二種方式,相當(dāng)于C++和Java的引用。如果后面b的值改變了,那么a的值也會(huì)改變;
 第三種方式,如果a沒(méi)有定義,則相當(dāng)于a=b ,否則不執(zhí)行任何操作;
 第四種方式,將b的值添加到a原有的值后面,再賦值給a。
2)獲取變量值
$(var) //表示取變量var的值,記得當(dāng)變量名多于一個(gè)字符時(shí),使用”()”.
[cpp]?view plaincopy這里我們可以看到變量的創(chuàng)建與使用。
3)預(yù)定義變量
? CC? :C編譯器的名稱,默認(rèn)值為cc 。CPP? C預(yù)編譯器的名稱,默認(rèn)值為$(CC) -E。
[cpp]?view plaincopy這里我們可以看到預(yù)定義變量CC的使用。
其他的預(yù)定義變量:
ARFLAGS??庫(kù)文件維護(hù)程序的選項(xiàng),無(wú)默認(rèn)值。
ASFALGS? 匯編程序的選項(xiàng),無(wú)默認(rèn)值。
 CFALGS??? C編譯器的選項(xiàng),無(wú)默認(rèn)值。
 ....
這里我們可以看到CFLAGS的使用。
4)自動(dòng)變量
$* 不包含擴(kuò)展名的目標(biāo)文件名稱
$< 第一個(gè)依賴文件的名稱
$@ 目標(biāo)文件的完整名稱
$^ 所有不重復(fù)的目標(biāo)依賴文件,以空格分開(kāi)
...
??? 變量的使用有助于我們修改makefile,大大加快了我們的開(kāi)發(fā)效率。
?
7、回顯問(wèn)題
我們知道,makefile中的命令行都會(huì)被打印出來(lái)的,如果遇到我們不想打印的命令怎么辦?
[cpp]?view plaincopy在命令行前面加 @ ,就是不回顯的意思,這樣"echo "Clean done!" "就不會(huì)打印。
?
我們來(lái)看看編譯效果:
[cpp]?view plaincopy 
 二、嵌套執(zhí)行Makefile
???? 在一些大的工程中,我們會(huì)把我們不同模塊或是不同功能的源文件放在不同的目錄中,我們可以在每個(gè)目錄中都書(shū)寫一個(gè)該目錄的Makefile,這有利于讓我們的Makefile變得更加地簡(jiǎn)潔,而不至于把所有的東西全部寫在一個(gè)Makefile中,這樣會(huì)很難維護(hù)我們的Makefile,這個(gè)技術(shù)對(duì)于我們模塊編譯和分段編譯有著非常大的好處。
例如,我們有一個(gè)子目錄叫subdir,這個(gè)目錄下有個(gè)Makefile文件,來(lái)指明了這個(gè)目錄下文件的編譯規(guī)則。那么我們總控的Makefile可以這樣書(shū)寫:
 ??? subsystem:?
 ??????????? cd subdir && $(MAKE)
其等價(jià)于:
 ??? subsystem:?
 ??????????? $(MAKE) -C subdir
定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數(shù),所以定義成一個(gè)變量比較利于維護(hù)。這兩個(gè)例子的意思都是先進(jìn)入“subdir”目錄,然后執(zhí)行make命令。
我們把這個(gè)Makefile叫做“總控Makefile”,總控Makefile的變量可以傳遞到下級(jí)的Makefile中(如果你顯示的聲明),但是不會(huì)覆蓋下層的Makefile中所定義的變量,除非指定了“-e”參數(shù)。
如果你要傳遞變量到下級(jí)Makefile中,那么你可以使用這樣的聲明:
??? export ;
如果你不想讓某些變量傳遞到下級(jí)Makefile中,那么你可以這樣聲明:?
??? unexport ;
 如:?
 ?????
 ??? 示例一:
??????? export variable = value
??????? 其等價(jià)于:
 ??????? variable = value?
 ??????? export variable
??????? 其等價(jià)于:
??????? export variable := value
??????? 其等價(jià)于:
 ??????? variable := value?
 ??????? export variable
??? 示例二:
??????? export variable += value
??????? 其等價(jià)于:
 ??????? variable += value?
 ??????? export variable
如果你要傳遞所有的變量,那么,只要一個(gè)export就行了。后面什么也不用跟,表示傳遞所有的變量。
需要注意的是,有兩個(gè)變量,一個(gè)是SHELL,一個(gè)是MAKEFLAGS,這兩個(gè)變量不管你是否export,其總是要傳遞到下層Makefile中,特別是MAKEFILES變量,其中包含了make的參數(shù)信息,如果我們執(zhí)行“總控Makefile”時(shí)有make參數(shù)或是在上層Makefile中定義了這個(gè)變量,那么MAKEFILES變量將會(huì)是這些參數(shù),并會(huì)傳遞到下層Makefile中,這是一個(gè)系統(tǒng)級(jí)的環(huán)境變量。
但是make命令中的有幾個(gè)參數(shù)并不往下傳遞,它們是“-C”,“-f”,“-h”“-o”和“-W”(有關(guān)Makefile參數(shù)的細(xì)節(jié)將在后面說(shuō)明),如果你不想往下層傳遞參數(shù),那么,你可以這樣來(lái):
 ??? subsystem:?
 ??????????? cd subdir && $(MAKE) MAKEFLAGS=
如果你定義了環(huán)境變量MAKEFLAGS,那么你得確信其中的選項(xiàng)是大家都會(huì)用到的,如果其中有“-t”,“-n”,和“-q”參數(shù),那么將會(huì)有讓你意想不到的結(jié)果,或許會(huì)讓你異常地恐慌。
還有一個(gè)在“嵌套執(zhí)行”中比較有用的參數(shù),“-w”或是“--print-directory”會(huì)在make的過(guò)程中輸出一些信息,讓你看到目前的工作目錄。比如,如果我們的下級(jí)make目錄是“/home/hchen/gnu/make”,如果我們使用“make -w”來(lái)執(zhí)行,那么當(dāng)進(jìn)入該目錄時(shí),我們會(huì)看到:
??? make: Entering directory `/home/hchen/gnu/make'.
而在完成下層make后離開(kāi)目錄時(shí),我們會(huì)看到:
??? make: Leaving directory `/home/hchen/gnu/make'
當(dāng)你使用“-C”參數(shù)來(lái)指定make下層Makefile時(shí),“-w”會(huì)被自動(dòng)打開(kāi)的。如果參數(shù)中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”總是失效的。
 頭的特殊變量,我們會(huì)在后面介紹),make在執(zhí)行命令包時(shí),命令包中的每個(gè)命令會(huì)被依次獨(dú)立執(zhí)行。
 
下面我們做一個(gè)實(shí)驗(yàn),學(xué)習(xí)嵌套執(zhí)行Makefile編寫的過(guò)程:
1、創(chuàng)建頂層目錄
[cpp]?view plaincopy在include文件夾中創(chuàng)建一個(gè)共用頭文件,在其中輸入#include <stdio.h>
[cpp]?view plaincopy2、創(chuàng)建頂層Makefile文件
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy?3、進(jìn)入在f1目錄下創(chuàng)建makefile
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy?4、進(jìn)入f2目錄
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy?5、進(jìn)入main目錄
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy6、進(jìn)入obj目錄
[cpp]?view plaincopy內(nèi)容如下:
[cpp]?view plaincopy這樣我們總體架構(gòu)就完成了,我們看一下樹(shù)狀圖:
我們執(zhí)行一下:
[cpp]?view plaincopy makefile時(shí)常遇到這樣的問(wèn)題,匯總網(wǎng)上的原因如下:
 1. 上一行換行符號(hào) \ 后面有空格
 2. 本行前面的空白有非法字符
 1)Makefile可能是以命令行開(kāi)始:以[Tab]字符開(kāi)始,但不是一個(gè)合法的命令行(例如,一個(gè)變量的賦值)。命令行必須和規(guī)則一一對(duì)應(yīng)。
 2)第二種原因可能是一行的第一個(gè)非空字符為分號(hào),make會(huì)認(rèn)為此處遺漏了規(guī)則的“target: prerequisite”部分。
這兒我們的原因是第一個(gè)
改正后編譯:
[cpp]?view plaincopy執(zhí)行一下:
[cpp]?view plaincopy總結(jié)
以上是生活随笔為你收集整理的Linux 应用---make及makefile的编写的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: Django学习笔记(4)
- 下一篇: FGUI分页
