Makefile札记
Makefile中:= ?= += =的區(qū)別
在Makefile中我們經(jīng)常看到 = := ?= +=這幾個(gè)賦值運(yùn)算符,那么他們有什么區(qū)別呢?我們來(lái)做個(gè)簡(jiǎn)單的實(shí)驗(yàn)
新建一個(gè)Makefile,內(nèi)容為:
ifdef DEFINE_VRE
??? VRE = “Hello World!”
else
endif
ifeq ($(OPT),define)
??? VRE ?= “Hello World! First!”
endif
ifeq ($(OPT),add)
??? VRE += “Kelly!”
endif
ifeq ($(OPT),recover)
??? VRE := “Hello World! Again!”
endif
all:
??? @echo $(VRE)
敲入以下make命令:
make DEFINE_VRE=true OPT=define 輸出:Hello World!
make DEFINE_VRE=true OPT=add 輸出:Hello World! Kelly!
make DEFINE_VRE=true OPT=recover? 輸出:Hello World! Again!
make DEFINE_VRE= OPT=define?輸出:Hello World! First!
make DEFINE_VRE= OPT=add 輸出:Kelly!
make DEFINE_VRE= OPT=recover 輸出:Hello World! Again!
從上面的結(jié)果中我們可以清楚的看到他們的區(qū)別了
= 是最基本的賦值
:= 是覆蓋之前的值
?= 是如果沒(méi)有被賦值過(guò)就賦予等號(hào)后面的值
+= 是添加等號(hào)后面的值
之前一直糾結(jié)makefile中“=”和“:=”的區(qū)別到底有什么區(qū)別,因?yàn)榻o變量賦值時(shí),兩個(gè)符號(hào)都在使用。網(wǎng)上搜了一下,有人給出了解答,但是本人愚鈍,看不懂什么意思。幾尋無(wú)果之下,也就放下了。今天看一篇博客,無(wú)意中發(fā)現(xiàn)作者對(duì)于這個(gè)問(wèn)題做了很好的解答。解決問(wèn)題之余不免感嘆,有時(shí)候給個(gè)例子不就清楚了嗎?為什么非要說(shuō)得那么學(xué)術(shù)呢。^_^
????? 1、“=”
????? make會(huì)將整個(gè)makefile展開(kāi)后,再?zèng)Q定變量的值。也就是說(shuō),變量的值將會(huì)是整個(gè)makefile中最后被指定的值。看例子:
????????????x = foo
??????????? y = $(x) bar
??????????? x = xyz
??????在上例中,y的值將會(huì)是?xyz bar?,而不是?foo bar?。
????? 2、“:=”
????? “:=”表示變量的值決定于它在makefile中的位置,而不是整個(gè)makefile展開(kāi)后的最終值。
????????????x := foo
??????????? y := $(x) bar
??????????? x := xyz
????? 在上例中,y的值將會(huì)是?foo bar?,而不是?xyz bar?了。
makefile宏定義:EXTRA_CFLAGS += -D與CONFIG_ =y
EXTRA_CFLAGS += -D 與CONFIG_ =y
1.
假如定義一個(gè)宏CONFIG_DEBUG
在.c里面定義為:#define?CONFIG_DEBUG
在makefile里定義為: CONFIG_DEBUG=y
假如說(shuō)我們想在makefile里為.c文件進(jìn)入一個(gè)宏定義,就用EXTRA_CFLAGS += DCONFIG_DEBUG(等價(jià)于在.c文件里定義#define?CONFIG_DEBUG)
這時(shí)CONFIG_DEBUG=y與EXTRA_CFLAGS += DCONFIG_DEBUG的區(qū)別應(yīng)該你已經(jīng)看出來(lái)的,前者是對(duì)makefile編譯時(shí)用的,比如說(shuō)obj-(CONFIG_DEBUG) += test.o,而后者則是對(duì).c源文件里的用的,比如說(shuō):
#if defined(CONFIG_DEBUG)?
...
#else
...
#endif
2.
假如定義一個(gè)宏CONFIG_DEBUG = 3
在.c里面定義為:#define?CONFIG_DEBUG 3
假如說(shuō)我們想在makefile里為.c文件進(jìn)入一個(gè)宏定義,就用EXTRA_CFLAGS += -DCONFIG_DEBUG=3
此時(shí)兩者的定義完全相同。
Makefile中通配符*與%的區(qū)別是什么?
此兩者均為通配符,但更準(zhǔn)確的講,%為Makefile規(guī)則通配符,一般用于規(guī)則描述,如
%.o:%c
? ? $(CC)? $< -o $@
表示所有的目標(biāo)文件及其依賴文件,或者
$(filter %.c ,SOURCES)
此處SOURCES表示包含.c .cc .cpp等多類型源文件,該過(guò)濾器函數(shù)將c文件過(guò)濾出來(lái),而%.c即為此過(guò)濾器規(guī)則。
通配符*則不具備上述功能。尤其是在Makefile,當(dāng)變量定義或者函數(shù)調(diào)用時(shí),該通配符的展開(kāi)功能就失效了,即不能正常使用了,此時(shí)需要借助wildcard函數(shù)。二者應(yīng)用范圍不同。
Makefile有三個(gè)非常有用的變量。分別是$@,$^,$<代表的意義分別是:
# 這是上面那個(gè)程序的Makefile文件:?
main:main.o?mytool1.o?mytool2.o?
gcc?-o?main?main.o?mytool1.o?mytool2.o?
main.o:main.c?mytool1.h?mytool2.h?
gcc?-c?main.c?
mytool1.o:mytool1.c?mytool1.h?
gcc?-c?mytool1.c?
mytool2.o:mytool2.c?mytool2.h?
gcc?-c?mytool2.c
有了這個(gè)Makefile文件,不論我們什么時(shí)候修改了源程序當(dāng)中的什么文件,我們只要執(zhí)行make命令,我們的編譯器都只會(huì)去編譯和我們修改的文件有關(guān)的文件,其它的文件它連理都不想去理的。?下面我們學(xué)習(xí)Makefile是如何編寫(xiě)的。?在Makefile中也#開(kāi)始的行都是注釋行.Makefile中最重要的是描述文件的依賴關(guān)系的說(shuō)明。一般的格式是:?
target:components?
TAB rule?
第一行表示的是依賴關(guān)系。第二行是規(guī)則。?
比如說(shuō)我們上面的那個(gè)Makefile文件的第二行。?
main:main.o mytool1.o mytool2.o?
表示我們的目標(biāo)(target)main的依賴對(duì)象(components)是main.o mytool1.omytool2.o當(dāng)倚賴的對(duì)象在目標(biāo)修改后修改的話,就要去執(zhí)行規(guī)則一行所指定的命令。
就象我們的上面那個(gè)Makefile第三行所說(shuō)的一樣要執(zhí)行 gcc-o main main.o mytool1.o mytool2.o注意規(guī)則一行中的TAB表示那里是一個(gè)TAB鍵?Makefile有三個(gè)非常有用的變量。
分別是$@,$^,$<代表的意義分別是:?
$@--目標(biāo)文件,$^--所有的依賴文件,$<--第一個(gè)依賴文件。?如果我們使用上面三個(gè)變量,
那么我們可以簡(jiǎn)化我們的Makefile文件為:?
# 這是簡(jiǎn)化后的Makefile?
main:main.o mytool1.o mytool2.o?
gcc -o $@ $^?
main.o:main.c mytool1.h mytool2.h?
gcc -c $<?
mytool1.o:mytool1.c mytool1.h?
gcc -c $<
mytool2.o:mytool2.c mytool2.h?
gcc -c $<?
經(jīng)過(guò)簡(jiǎn)化后,我們的Makefile是簡(jiǎn)單了一點(diǎn),不過(guò)人們有時(shí)候還想簡(jiǎn)單一點(diǎn)。這里我們學(xué)習(xí)一個(gè)Makefile的缺省規(guī)則?.c.o:?gcc -c $<?這個(gè)規(guī)則表示所有的 .o文件都是依賴與相應(yīng)的.c文件的。例如mytool.o依賴于mytool.c這樣Makefile還可以變?yōu)?#xff1a;?
# 這是再一次簡(jiǎn)化后的Makefile?
main:main.o mytool1.o mytool2.o?
gcc -o $@ $^?
.c.o:?
gcc -c $<?
好了,我們的Makefile 也差不多了,如果想知道更多的關(guān)于Makefile的規(guī)則,可以查看相應(yīng)的文檔。
makefile里PHONY的相關(guān)介紹
PHONY 目標(biāo)并非實(shí)際的文件名:只是在顯式請(qǐng)求時(shí)執(zhí)行命令的名字。有兩種理由需要使用PHONY目標(biāo):避免和同名文件沖突,改善性能。
如果編寫(xiě)一個(gè)規(guī)則,并不產(chǎn)生目標(biāo)文件,則其命令在每次make該目標(biāo)時(shí)都執(zhí)行。例如:
clean:
rm *.o temp
因?yàn)?span style="line-height:normal; font-family:Helvetica">"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
phony 目標(biāo)可以有依賴關(guān)系。當(dāng)一個(gè)目錄中有多個(gè)程序,將其放在一個(gè)makefile中會(huì)更方便。因?yàn)槿笔∧繕?biāo)是makefile中的第一個(gè)目標(biāo),通常將這個(gè)phony目標(biāo)叫做"all",其依賴文件為各個(gè)程序:
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
? ? ? ? cc -o prog1 prog1.o utils.o
prog2 : prog2.o
? ? ? ? cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
? ? ? ? cc -o prog3 prog3.o sort.o utils.o
假設(shè)你的一個(gè)項(xiàng)目最后需要產(chǎn)生兩個(gè)可執(zhí)行文件。你的主要目標(biāo)是產(chǎn)生兩個(gè)可執(zhí)行文件,但這兩個(gè)文件是相互獨(dú)立的——如果一個(gè)文件需要重建,并不影響另一個(gè)。你可以使用“假象目的”來(lái)達(dá)到這種效果。一個(gè)假象目的跟一個(gè)正常的目的幾乎是一樣的,只是這個(gè)目的文件是不存在的。因此, make總是會(huì)假設(shè)它需要 被生成,當(dāng)把它的依賴文件更新后,就會(huì)執(zhí)行它的規(guī)則里的命令行。
如果在我們的 makefile 開(kāi)始處輸入:
all : exec1 exec2
其中 exec1 和 exec2是我們做為目的的兩個(gè)可執(zhí)行文件。 make把這個(gè) 'all' 做為它的主要目的,每次執(zhí)行時(shí)都會(huì)嘗試把 'all'更新。但既然這行規(guī)則里沒(méi)有哪個(gè)命令來(lái)作用在一個(gè)叫 'all'的實(shí)際文件(事實(shí)上 all并不會(huì)在磁碟上實(shí)際產(chǎn)生),所以這個(gè)規(guī)則并不真的改變 'all'的狀態(tài)??杉热贿@個(gè)文件并不存在,所以 make會(huì)嘗試更新 all 規(guī)則,因此就檢查它的依靠 exec1, exec2是否需要更新,如果需要,就把它們更新,從而達(dá)到我們的目的。
假象目的也可以用來(lái)描述一組非預(yù)設(shè)的動(dòng)作。例如,你想把所有由 make產(chǎn)生的文件刪除,你可以在 makefile里設(shè)立這樣一個(gè)規(guī)則:
veryclean :
rm *.o
rm myprog
前提是沒(méi)有其它的規(guī)則依靠這個(gè) 'veryclean'目的,它將永遠(yuǎn)不會(huì)被執(zhí)行。但是,如果你明確的使用命令 'make veryclean', make會(huì)把這個(gè)目的做為它的主要目標(biāo),執(zhí)行那些 rm命令。
如果你的磁碟上存在一個(gè)叫 veryclean文件,會(huì)發(fā)生什么事?這時(shí)因?yàn)樵谶@個(gè)規(guī)則里沒(méi)有任何依靠文件,所以這個(gè)目的文件一定是最新的了(所有的依靠文件都已經(jīng)是最新的了),所以既使用戶明確命令 make重新產(chǎn)生它,也不會(huì)有任何事情發(fā)生。解決方法是標(biāo)明所有的假象目的(用 .PHONY),這就告訴 make不用檢查它們是否存在于磁碟上,也不用查找任何隱含規(guī)則,直接假設(shè)指定的目的需要被更新。在 makefile里加入下面這行包含上面規(guī)則的規(guī)則:
.PHONY : veryclean
就可以了。注意,這是一個(gè)特殊的 make規(guī)則,make知道 .PHONY是一個(gè)特殊目的,當(dāng)然你可以在它的依靠里加入你想用的任何假象目的,而 make知道它們都是假象目的。
?Makefile與shell腳本區(qū)別
在Makefile可以調(diào)用shell腳本,但是Makefile和shell腳本是不同的。本文試著歸納一下Makefile和shell腳本的不同。
1、 shell中所有引用以$打頭的變量其后要加{},而在Makefile中的變量是以$打頭的后加()。實(shí)例如下:
Makefile
PATH="/data/"
SUBPATH=$(PATH)
Shell
PATH="/data/"
SUBPATH=${PATH}
2、Makefile中所有以$打頭的單詞都會(huì)被解釋成Makefile中的變量。如果你需要調(diào)用shell中的變量(或者正則表達(dá)式中錨定句位$),都需要加兩個(gè)$符號(hào)($$)。實(shí)例如下:
PATH="/data/"
all:
? ? echo ${PATH}
? ? echo $$PATH
?例子中的第一個(gè)${PATH}引用的是Makefile中的變量,而不是shell中的PATH環(huán)境變量,后者引用的事Shell中的PATH環(huán)境變量。
3、通配符區(qū)別
shell 中通配符*表示所有的字符
Makefile 中通配符%表示所有的字符
4、在Makefile中只能在某一個(gè)target下的命令中調(diào)用Shell腳本,其他地方是不能輸出的。比如如下代碼就是沒(méi)有任何輸出:
VAR="Hello"
echo "$VAR"
all:
?? .....以上代碼任何時(shí)候都不會(huì)輸出,沒(méi)有在任何target下得命令內(nèi),如果上述代碼改為如下:
VAR="Hello"
all:
? ? echo "$VAR"
? ? .....以上代碼,在make all的時(shí)候?qū)?huì)執(zhí)行echo命令。
5、在Makefile中執(zhí)行shell命令,一行創(chuàng)建一個(gè)進(jìn)程來(lái)執(zhí)行。這也是為什么很多Makefile中有很多行的末尾都是“;? \”,以此來(lái)保證代碼是一行而不是多行,這樣Makefile可以在一個(gè)進(jìn)程中執(zhí)行,例如:
SUBDIR=src example
all:
? ? @for subdir in $(SUBDIR); \
? ? do\
? ? ? ? echo "building "; \
? ? done上述可以看出for循環(huán)中每行都是以”; \”結(jié)尾的。
6、獲取當(dāng)前目錄
PATH=`pwd` 注意是``,不是’'
?Makefile中的shell2009-12-24 09:27:05
分類:
一下摘錄Makefile中調(diào)用shell的一段
install:
? ? ? ? -if [ ! -e xxx ]; then sudo mkdir xxx; fi
注意,將上面的if語(yǔ)句寫(xiě)到一行的話,必須在fi前面加上分號(hào),否則會(huì)出現(xiàn)下面錯(cuò)誤
unexpected end of file
下面轉(zhuǎn)一個(gè)相關(guān)文章
Makefile與Shell的問(wèn)題
?
大概只要知道Makefile的人,都知道Makefile可以調(diào)用Shell腳本。但是在實(shí)際使用時(shí),并不那么簡(jiǎn)單,一些模棱兩可的地方可能會(huì)讓你抓狂。你若不信,可以先看幾個(gè)例子,想象一下這些這些例子會(huì)打印什么內(nèi)容,記下你想象的結(jié)果,然后在計(jì)算機(jī)上運(yùn)行這些例子,對(duì)照看一下。
?
?
示例一:
if [ "$(BUILD)" = "debug" ]; then? echo "build debug"; else echo "build release"; fi
all:
? ? echo "done"
示例二:
all:
? ? @CC=arm-linux-gcc
? ? @echo $(CC)
示例三:
CC=arm-linux-gcc
all:
? ? @echo $(CC)
示例四:
SUBDIR=src example
all:
? ? @for subdir in $(SUBDIR); \
? ? do\
? ? ? ? echo "building " $(subdir); \
? ? done
?
?
說(shuō)明:
1. ? ? ? ? Shell腳本在target里才有效,其它地方都被忽略掉了。所以示例一中,”build debug”之類的字符串根本打印不出來(lái)。示例一的正確寫(xiě)法是:
?
示例一:
all:
? ? if [ "$(BUILD)" = "debug" ]; then? echo "build debug"; else echo "build release"; fi
? ? echo "done"
?
2. ? ? ? ? make把每一行Shell腳本當(dāng)作一個(gè)獨(dú)立的單元,它們?cè)趩为?dú)的進(jìn)程中運(yùn)行。示例二中,兩行Shell腳本在兩個(gè)莫不相干的進(jìn)程里運(yùn)行,第一個(gè)進(jìn)程把CC設(shè)置為arm-linux-gcc,第二個(gè)進(jìn)程是不知道的,所以打印的結(jié)果自然不是arm-linux-gcc了。示例二的正確寫(xiě)法是:
?
示例二:
all:
? ? @CC=arm-linux-gcc; echo $(CC)
或者:
all:
@CC=arm-linux-gcc; \
echo $(CC)
?
3. ? ? ? ? make在調(diào)用Shell之前先進(jìn)行預(yù)處理,即展開(kāi)所有Makefile的變量和函數(shù)。這些變量和函數(shù)都以$開(kāi)頭。示例三中,Shell拿的腳本實(shí)際上是echo arm-linux-gcc,所以打印結(jié)果正確。
?
4. ? ? ? ? make預(yù)處理時(shí),所有以$開(kāi)頭的,它都不會(huì)放過(guò)。要想引用Shell自己的變量,應(yīng)該以$$開(kāi)頭。另外要注意,Shell自己的變量是不需要括號(hào)的。示例四的正確寫(xiě)法是:
?
示例四:
SUBDIR=src example
all:
? ? @for subdir in $(SUBDIR); \
? ? do\
? ? ? ? echo "building " $$subdir; \
? ? done
感謝,Thanks!
?linux Makefile obj-m obj-y ..
分類: Linux
2013-02-20 14:01 1773人閱讀 評(píng)論(0) 收藏 舉報(bào)
目標(biāo)定義是Kbuild Makefile的主要部分,也是核心部分。主要是定義了要編 譯的文件,所有的選項(xiàng),以及到哪些子目錄去執(zhí)行遞歸操作。 最簡(jiǎn)單的Kbuild makefile 只包含一行: 例子: obj-y += foo.o 該例子告訴Kbuild在這目錄里,有一個(gè)名為foo.o的目標(biāo)文件。foo.o將從foo.c 或foo.S文件編譯得到。 如果foo.o要編譯成一模塊,那就要用obj-m了。所采用的形式如下: 例子: obj-$(CONFIG_FOO) += foo.o $(CONFIG_FOO)可以為y(編譯進(jìn)內(nèi)核) 或m(編譯成模塊)。如果CONFIG_FOO不是y 和m,那么該文件就不會(huì)被編譯聯(lián)接了
Linux各級(jí)內(nèi)核源代碼的子目錄下都有Makefile,大多數(shù)Makefile要嵌入主目錄下的Rule.make,Rule.make將識(shí)別各個(gè)Makefile中所定義的一些變量。變量obj-y表示需要編繹到內(nèi)核中的目標(biāo)文件名集合,定義O_TARGET表示將obj-y連接為一個(gè)O_TARGET名稱的目標(biāo)文件,定義L_TARGET表示將obj-y合并為一個(gè)L_TARGET名稱的庫(kù)文件。同樣obj-m表示需要編繹成模塊的目標(biāo)文件名集合。如果還需進(jìn)行子目錄make,則需要定義subdir-y和subdir-m。在Makefile中,用"obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o"和"subdir-$(CONFIG_EXT2_FS) += ext2"這種形式自動(dòng)為obj-y、obj-m、subdir-y、subdir-m添加文件名。有時(shí),情況沒(méi)有這么單純,還需要使用條件語(yǔ)句個(gè)別對(duì)待。Makefile中還有其它一些變量,如mod-subdirs定義了subdir-m以外的所有模塊子目錄。?
Rules.make是如何使make進(jìn)入子目錄的呢? 先來(lái)看subdir-y是如何處理的,在Rules.make中,先對(duì)subdir-y中的每一個(gè)文件名加上前綴"_subdir_"再進(jìn)行排序生成subdir-list集合,再以它作為目標(biāo)集,對(duì)其中每一個(gè)目標(biāo)產(chǎn)生一個(gè)子make,同時(shí)將目標(biāo)名的前綴去掉得到子目錄名,作為子make的起始目錄參數(shù)。subdir-m與subdir-y類似,但情況稍微復(fù)雜一些。由于subdir-y中可能有模塊定義,因此利用mod-subdirs變量將subdir-y中模塊目錄提取出來(lái),再與subdir-m合成一個(gè)大的MOD_SUB_DIRS集合。subdir-m的目標(biāo)所用的前綴是"_modsubdir_"。?
一點(diǎn)說(shuō)明,子目錄中的Makefile與Rules.make都沒(méi)有嵌入.config文件,它是通過(guò)主Makefile向下傳遞MAKEFILES變量完成的。MAKEFILES是make自已識(shí)別的一個(gè)變量,在執(zhí)行新的Makefile之前,make會(huì)首先加載MAKEFILES所指的文件。在主Makefile中它即指向.config。?
總結(jié)
以上是生活随笔為你收集整理的Makefile札记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Volatile的陷阱
- 下一篇: 通用Makefile实现