本文摘抄自「跟我一塊兒寫Makefile 」,只是原文中我本身感受比較精要的一部分,而且只針對C語言,使用GCC編譯器。 原文請看這裏:http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefilehtml
寫完以後才發現基本上都是一些比較枯燥的規則,看看1、2、八三個部分就能夠了。看成參考工具吧,何時用到了再來看看。shell
1、概述ubuntu
我所使用的make 版本是 GNU Make 3.81,使用的系統是 Ubuntu 10.10,GCC版本爲 4.4.5。與原文做者使用的Make 版本很類似。函數
1.1 關於程序的編譯和連接工具
對於C語言的編譯,首先要把源文件編譯成中間代碼文件,即.o文件(在Windows下是.obj文件)。這個動做叫作編譯(compile)。而後再把大量Object File合成執行文件,這個動做叫作連接(link)。更加詳細的內容能夠參考這裏:使用gcc編譯C程序的詳細過程post
編譯時,編譯器主要檢查語法、函數與變量的聲明是否正確。函數的聲明一般放在頭文件中(頭文件中應該只放聲明,對於函數的具體實現則應該放到單獨的 .c 源文件中),你告訴編譯器頭文件所在的位置,編譯器就會到相應的地方去找函數聲明。只要語法正確,編譯器就會生成中間文件。通常來講一個源文件(.c)對應一個目標文件(.o)。測試
連接時,主要是連接函數和全局變量,因此,咱們可使用這些中間目標文件(O文件或是OBJ文件)來連接咱們的應用程序,並不須要源文件的存在。在不少時候,因爲中間目標文件太多,而在連接時須要明顯地指出中間目標文件名,這對於編譯很不方便,因此,咱們要給中間目標文件打個包,在Windows下這種包叫「庫文件」(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。優化
2、Makefile 介紹ui
2.1 Makefile的規則spa
target ... : prerequisites ... command ... ...
target能夠是一個object file(目標文件),也能夠是一個執行文件,還能夠是一個標籤(label)。對於標籤這種特性,在後續的「僞目標」章節中會有敘述。
prerequisites就是,要生成那個target所須要的文件或是目標。
command也就是make須要執行的命令。(任意的shell命令)。注意command以前是TAB鍵,並不是空格鍵。
2.2 一個試例
一個工程有3個頭文件,和8個c文件,關係以下圖:
咱們的makefile應該是下面的這個樣子的:
edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o main.o : main.c defs.h cc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c command.o : command.c defs.h command.h cc -c command.c display.o : display.c defs.h buffer.h cc -c display.c insert.o : insert.c defs.h buffer.h cc -c insert.c search.o : search.c defs.h buffer.h cc -c search.c files.o : files.c defs.h buffer.h command.h cc -c files.c utils.o : utils.c defs.h cc -c utils.c clean : rm edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
make命令會找到Makefile文件中第一個target,而且把它當作最終的目標文件。Makefile所指示出來的關係就是咱們在上面的圖中所展現的關係。
2.3 Makefile中使用變量
makefile的變量也就是一個字符串,理解成C語言中的宏可能會更好。
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o edit : $(objects) cc -o edit $(objects)
2.4 讓make自動推導
只要make看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關係中。
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc = gcc edit : $(objects) cc -o edit $(objects) main.o : defs.h kbd.o : defs.h command.h command.o : defs.h command.h display.o : defs.h buffer.h insert.o : defs.h buffer.h search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h .PHONY : clean clean : rm edit $(objects)
2.5 清空目標文件的規則
.PHONY : clean clean : -rm edit $(objects)
2.6 Makefile中有什麼?
Makefile裏主要包含了五個東西:顯式規則、隱晦規則、變量定義、文件指示和註釋。
最後,還值得一提的是,在Makefile中的命令,必需要以[Tab]鍵開始。
2.7 引用其餘Makefile
在include前面能夠有一些空字符,可是毫不能是[Tab]鍵開始。你有這樣幾個Makefile:a.mk、b.mk、c.mk,還有一個文件叫foo.make,以及一個變量$(bar),其包含了 e.mk和f.mk。
include foo.make *.mk $(bar)
若是文件都沒有指定絕對路徑或是相對路徑的話,make會在當前目錄下首先尋找,若是當前目錄下沒有找到,那麼,make還會在下面的幾個目錄下找:
若是有文件沒有找到的話,make會生成一條警告信息,但不會立刻出現致命錯誤。它會繼續載入其它的文件,一旦完成makefile的讀取, make會再重試這些沒有找到,或是不能讀取的文件,若是仍是不行,make纔會出現一條致命信息。若是你想讓make不理那些沒法讀取的文件,而繼續執行,你能夠在include前加一個減號「-」,其表示,不管include過程當中出現什麼錯誤,都不要報錯繼續執行。
2.8 make的工做方式
GNU的make工做時的執行步驟入下:(想來其它的make也是相似)
1-5步爲第一個階段,6-7爲第二個階段。第一個階段中,若是定義的變量被使用了,那麼,make會把其展開在使用的位置。但make並不會徹底立刻展開,make使用的是拖延戰術,若是變量出如今依賴關係的規則中,那麼僅當這條依賴被決定要使用了,變量纔會在其內部展開。
看完這些再看 第八部分:隱含規則,就能夠了,中間的都不用看了。我怎麼寫了這麼多。
3、書寫規則
3.1 文件搜索
使用特殊變量VPATH 來搜索,若是不指定,make只會在當前的目錄中去找尋依賴文件和目標文件。指定了這個變量,在當前目錄搜索完而找不到以後會搜索相應目錄。
VPATH = src:../headers
目錄之間用冒號隔開。
還可使用vpath關鍵字,它的使用方法有三種:這裏只說一種,詳情看原文。
爲符合模式<pattern>的文件指定搜索目錄<directories>。
vapth使用方法中的<pattern>須要包含「%」字符。「%」的意思是匹配零或若干字符,(需引用「%」,使用「\%")例如,「%.h」表示全部以 「.h」結尾的文件。<pattern>指定了要搜索的文件集,而<directories>則指定了< pattern>的文件集的搜索的目錄。
vpath %.h ../headers
3.2 僞目標
好比剛開始咱們指定的
.PHONY : clean clean : rm *.o temp
也能夠爲僞目標指定依賴文件,常見於all,而且把它放在最前,指定爲Makefile的終極目標。因爲all是僞目標,因此不會生成all文件。
all : prog1 prog2 prog3 .PHONY : all
3.3 靜態模式
靜態模式能夠更加容易地定義多目標的規則,語法:
<targets ...>: <target-pattern>: <prereq-patterns ...> <commands> ...
以下面的例子:
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -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
$(objects): %.o: %.c,這裏先把$(object) 在這裏展開,表示把全部objects之中的.o文件都用.c文件與之對應起來。
$< 表示依賴文件,這裏即.c文件, $@ 表示target文件,這裏即.o文件。
3.4 自動生成依賴性
在GCC中,咱們用gcc –MM main.c 這條命令就能夠查看全部main.o 的依賴關係。因而,能夠利用這一特性來讓Makefile自動生成依賴關係,不用咱們本身寫了。
具體看這裏。
4、書寫命令
每條規則中的命令和操做系統Shell的命令行是一致的。make會按順序一條一條的執行命令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規則後面的分號後的。
4.1 顯示命令
用「@」字符在命令行前,那麼,這個命令將不被make顯示出來。
make執行時,帶入make參數「-n」或「--just-print」,那麼其只是顯示命令,但不會執行命令。利於調試Makefile。
make參數「-s」或「--slient」則是全面禁止命令的顯示。
4.2 命令執行
若是上一條命令執行的結果須要在下一條指令中使用,那麼這兩個指令應該放在一行,中間用分號隔開。
exec: cd /home/hchen; pwd
4.3 命令出錯
若是想要忽略命令的錯誤,在它以前加 ‘-’便可。
4.4 嵌套執行make
在一些大的工程中,咱們會把咱們不一樣模塊或是不一樣功能的源文件放在不一樣的目錄中,咱們能夠在每一個目錄中都書寫一個該目錄的Makefile,這有利於讓咱們的Makefile變得更加地簡潔,而不至於把全部的東西所有寫在一個Makefile中,這樣會很難維護咱們的Makefile,這個技術對於咱們模塊編譯和分段編譯有着很是大的好處。
例如,咱們有一個子目錄叫subdir,這個目錄下有個Makefile文件,來指明瞭這個目錄下文件的編譯規則。那麼咱們總控的Makefile能夠這樣書寫:
subsystem: cd subdir && $(MAKE)
等價於:
subsystem: $(MAKE) -C subdir
具體看這裏。
5、使用變量
在Makefile中的定義的變量,表明了一個文本字串,在Makefile中執行的時候其會自動原模原樣地展開在所使用的地方。變量是大小寫敏感的。「$<」、「$@」等,這些是自動化變量。
5.1 變量的基礎
變量在定義時要賦初值。使用時在其前加$ 符號,而且用() 或者 {} 把變量名括起來。"$$」 表示真實的 $。
5.2 變量中的變量
這裏說變量怎樣定義,怎樣賦值。
變量賦值有三種方式,一種是 = ,一種是 := ,一種是 ?= 。前一種可使用延後定義的變量,中間只能按照從上到下的順序定義變量。最後一種表示若是這個變量已經賦值,就再也不對他進行賦值了,若是沒有賦值,就對他賦值。詳情參看這裏。
+= 是追加賦值。
好比定義一個空格,下面的變量space就表示一個空格,其餘方法還很差定義空格:
nullstring :=# there is nothing space := $(nullstring) # end of the line
5.3 變量高級用法
· 變量值的替換:
咱們能夠替換變量中的共有的部分,其格式是「$(var:a=b)」或是「${var:a=b}」,其意思是,把變量「var」中全部以「a」字串「結尾」的「a」替換成「b」字串。這裏的「結尾」意思是「空格」或是「結束符」。例如:
foo := a.o b.o c.o bar := $(foo:.o=.c)
對於靜態模式,咱們要這樣寫: bar := $(foo:%.o=%.c)
· 把變量的值再當成變量
即便用變量時,每一個變量都展開成一個字符串使用:
x = y y = z a := $($(x))
更加詳細的描述見這裏。
5.4 目標變量
爲某個target指定只會在這條規則以及它所連帶的規則中使用的變量:
prog : CFLAGS = -g prog : prog.o foo.o bar.o $(CC) $(CFLAGS) prog.o foo.o bar.o prog.o : prog.c $(CC) $(CFLAGS) prog.c foo.o : foo.c $(CC) $(CFLAGS) foo.c bar.o : bar.c $(CC) $(CFLAGS) bar.c
6、使用函數
在Makefile中可使用函數來處理變量,從而讓咱們的命令或是規則更爲的靈活和具備智能。make所支持的函數也不算不少,不過已經足夠咱們的操做了。函數調用後,函數的返回值能夠當作變量來使用。
6.1 函數調用的語法
函數調用,很像變量的使用,也是以「$」來標識的,其語法以下:
$(<function> <arguments>) 或者 ${<function> <arguments>}
<arguments>爲函數的參數,參數間以逗號「,」分隔,而函數名和參數之間以「空格」分隔。下面是一個具體的例子:
comma:= , empty:= space:= $(empty) $(empty) foo:= a b c bar:= $(subst $(space),$(comma),$(foo))
6.2 字符串處理函數
$(subst <from>,<to>,<text>)
$(patsubst <pattern>,<replacement>,<text>)
這和咱們前面「變量章節」說過的相關知識有點類似。如:
「$(var:<pattern>=<replacement>;)」 至關於 「$(patsubst <pattern>,<replacement>,$(var))」,
而「$(var: <suffix>=<replacement>)」 則至關於 「$(patsubst %<suffix>,%<replacement>,$(var))」。
例若有:objects = foo.o bar.o baz.o, 那麼,「$(objects:.o=.c)」和「$(patsubst %.o,%.c,$(objects))」是同樣的。
$(filter <pattern...>,<text>)
其餘更多的函數和示例參看這裏。
6.3 文件名操做函數
這裏列出不少對文件名的操做函數。
還有foreach, if, call這三個比較特殊的函數。
7、make的運行
7.1 指定Makefile
指定某個不叫Makefile的文件運行make命令,加-f選項:
make –f hchen.mk
7.2 指定目標
「all」這個僞目標是全部目標的目標,其功能通常是編譯全部的目標。「clean」這個僞目標功能是刪除全部被make建立的文件。「install」這個僞目標功能是安裝已編譯好的程序,其實就是把目標執行文件拷貝到指定的目標中去。「print」這個僞目標的功能是例出改變過的源文件。「tar」這個僞目標功能是把源程序打包備份。也就是一個tar文件。「dist」這個僞目標功能是建立一個壓縮文件,通常是把tar文件壓成Z文件。或是gz文件。「TAGS」這個僞目標功能是更新全部的目標,以備完整地重編譯使用。「check」和「test」這兩個僞目標通常用來測試makefile的流程。
7.3 檢查規則
「-n」 「--just-print」 「--dry-run」 「--recon」 不執行參數,這些參數只是打印命令,無論目標是否更新,把規則和連帶規則下的命令打印出來,但不執行,這些參數對於咱們調試makefile頗有用處。
7.4 make的參數
點擊小標題。
8、隱含規則
8.1 C語言編譯的隱含規則
「<n>;.o」的目標的依賴目標會自動推導爲「<n>;.c」,而且其生成命令是「$(CC) –c $(CPPFLAGS) $(CFLAGS)」。
其餘語言的隱含規則看這裏。
8.2 隱含規則使用的變量
一、關於命令的變量
CCC語言編譯程序。默認命令是「cc」。
二、關於命令參數的變量
CFLAGSC語言編譯器參數。
更多變量看這裏。
8.3 模式規則
模式規則中都是用%,它表示匹配0個(仍是1個?)或多個字符。
%.o : %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
8.4 自動化變量
所謂自動化變量,就是這種變量會把模式中所定義的一系列的文件自動地挨個取出,直至全部的符合模式的文件都取完了。這種自動化變量只應出如今模式規則的命令中。
在 a: a.o b.o c.o 這個依賴規則中,a 叫作target,即目標; a.o b.o c.o 都叫作 依賴目標。
多麼但願有個實例能夠作一下呀,有好多文件,看看怎麼把這些文件用Makefile組織起來,而且優化Makefile的文件。只看這些乾巴巴的規則,一下子就累了………………………………