1 Makefile
1.1 Makefile運行順序
1、讀入所有的 Makefile。
2、讀入被 include 的其它 Makefile。
3、初始化文件中的變量。
4、推導隱晦規則,並分析所有規則。
5、爲所有的目標文件創建依賴關係鏈。
6、根據依賴關係,決定哪些目標要重新生成。
7、執行生成命令。
1-5步爲第一個階段,6-7 爲第二個階段。第一個階段中,如果定義
的變量被使用了,那麼,make 會把其展開在使用的位置。但 make
並不會完全馬上展開,make 使用的是拖延戰術,如果變量出現在依
賴關係的規則中,那麼僅當這條依賴被決定要使用了,變量纔會在其
內部展開。
1.2 Makefile顯式規則
target ... : prerequisites ...
command
...
...
例如
target:目標文件1 目標文件2 目標文件3
<tab> gcc –o 欲建立的執行文件目標文件1 目標文件2 目標文件3
target: 是一個目標文件;可以是 Object File,也可以是執行文件;還可以是一個標籤(Label);
prerequisites: 要生成那個target 所需要的文件或是目標
command: 也就是 make 需要執行的命令(任意的Shell 命令)
1.3 引用其他的makefile
使用 include 關鍵字可以把別的 Makefile 包含進來,這很像C語言的#include,被包含的文件會原模原樣的放在當前文件的包含位置。
include 的語法是:
include <filename>
filename 可以是當前操作系統 Shell 的文件模式(可以保含路徑和通配符)
注意!!!在 include 前面可以有一些空字符, 但是絕不能是[Tab]鍵開始。 include和<filename>可以用一個或多個空格隔開
1.4 一些注意的符號
注意!!!
1,- :命令前面加了一個小減號,標記爲不管命令出不出錯都認爲是成功的
2,@ :用“@”字符在命令行前,這個命令將不被 make 顯示出來
3,$< :自動化變量,表示所有的依賴目標集
4,$@ :自動化變量,表示目標集
5,$$$$ :意爲一個隨機編號
6,$(CFLAGS) 編譯參數。如果沒有定義就是空的。CFLAGS主要是指makefile中的隱式規則裏會用到的常見預定義變量,是C編譯器的選項
1.5 Make的參數
1,-n:只顯示命令,但不執行命令,利於調試Makefile,看看命令執行起來是什麼順序
2,-s:全面禁止命令的顯示
3,-f:運行指定的 Makefile,例如:make -f make.linux
4,-i:Makefile 中所有命令都會忽略錯誤
5,-k:如果某規則中的命令出錯了,那麼就終目該規則的執行,但繼續執行其它規則
1.6 文件搜尋目錄
在一些大的工程中,有大量的源文件,我們通常的做法是把這許多的源文件分類,並存放在不同的目錄中。所以,當 make 需要去找尋文件的依賴關係時,你可以在文件前加上路徑,但最好的方法是把一個路徑告訴 make,讓 make 在自動去找。
Makefile文件中的特殊變量“VPATH”就是完成這個功能的,如果沒有指明這個變量,make只會在當前的目錄中去找尋依賴文件和目標文件。如果定義了這個變量,那麼,make就會在噹噹前目錄找不到的情況下,到所指定的目錄中去找尋文件了。
VPATH = src:../headers
上面的的定義指定兩個目錄,“src”和“../headers”,make會按照這個順序進行搜索。目錄由“冒號”分隔。(當然,當前目錄永遠是最高優先搜索的地方)
另一個設置文件搜索路徑的方法是使用make的“vpath”關鍵字(注意,它是全小寫的),這不是變量,這是一個make的關鍵字,它的使用方法有三種:
vpath <pattern> <directories>
爲符合模式<pattern>的文件指定搜索目錄<directories>
vpath <pattern>
清除符合模式<pattern>的文件的搜索目錄。
vpath
清除所有已被設置好了的文件搜索目錄。
vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”結尾的文件。<pattern>指定了要搜索的文件集,而<directories>則指定了<pattern>的文件集的搜索的目錄。例如:
vpath %.h ../headers 該語句表示,要求make在“../headers”目錄下搜索所有以“.h”結尾的文件。(如果某文件在當前目錄沒有找到的話)
vpath %.h ../headers:work 該語句表示,要求make在“../headers”和work 兩個目錄下搜索所有以“.h”結尾的文件。(如果某文件在當前目錄沒有找到的話)
我們可以連續地使用 vpath 語句,以指定不同搜索策略。如果連續的vpath 語句中出現了相同的<pattern>,或是被重複了的<pattern>,那麼,make 會按照 vpath 語句的先後順序來執行搜索
1.7 僞目標
“僞目標”並不是一個文件,只是一個標籤,由於“僞目標”不是文件,所以make無法生成它的依賴關係和決定它是否要執行;爲了避免和文件重名的這種情況,我們可以使用一個特殊的標記“.PHONY”來顯示地指明一個目標是“僞目標”
.PHONY:clean
clean :
rm edit $(OBJECTS)
僞目標作爲依賴;目的:不同的指令清除不同的目標;可以輸入“make cleanall”和
“make cleanobj”和“make cleandiff”命令來達到清除不同種類文件的目的
.PHONY:cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
注意!!!
可以使用 依賴一個僞目標,這樣每次編譯時因爲ALWAYSMAKE不存在,都會執行依賴後的命令
1.8 靜態模式
需要重點看下!!!
靜態模式可以更加容易地定義多目標的規則,可以讓我們的規則變得更加的有彈性和靈活。
<targets ...>: <target-pattern>:<prereq-patterns ...>
<commands>
..
targets定義了一系列的目標文件,可以有通配符。是目標的一個集合(目標集)。
target-parrtern是指明瞭targets的模式,也就是的目標集模式(目標模式)。
prereq-parrterns是目標的依賴模式(依賴模式),它對target-parrtern形成的模式再進行一次依賴目標的定義。
上面的模式爲生成一個新集合(依賴目標集或者依賴集)
還是舉個例子來說明一下吧。如果我們的<target-parrtern>定義成“%.o”,意思是我們的<target>集合中都是以“.o”結尾的,而如果我們的<prereq-parrterns>定義成“%.c”,意思是對<target-parrtern>所形成的目標集進行二次定義,其計算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]這個結尾),併爲其加上[.c]這個結尾,形成的新集合(依賴目標集)。
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
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”集,
上面的例子中,指明瞭我們的目標從$object中獲取,“%.o”表明要所有以“.o”結尾的目標,也就是“foo.o bar.o”,也就是變量$object集合的模式,而依賴模式“%.c”則取模式“%.o”的“%”,也就是“foo bar”,併爲其加下“.c”的後綴,於是,我們的依賴目標就是“foo.c bar.c”。而命令中的“$<”和“$@”則是自動化變量,“$<”表示所有的依賴目標集(也就是“foo.c bar.c”),“$@”表示目標集(也就是“foo.o bar.o”)。
1.8.1.1 嵌套執行 make
在一些大的工程中,我們會把我們不同模塊或是不同功能的源文件放在不同的目錄中,我們可以在每個目錄中都書寫一個該目錄的Makefile,這有利於讓我們的Makefile變得更加地簡潔,而不至於把所有的東西全部寫在一個Makefile中,這樣會很難維護我們的Makefile,這個技術對於我們模塊編譯和分段編譯有着非常大的好處。
我們有一個子目錄叫subdir,這個目錄下有個Makefile文件,來指明瞭這個目錄下文件的編譯規則。那麼我們總控的Makefile可以這樣書寫:
subsystem:
cd subdir && $(MAKE)
其等價於:
subsystem:
$(MAKE) -C subdir
定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數,所以定義成一個變量比較利於維護。這兩個例子的意思都是先進入“subdir”目錄,然後執行make命令。
如果你要傳遞變量到下級Makefile中,那麼你可以使用這樣的聲明:
export <variable ...>
否則
unexport <variable ...>
export variable = value
其等價於:
variable = value
export variable
其等價於:
export variable := value
其等價於:
variable := value
export variable
如果你要傳遞所有的變量,那麼,只要一個export就行了。後面什麼也不用跟,表示傳遞所有的變量。
“-w” 輸出當前的工作目錄;
如果我們的下級make目錄是“/home/hchen/gnu/make”,如果我們使用“make -w”來執行,那麼當進入該目錄時,我們會看到:
make: Entering directory `/home/hchen/gnu/make'.
而在完成下層make後離開目錄時,我們會看到:
make: Leaving directory `/home/hchen/gnu/make'
當你使用“-C”參數來指定make下層Makefile時,“-w”會被自動打開的。如果參數中有“-s”(“--slient”)或是“--no-print-directory”,那麼,“-w”總是失效的。
1.9 Makefile變量
1.9.1 定義變量
變量的命名字可以包含字符、數字,下劃線(可以是數字開頭),但不應該含有“:”、“#”、“=”或是空字符(空格、回車等)。使用寫法(MAKEFLAGS)。
變量使用:
$ MAKEFLAGS
$(MAKEFLAGS)同一使用此種模式,包括在shell中
${ MAKEFLAGS }
如果要使用真實的“$”字符,那麼需要用“$$”來表示。
1.9.2 定義命令包
這種命令序列的語法以“define”開始,以“endef”結束
1.9.3 變量中的變量
1,”=”
MyName=$(Name)
Name=wzt
變量是可以使用後面的變量來定義的(遞歸調用容易出問題)
2,”:=”
Name:=wzt
MyName:=$(Name)
前面的變量不能使用後面的變量,只能使用前面已定義好了的變量
推薦使用此種方法
3,”?=”
如果 FOO 沒有被定義過,那麼變量 FOO 的值就是“bar”
如果 FOO 先前被定義過,那麼這條語將什麼也不做
4,”+=”
追加變量值
objects =main.o foo.o bar.o utils.o
objects +=another.o
於是,我們的$(objects)值變成:“main.o foo.o bar.o utils.o another.o”
(another.o 被追加進去了)
如果變量之前沒有定義過,那麼,“+=”會自動變成“=”,如果前面有變量定義,那麼“+=”會繼承於前次操作的賦值符。如果前一次的是“:=”,那麼“+=”會以“:=”作爲其賦值符,
1.9.4 多行變量
define 指示符後面跟的是變量的名字,而重起一行定義變量的值,定義是以 endef 關鍵字結束。其工作方式和“=”操作符一樣。變量的值可以包含函數、命令、文字,或是其它變量。因爲命令需要以[Tab]鍵開頭,所以如果你用 define 定義的命令變量中沒有以[Tab]鍵開頭,那麼 make 就不會把其認爲是命令
definetwo-lines
echo foo
echo $(bar)
endef
1.9.5 變量高級用法
1,變量值的替換(類似靜態模式)
$(var:a=b)
把變量“var”中所有以“a”字串“結尾”的“a”替換成“b”字串。這裏的“結尾”意思是“空格”或是“結束符”
2,把變量的值再當成變量
x = y
y = z
a := $($(x))
有$(a)=z
1.9.6 目標變量
可以爲某個目標設置局部變量,這種變量被稱爲“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
prog.o: prog.c
$(CC)$(CFLAGS) prog.c
foo.o: foo.c
$(CC)$(CFLAGS) foo.c
bar.o: bar.c
$(CC)$(CFLAGS) bar.c
在這個示例中,不管全局的$(CFLAGS)的值是什麼,在 prog 目標,以及其所引發的所有規則中(prog.o foo.o bar.o 的規則) ,$(CFLAGS) 的值都是“-g”
1.9.7 模式變量
我們知道,make 的“模式”一般是至少含有一個“%”的,所以,我們可以以如下方式給所有以[.o]結尾的目標定義目標變量:
%.o: CFLAGS = -O
同樣,模式變量的語法和“目標變量”一樣:
<pattern...> : <variable-assignment>
<pattern...> : override <variable-assignment>
override同樣是針對於系統環境傳入的變量,或是make 命令行指定的變量
1.10 條件判斷
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
ifeq(<arg1>, <arg2> )
ifeq'<arg1>' '<arg2>'
ifeq"<arg1>" "<arg2>"
ifeq"<arg1>" '<arg2>'
ifeq'<arg1>' "<arg2>"
比較參數“arg1”和“arg2”的值是否相同
<conditional-directive>表示條件關鍵字,如“ifeq”。這個關鍵字有四個。
1,ifeq “arg1”和“arg2”的值相同爲真
2,ifneq “arg1”和“arg2”的值不同爲真
3,ifdef
語法:
ifdef <variable-name>
如果變量<variable-name>的值非空,那到表達式爲真。否則,表達式爲假,
ifdef 只是測試一個變量是否有值,其並不會把變量擴展到當前位置。
4,ifndef
在<conditional-directive>這一行上,多餘的空格是被允許的,但是不能以[Tab]鍵做爲開始(不然就被認爲是命令) 。而註釋符“#”同樣也是安全的。“else”和“endif”也一樣,只要不是以[Tab]鍵開始就行了。
特別注意的是,make 是在讀取 Makefile 時就計算條件表達式的值,並根據條件表達式的值來選擇語句,所以,你最好不要把自動化變量(如“$@”等)放入條件表達式中,因爲自動化變量是在運行時纔有的。
1.11 常用函數
函數調用,很像變量的使用,也是以“$”來標識的,其語法爲:$( )或${ }。
1.11.1 wildcard
原型:$(wildcard PATTERN)
功能:獲取匹配模式的文件名
說明:這個函數的功能是查找當前目錄下所有符合模式 PATTERN 的文件名,其返回值是以空格分割的,當前目錄下的所有符合模式 PATTERN 的文件名列表。
例如:
如下模式返回當前目錄下所有擴展名位 .c 的文件列表。
$(wildcard *.c)
1.11.2 patsubst
原型:$(patsubst pattern, replacement, text)
功能:模式替換函數
說明:函數功能是查找字符串 text 中按照空格分開的單詞,將符合模式 pattern 的字符串替換成 replacement。 Pattern 中的模式可以使用通配符,當 pattern 和 replacement 中都有 % 時,符合條件的字符將被 replacement 中的替換。函數的返回值是替換後的新字符串。
例如:
需要將 C 文件替換爲 .o 的目標文件可以使用如下模式:
$(patsubst%.c, %.o, add.c)
上面的模式將 add.c 字符串作爲輸入,當擴展名爲 .c 時符合模式 %.c ,其中 % 在這裏代表 add,替換爲 add.o,並作爲輸出字符串。
$(patsubst%.c, %.o, $(wildcard *.c))
輸出的字符串將當前擴展名爲 .c 的文件替換成 .o 的文件列表。
1.11.3 foreach
原型:$(foreach VAR, LIST, TEXT)
功能:循環函數
說明: foreach 將 LIST 字符串中一個空格分割的單詞,先傳給臨時變量 VAR ,然後執行 TEXT 表達式,TEXT 表達式處理結束後輸出。其返回值是空格分割表達式 TEXT 的計算結果。
例如:
對於存在 add 和 sub 的兩個目錄,設置 DIRS 爲 "add sub ./" 包含目錄 add、sub 和當前目錄。表達式$(wildcard $(dir)/*.c) ,可以取出目錄 add 和 sub 及當前目錄中的所有擴展名爲 .c 的C語言源文件:
DIRS = subadd ./
FILES =$(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))
1.11.4 filter
原型:$(filter PATTERN…,TEXT)
功能:過濾函數
說明:過濾掉字串“TEXT”中所有不符合模式“PATTERN”的單詞,保留所有符合此模式的單詞。可以使用多個模式。模式中一般需要包含模式字符“%”。存在多個模式時,模式表達式之間使用空格分割。一般用來去除一個變量中的某些字符串
返回值:空格分割的“TEXT”字串中所有符合模式“PATTERN”的字串。
例如:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
使用“$(filter %.c%.s,$(sources))”的返回值給 cc 來編譯生成目標“foo”,函數返回
值爲“foo.c bar.c baz.s”
1.11.5 subst
原型:$(subst FROM, TO, TEXT),
功能:替換函數
說明:即將字符串TEXT中的子串FROM變爲TO