參考原文http://blog.csdn.net/liang13664759/article/details/1771246/bash
Makefile書寫規則包含兩部分,一個是依賴關係,一個是生成目標。在Makefile中,規則的順序很重要,由於Makefile中只有一個最終目標。通常,定義在Makefile中的目標可能會有不少,可是第一條規則中的目標將被確立爲最終目標。若是第一條規則中的目標有多個,那麼第一個目標會成爲最終目標,也就是make所完成的目標。函數
foo.o:foo.c defs.h #foo模塊ui
cc -c -g foo.cspa
這個規則告訴咱們:文件的依賴關係,還有若是要生成或更新foo.o文件,則cc命令說明如何生成。.net
target:prerequisites命令行
commandcode
...blog
或者:文檔
targets: prerequisites;commandget
command
...
targets是文件名,空格分開,可使用通配符。通常,咱們的目標基本上是一個文件,但也多是多個文件。
command是命令行,若是其不與「target:prerequisites」在一行上,那麼必須以Tab鍵開頭。若是和prerequisites在一行可用分好做爲分隔。
prerequisites即爲目標所依賴的文件或依賴目標。若是其中的某個文件要比目標文件新,則從新生成。
‘/’表示換行符。make會以UNIX的標準Shell,也就是/bin/sh來執行命令。
make支持三種通配符:‘*’,「?」和「[...]」
"~"字符在文件名中也比較特殊,「~/test」表示當前用戶的$HOME目錄下的test目錄
而「~hchen/test」則表示用戶hchen的宿主目錄下的test目錄
例子:
objects = *.o 表示objects就是「*.o」,並不會展開。
若是要讓通配符在變量中展開,讓objects的值是全部[.o]的文件名的集合,
能夠這樣:objects:=$(wildcard *.o)主要是由關鍵字wildcard控制。
當make尋找文件的依賴關係時,能夠在文件前加上路徑,但最好的方法是把一個路徑告訴make,讓make自動尋找。
一、VPATH變量
Makefile文件中的特殊變量「VPATH」就是完成這個功能的,若是沒有指明這個變量,make只會在當前的目錄中去尋找依賴文件和目標。若是定義了VPATH,那麼make會在當前目錄找不到的狀況下,到所指定的目錄中去尋找文件。
VPATH=src:../headers
該定義制定兩個目錄,「src」和「../headers」,make會按照這個順序搜索,目錄由冒號分隔。(當前目錄永遠是最優先搜索的地方)
二、vpath關鍵字
另外一個設置是使用make的「vpath」關鍵字(全小寫),這不是變量,是一個make的關鍵字。和上面提到的VPATH相似,但更靈活。能夠制定不一樣的文件在不一樣的目錄中搜索。
使用方法:
①、vpath<pattern><directories> 爲符合模式<patern>的文件制定搜索目錄<directories>
②、vpath<pattern> 清除符合模式<pattern>的文件搜索目錄
③、vpath 清除全部已經設置好的文件搜索目錄
說明:vpath中的<pattern>須要包含「%」,意思是匹配零或若干字符,例如,「%.h」表示全部以「.h」結尾的文件。<pattern>制定了要搜索的文件集,而<directories>則制定了<pattern>的文件集搜索目錄。
例如: vpath %.h ../headers 表示make在「../headers」目錄下搜索全部以「.h」結尾的文件。
連續使用vpath語句,可制定不一樣搜索策略,若是連續的vpath語句出現了相同的<pattern>,或是被重複了的<pattern>,則make會按照vpath語句的前後順序來執行搜索。
vpath %.c foo
vpath % blish
vpath %.c bar
表示「.c」文件,先在foo目錄,而後是blish,最後是bar目錄。
vpath %.c foo:bar
vpath % blish
上面的語句則表示「.c」文件,先在foo目錄,而後是bar目錄,最後是blish目錄
如前面的clean文件,並非文件,只是一個標籤。make沒法生成它的依賴關係和決定他是否執行,只能能過顯式指明這個目標纔可以讓其生效。固然,爲目標的取名不能和文件名重名,否則失去僞目標的意義。
聲明.PHONY:clean,表不論是否有「clean」文件,要運行「clean」目標,只有「make clean」。
僞目標通常沒有依賴的文件。可是,也能夠爲僞目標指定所依賴的文件。僞目標一樣能夠做爲「默認目標」。只要將其放在第一個。例如,當Makefile須要一會兒生成若干個可執行文件,可是隻想簡單地一個make命令完事,而且全部的目標文件都寫在一個Makefile中,那麼可使用僞目標這特性:
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
Makefile中的第一個目標會被做爲默認目標。聲明瞭一個「all」僞目標,依賴於其餘三個目標,因爲僞目標的特性是總被執行。因此其依賴於的那三個目標就老是不如all目標新。因此其餘三個目標老是會被執行,也就達到了一會兒生成多個目標的目的。
Makefile的規則中的目標能夠不止一個,支持多目標。有可能咱們的多個目標同時依賴於一個文件,其生成命令大致相似。因而咱們能夠將其合併。自動化變量「$@」,表示目前規則中全部的目標集合。
bigoutput littleoutput: text.g
generate text.g -$(subst output,,$@)>$@
上述規則等價於:
bigoutput: text.g
generate text.g -big > bigoutput
littleoutput:text.g
generate text.g -little > littleoutput
其中,-$(subst output,,$@)中的「$」表示執行了一個Makefile的函數,函數名爲subst,後面的爲參數。「$@」表示目標集合,依次取出目標並執行命令。
靜態模式能夠更容易地定義多目標的規則,語法:
<target ...>:<target-parttern>:<prereq-patterns ...>
<command>
...
targets定義了一系列的目標文件,能夠有通配符。
targets-pattern是指明瞭targets的模式,也就是目標集模式
prereq-patterns是目標的依賴模式,對target-pattern造成的模式再進行一次依賴目標的定義。
例子:
objects = foo.o bar.o
all: $(objects)
$(objects) : %.o : %.c
$(CC) -c $(CFLAGS) $< -o $@
例子,咱們的目標從$object中獲取,「%.o」代表要全部「.o」結尾的目標,也就是「foo.o bar.o」,變量$objects集合的模式,
而依賴模式「%.c」則取模式「%.o」的「%」,也就是「foo bar」,併爲其加上「.c」後綴,因而,依賴目標就是「foo.c bar.c」。
而命令中的「$<」和「$@」自動化變量,「$<」表示全部的依賴目標集(「foo.c bar.c」),「$@」表目標集(「foo.o bar.o」)
因此,上面的規則等價於:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
另外一個例子:
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o : %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)):%.elc:%el
emacs -f batch-byte-compile $<
說明:$(filter %.o,$(files))表示調用Makefile的filter函數,過濾「$filter」集,只要其中模式爲「%.o」的內容。
cc -M main.c 中的-M選項,自動找尋源文件中包含的頭文件,並生成一個依賴關係。
其輸出是: main.o : main.c defs.h
由於mian.c中包含頭文件defs.h,因而編譯器自動生成依賴關係。若是是GNU的C/C++編譯器,得用「-MM」參數,否則,「-M」參數會把一些標準庫的頭文件也包含進來。
編譯器的功能聯繫進Makefile,GNU建議把編譯器爲每個源文件自動生成的依賴關係放到一個文件中,爲每個「name.c」的文件都生成一個「name.d」的Makefile文件,[.d]文件中就存放對應[.c]文件的依賴關係。因而,能夠寫出[.c]文件和[.d]文件的依賴關係,並讓make自動更新或生成[.d]文件,幷包含在咱們的主Makefile中。
產生[.d]文件的模式規則:
%.d : %.c
@set -e; rm -f $@;/
$(CC) -M $(CPFLAGS) $< > $@.;/
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.> $@; /
rm -f $@.
@set -e表示告訴bash若是任何語句的執行結果不是true則應該退出。該規則的意思是,全部的[.d]文件依賴於[.c]文件,「rm -f $@」刪除全部目標[.d]文件。下一行是爲每一個依賴文件「$<」,也就是[.c]文件生成依賴文件,「$@」表示模式「%.d」文件,若是有一個C文件是name.c,那麼「%」就是「name」。sed命令,查看文檔。這裏表示整行替換
即把依賴關係mian.o : main.c defs.h ====>>>轉成: main.o main.d : main.c defs.h
接下來,把自動生成的規則放進主Makefile中,使用Makefile中的「include」引入別的Makefile文件,例如:
sources = foo.c bar.c
include $(sources:.c = .d)
說明:「$(sources:.c=.d)」中的「.c=.d」表示替換,把變量$(sources)全部的[.c]的字串都替換成[.d]。替換也會按照次序來的。