makefile是爲工程組織編譯,爲「自動化編譯」,一旦寫成,只須要一個make命令,整個工程徹底自動編譯,極大提升了軟件開發效率。make是一個命令工具,是一個解釋makefile中指令的命令工具。通常來講,大多數IDE都有這個命令,如Delphi的make,Visual C++的nmake,linux下GNU的make。可見,Makefile都成爲一種在工程方面的編譯方法。
一個Makefile告訴make命令如何編譯和連接文件,規則是:
1) 若是這個工程沒有編譯過,那麼咱們的全部C文件都要編譯並被連接。
2) 若是這個工程的某幾個C文件被修改,那麼咱們只編譯被修改的C文件,並連接目標程序。
3) 若是這個工程的頭文件被改變了,那麼咱們須要編譯引用了這幾個頭文件的 C 文件,並連接目標程序。
只要咱們的 Makefile 寫得夠好,全部的這一切,咱們只用一個 make 命令就能夠完成,make 命令會自動智能地根據當前的文件修改的狀況來肯定哪些文件須要重編譯,從而本身編譯所須要的文件和連接目標程序。html
通常來講,不管是C仍是C++,首先要把源文件編譯成中間代碼文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,這個動做叫作編譯(compile),通常來講,每一個源文件都應該對應於一箇中間目標文件(O文件或是OBJ文件)。而後再把大量的Object File合成執行文件,這個動做叫做連接(link)。
編譯時,編譯器須要的是語法的正確,函數與變量的聲明的正確。對於後者,一般是你須要告訴編譯器頭文件的所在位置(頭文件中應該只是聲明,而定義應該放在C/C++文件中),只要全部的語法正確,編譯器就能夠編譯出中間目標文件。
連接時,主要是連接函數和全局變量,因此,咱們可使用這些中間目標文件(O文件或是OBJ文件)來 連接咱們的應用程序。連接器並無論函數所在的源文件,只管函數的中間目標文件(Object File),在大多數時候,因爲源文件太多,編譯生成的中間目標文件太多,而在連接時須要明顯地指出中間目標文件名,這對於編譯很不方便,因此,咱們要給 中間目標文件打個包,在Windows下這種包叫「庫文件」(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。linux
target …: prerequisites…
command
make的書寫規則包含兩部分,一個是依賴關係,另外一個是生成目標的方法。
prerequisites中若是有一個以上的文件比target文件要新的話,command所定義的命令就會被執行。這就是Makefile的規則,也就是Makefile中最核心的內容。
makefile文件中只應該有一個最終目標,其餘的目標都是被這個目標所連帶出來的。通常來講,定義在makefile文件中的目標會不少,但第一條規則中的目標將被肯定爲最終目標。
每一個Makefile都應該寫一個清空目標文件的規則(.o和執行文件),這不只便於重編譯,也有利於保持文件的清潔。這是一個「修養」。
.PHONY: clean
clean:
-rm edit $(objects)shell
隱含規則1:倚賴關係中有FORCE編程
download: .config FORCEide
FORCE:
.PHONY: FORCE函數
FORCE總會被認爲是最新的,這樣當FORCE做爲其餘規則的倚賴時,倚賴總被認爲更新過,規則中定義的命令總會被執行。工具
1) 讀入全部makefile文件;
2) 讀入被include包括的其餘makefile文件;
3) 初始化文件中變量;
4) 推導隱式規則,並分析全部規則;
5) 爲全部目標文件建立依賴關係鏈;
6) 根據依賴關係,決定哪些目標要從新生成;
7) 執行生成命令;
其中,1)-5)爲第一階段,6)-7)爲第二階段。ui
注:倚賴關係必須先存在,而後才能執行命令,即倚賴關係先執行,而後生產目標的命令才能執行。spa
規則中的命令同OS中Shell命令行是一致的。每條命令必須以Tab鍵開頭,除非命令緊跟在依賴關係分號後。命令行之間的空格或空行會被忽略,但空格或空行以Tab鍵開頭的,make會認爲是一個空命令,除非指定一個其餘的shell。
1. 顯示命令
命令行前加「@」字符時,這個命令不被make顯示出來。
1) 若是執行make時,帶入make參數-n或--just-print,其只顯示命令,而不執行命令。
2) make參數-s或--slient全面禁止命令顯示。.net
2. 執行命令
若是要讓一條命令的結果應用在下一條命令上,應使用分號分隔兩條命令(兩命令同行)。
3. 命令出錯
忽略錯誤執行:
1) 命令行前加「-」;
2) make加參數-i或--ignore-errors;
3) make加參數-k或--keep-going(終止此規則執行,繼續執行其餘規則)。
4. 嵌套執行make
不一樣模塊在不一樣目錄中,每一個目錄都書寫一個該目錄的makefile文件。
例:有一個子目錄subdir,這個目錄下有個makefile文件來指明這個目錄下文件編譯規則。總控makefile能夠這樣寫:
subsystem:
cd subdir && $(MAKE)
<==>
subsystem:
$(MAKE) –C subdir
定義($(MAKE))宏變量是由於,也許make須要一些參數,因此定義成一個變量比較利於維護。
dirs :=multfile/src multfile/sub \ top/src all: $(foreach N, $(dirs),make -C $(N);) clean: $(foreach N, $(dirs),make clean -C $(N);)
若是你要傳遞變量到下級Makefile中,那麼你可使用這樣的聲明:
export <variable ...>
若是你不想讓某些變量傳遞到下級Makefile中,那麼你能夠這樣聲明:
unexport <variable ...>
如果你要傳遞全部的變量,那麼只要一個export就好了。後面什麼也不用跟,表示傳遞全部的變量。
要注意的是,有兩個變量,一個是SHELL,一個是MAKEFLAGS,這兩個變量無論你是否export,其老是要傳遞到下層Makefile中,特別是MAKEFILES變量,其中包含了make的參數信息,若是咱們執行「總控Makefile」時有make參數或是在上層Makefile中定義了這個變量, 那麼MAKEFILES變量將會是這些參數, 並會傳遞到下層Makefile中, 這是一個系統級的環境變量。
可是make命令中的有幾個參數並不往下傳遞,它們是「-C」,「-f」,「-h」 「-o」和「-W」,若是你不想往下層傳遞參數,那麼,你能夠這樣來:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
若是你定義了環境變量MAKEFLAGS,那麼你得確信其中的選項是你們都會用到的,若是其中有「-t」,「-n」,和「-q」參數,那麼將會有讓你意想不到的結果,或許會讓你異常地恐慌。
5. 定義命令包
若是Makefile中出現一些相同命令序列,那麼咱們能夠爲這些相同的命令序列定義一個變量。定義這種命令序列的語法以「define」開始,以「endef」結束。
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
這裏,「 run-yacc」是這個命令包的名字,其不要和 Makefile 中的變量重名。在「define」和「endef」中的兩行就是命令序列。
make支持3種通配符:「*」,「?」和「[…]」。 「\*」用轉義字符「\」來顯示「*」。
* 表示任意一個或多個字符
? 表示任意一個字符
[…] [abcd]表示a,b,c,d中任意一個字符,[^abcd]表示除a,b,c,d之外的字符,[0-9]表示0~9中任意一個數字
~ 表示用戶的home目錄
在Makefile規則中,通配符會被自動展開。但在變量的定義和函數引用時,通配符將失效。這種狀況若是須要通配符有效,就須要使用函數wildcard。
變量聲明時賦初值,使用時,須要在變量名前加上「$」符號,但最好用「()」或「{}」括起來。如使用真實的$要用「$$」來表示。
定義變量值時,能夠用其餘變量來構造變量的值,有兩種方式:
1)= 左變量右側值
右側值可定義在賦值前也可後。
2):= 賦值時右側變量必須先定義
要定義一變量,其值是一個空格,能夠
nullstring :=
space := $(nullstring) #end
而dir: = /foo/bar #end表明四個空格
3)?=
如:FOO ?= bar
若FOO沒有定義過,變量FOO值就是bar;
若FOO先前被定義,則這條語句什麼也不作。
4)+= 追加變量值
若是變量以前沒有定義過,「+=」會自動變成「=」,若定義過,「+=」會繼承前一次操做的賦值。
5)變量的高級用法
這裏介紹兩種變量的高級使用方法,第一種是變量值的替換。咱們能夠替換變量中的共有的部分,其格式是「$(var:a=b)」或是「${var:a=b}」,其意思是,把變量「var」中全部以「a」字串「結尾」的「a」替換成「b」字串。這裏的「結尾」意思是「空格」或是「結束符」。另一種變量替換的技術是以「靜態模式」定義的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
這依賴於被替換字串中的有相同的模式,模式中必須包含一個「%」字符,這個例子一樣讓$(bar)變量的值爲「a.c b.c c.c」。
第二種高級用法是——「把變量的值再當成變量」。
x = y
y = z
a := $($(x))
在這個例子中,$(x)的值是「y」,因此$($(x))就是$(y),因而$(a)的值就是「z」。(注意,是「x=y」,而不是「x=$(y)」)
6) override
若是有變量是一般make的命令行參數設置的,那麼Makefile中對這個變量的賦值會被忽略。若是你想在Makefile中設置這類參數的值,那麼,你可使用「override」指示符。其語法是:
override <variable> = <value>
override <variable> := <value>
固然,你還能夠追加:
override <variable> += <more text>
對於多行的變量定義, 咱們用define指示符, 在define指示符前, 也一樣可使用ovveride 指示符,如:
override define foo
bar
endef
7) 目標變量
能夠爲某個目標設置局部變量,這種變量被稱爲「Target-specific Variable」,它的做用範圍只在這條規則以及連帶規則中,因此其值也只在做用範圍內有效。而不會影響規則鏈之外的全局變量的值。
語法是:
<target ...> : <variable-assignment>
<target ...> : overide <variable-assignment>
<variable-assignment>能夠是前面講過的各類賦值表達式,如「=」、「:=」、「+=」 或是「?=」。第二個語法是針對於make命令行帶入的變量,或是系統環境變量。這個特性很是的有用,當咱們設置了這樣一個變量,這個變量會做用到由這個目標所引起的全部的規則中去。如:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
8)模式變量
模式變量的好處就是,咱們能夠給定一種「模式」,能夠把變量定義在符合這種模式的全部目標上。
make的「模式」通常是至少含有一個「%」的,因此,能夠以以下方式給全部以[.o]結尾的目標定義目標變量:
%.o : CFLAGS = -O
一樣,模式變量的語法和「目標變量」同樣:
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>
override一樣是針對於系統環境傳入的變量,或是make命令行指定的變量。
環境變量、自動變量和預約義變量。
1) 環境變量
make運行時的系統環境變量能夠在make開始運行時被載入到Makefile文件中,可是若是 Makefile中已定義了這個變量,或是這個變量由make命令行帶入,那麼系統的環境變量的值將被覆蓋。(若是make指定了「-e」參數,那麼,系統環境變量將覆蓋Makefile中定義的變量)
所以,若是咱們在環境變量中設置了「CFLAGS」環境變量,那麼咱們就能夠在全部的 Makefile中使用這個變量了。 這對於咱們使用統一的編譯參數有比較大的好處。 若是Makefile 中定義了CFLAGS,那麼則會使用Makefile中的這個變量,若是沒有定義則使用系統環境變量的值,一個共性和個性的統一,很像「全局變量」和「局部變量」的特性。
當make嵌套調用時(參見前面的「嵌套調用」章節),上層Makefile中定義的變量會以系統環境變量的方式傳遞到下層的Makefile中。固然,默認狀況下,只有經過命令行設置的變量會被傳遞,而定義在文件中的變量,若是要向下層Makefile傳遞,則須要使用exprot關鍵字來聲明。
2) 自動變量
make管理項目所使用的自動變量所有以美圓符號$開頭。
$@ Makefile文件中一條規則的目標文件名。
$< 依賴文件中的第一個
$^ Makefile文件中規則的目標所對應的全部依賴文件的列表,以空格分隔(不重複)
$? 依賴文件中新於目標的文件列表,以空格分隔
$(@D) 目標文件的目錄部分,若是目標在子目錄中。
$(@F) 目標文件的文件名部分,若是目標在子目錄中。
$* 不包含擴展名的目標文件名稱
$+ 全部依賴文件,空格隔開,前後爲序,可重複
$% 若是目標是歸檔成員(函數庫),則該變量表示目標的歸檔成員名稱。
3)預約義變量
make管理項目支持的預約義變量主要用於定義程序名,以及傳給這些程序的參數及標誌位。
AR 歸檔維護程序,缺省值爲ar。
AS 彙編程序,缺省值爲as。
CC C語言編譯程序,缺省值爲CC。
CPP C語言預處理程序,缺省值爲CPP。
RM 文件刪除程序,缺省值爲rm –f。
ARFLAGS 傳給歸檔維護程序的標誌,缺省值爲rv。
ASFLAGS 傳給彙編程序的標誌,完好省值。
CFLAGS 傳給C語言編譯的標誌,完好省值。
CPPFLAGS 傳給預處理的標誌,完好省值。
LDFLAGS 傳給連接程序的標誌,完好省值
CXX C++編譯器名稱,默認值爲g++。
CXXFLAGS C++編譯器選項。
使用條件判斷,可讓make根據運行時的不一樣狀況選擇不一樣的執行分支。條件表達式能夠是比較變量的值,或是比較變量和常量的值。
語法:
<conditional-directive>
<text-if-true>
endif
或者
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
conditional-directive表示條件關鍵字,共4個:ifeq,ifneq,ifdef,ifndef。
ifeq (<arg1>,<arg2>)
ifneq (<arg1>,<arg2>)
ifdef <variable-name>
ifndef <variable-name>
特別注意的是, make是在讀取Makefile時就計算條件表達式的值, 並根據條件表達式的值來選擇語句,因此,你最好不要把自動化變量(如「$@」等)放入條件表達式中,由於自動化變量是在運行時纔有的。 並且,爲了不混亂,make不容許把整個條件語句分紅兩部分放在不一樣的文件中。
創建makefile文件後,就可使用make命令生成和維護目標文件了。
make [options] [macrodef] [target]
options指定make的工做行爲,macrodef指定執行makefile時的宏值,目標(target)是要更新的文件列表。
options:
-C dir make開始運行以後的工做目錄爲指定目錄。若是有多個-C,後面的dir指定的目錄是相對於前一個目錄。
-d 打印除通常處理信息以外的調試信息。
-e 不容許在makefile中對環境的宏賦新值。
-f 使用指定的文件爲makefile
-i 忽略運行makefile時命令行產生的錯誤,不退出make。
-I dir 指定搜索被include的makefile的目錄。
若是命令行中有多個-I選項,按出現順序依次搜索。與其餘選項不一樣,容許dir緊跟在-I以後(不加空格),這是爲了與C預處理器兼容。
-k 執行命令出錯時,放棄當前的目標文件,儘量地維護其餘目標。
-n 按實際運行時執行順序顯示命令,包括@開頭的命令,但不真正執行。
-o file 不維護指定文件
-p 顯示makefile中全部宏定義和描述內部規則的規則(隱含規則),而後按通常狀況執行。
如只想打印信息而不真正維護,可使用make –p –f /dev/null
-q 「問題模式」。若是指令的目標目前沒有過時,就返回0,不然返回一個非零值。
不運行任何命令或打印任何信息。
-r 忽略內部規則,同時清除缺省的後綴規則。
-s 執行但不顯示執行的命令。
-S 執行makefile命令菜單時出錯即退出make。這是make的默認工做方式,通常不指定。
-t 修改每一個目標文件的建立日期,不真正從新建立文件。
-v 打印make版本號,而後正常執行。如只想打印而不維護,使用
make –v –f /dev/null
make命令執行後有三個退出碼:
0--表示成功執行
1--若是make運行時出現錯誤,返回1
2--若是使用make的「-q」選項,而且make使得一些目標不須要更新,返回2。
配置、編譯、安裝源碼包軟件
#./configure
#make
#make install
./configure比較重要的參數爲--prefix,指定軟件的安裝目錄。
在Makefile中引用其餘Makefile文件的方法是,使用include filename.mk
參考:
1. 《跟我一塊兒寫Makefile》
5. gnu make