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