make能夠自動推導文件及其文件依賴關係後面的命令,因此咱們沒有必要在每個.o文件後面都寫上相似的命令,由於make 會自動識別而且自動推導命令.c++
objects = main.o main.o:main.cc main:$(objects) c++ $(objects) -o main # 僞目標,防止目錄下有同名爲clean的文件形成make clean執行失敗 .PHONY:clean clean: -rm $(objects) main
objects = $(wildcard *.o)
表示將object變量定義爲全部.o文件組成的集合。在定義變量的時候,不能使用通配符,不然上述變量objects的值就爲*.o,除非使用wildcard函數.git
能夠經過include 命令引用其餘的makefile文件,以下所示:github
include foo.make *.mk
上面表示引用當前目錄下的foo.make文件以及全部後綴爲mk的文件。shell
1. 讀入全部的makefile
2. 讀入被include 的其餘makefile
3. 初始化文件中的變量
4. 推導隱晦規則,分析全部規則
5. 爲全部的目標文件建立依賴關係鏈
6. 根據依賴關係,肯定那些目標須要從新生成
7. 執行生成命令函數
爲了指明更多的文件搜索路徑,能夠定義vpath變量。vpath的定義方式有下面3種:學習
vpath %.h ../header #表示要求make在"../headers"目錄下搜索全部以".h"結尾的文件
靜態模式能夠更加容易地定義多目標的規則,可讓咱們的規則更有彈性。
語法:code
<targets ...>:<target-pattern>:<prereq-patterns> <commands>
在上面的命令中:orm
例子:排序
objects = foo.o bar.o all: $(objects) $(objects): %.o:%.c $(CC) -c $(CFLAGS) $< -o $@
上面的代碼和下述代碼等價:ip
foo.o: foo.c $(CC) -c $(CFLAGS) foo.c -o foo.o bar.o: bar.c $(CC) -c $(CFLAGS) bar.c -o bar.o
可使用filter函數去除指定模式的文件,以下所示:
files = foo.elc bar.o $(filter %.o,$(files)):%.o:%.c $(CC) -c $(CFLAGS) $< -o $@
在一些大型的工程中,咱們會把不一樣的模塊或者不一樣功能的源文件放在不一樣的目錄中,咱們能夠在每一個目錄中都書寫一個該目錄下的makefile文件,這有利於維護咱們的makefile.
例如咱們有一個子目錄叫subdir, 這個目錄下有一個makefile文件,來指明瞭這個目錄下的文件的編譯規則,那麼咱們總控的makefile文件能夠這麼寫。
subsystem: cd subdir $$(MAKE)
其等價於:
subsystem: $(MAKE) -C subdir
若是想要傳遞變量到下級makefile,可使用這樣的聲明:
export <variable ...>
若是不想讓某些變量傳遞到下級makefile中,能夠這樣聲明:
unexport <variable ...>
在定義變量的值時,可使用其它變量來構造變量的值。有下述兩種方式:
使用=定義變量可使用未定義的值,也能夠是後面定義的值,以下所示:
foo = $(bar) $bar=huh?
上面例子中$(foo)最終的值爲huh?
使用:=定義變量,必須使用當前已經定義好的變量,不能使用當前未定義的變量。
咱們可使用"+="操做符給變量追加值。例如:
objects: main.o foo.o bar.o utils.o objects+=another.o
這樣,上面例子中的$(objects)最終的值爲main.o foo.o bar.o utils.o another.o
用兩個例子來介紹這一部分的內容:
例1:
libs_for_gcc = -lgnu normal_libs = foo:$(objects) ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc) else $(CC) -o foo $(objects) $(normal_libs) endif
與ifeq相對應的有ifneq,即若是不等於
例2:
bar = foo = $(bar) ifdef foo frobozz=yes els frobozz=no endif
相似於C++中宏定義中的#ifdef
$(subst <from>,<to>,<text>) 名稱:字符串替換函數-subst函數 功能:將字符串<test>中的<from>字符串替換爲<to> 返回:函數返回被替換事後的字符串
$(patsubst <pattern>,<replacement>,<text>) 名稱:模式字符串替換函數-patsubst 功能:查找<test>中的單詞是否符合模式pattern,若是匹配,就以<replacement>進行替換,這裏的<pattern>能夠包括通配符"%"表示任意長度的字符串。
例子:
patsubst(%.c,%.o,x.c.c bar.c) 將字符串"x.c.c bar.c"中符合模式[%.c]的單詞替換爲[%.o],故返回的結果爲x.c.o bar.o
$(strip <string>) 名稱:去空格函數 功能:去掉<string>字符串中開頭和結尾的空字符串 返回:返回去掉空格的字符串值
$(findstring
名稱:查找字符串函數-findstring
功能:在字符串
返回:若是找到,返回
$(filter <pattern...>,<text>) 名稱:過濾函數 功能:以<pattern>模式過濾<text>字符串中的單詞,保留符合模式<pattern> 的單詞。能夠有多個模式
例子:
sources = foo.c bar.c baz.s ugh.h foo:$(source) cc $(filter %.c %.s,$(sources)) -o foo
其中$(filter %.c %.s,$(sources))返回的值爲"foo.c bar.c baz.s"
$(filter-out <pattern ...>,<text>) 名稱:反過濾函數-filter-out 功能:以<pattern>模式過濾<text>字符串中的單詞,去掉符合模式<pattern>的單詞,能夠有多個模式 返回:返回不符合模式<pattern>的字符串。
例子:
objects = main1.o main2.o foo.o bar.o mains=main1.o main2.o $(filter-out $(mains),$(objects)) 上述通過filter-out的返回值爲bar.o以及foo.o
$(sort <list>) 名稱:排序函數-sort 功能:給字符串<list>中的單詞排序 返回:返回排序後的字符串 示例:$(sort foo bar lose)返回"bar foo lose" 備註:sort函數會去除list之中重複的單詞
$(word <n>,<text>) 名稱:去單詞函數-word 功能:取出字符串text中的第<n>個單詞(從1開始) 返回:返回字符串<text> 中的第<n>個單詞,若<n>比 <text>中的單詞數要大,那麼返回空字符串 示例:$(word 2,foo bar baz) 返回值爲bar
$(dir <names ...>) 名稱:取目錄函數 --dir 功能:從文件名序列<names>中取出目錄部分,目錄部分指的是最後一個反斜槓 "/"以前的部分,若是沒有反斜槓,則返回「./」 返回:返回文件名序列<names>中的目錄部分 示例:$(dir src/foo.c hacks)返回值爲 "src/ ./"
$(notdir <names...>) 名稱:取文件函數 功能:從文件名序列<names>中取出非目錄的部分。菲目錄部分是最後一個/以後的部分 返回:返回文件名序列<names>中的非目錄部分 示例:$(notdir src/foo.c hacks)返回值爲"foo.c hacks"
$(suffix <names...>) 名稱:取後綴函數--suffix 功能:從文件名序列<names> 中取出各個文件名的後綴 返回:返回文件名序列<names>的後綴序列,若是文件沒有後綴,則返回空字符串。 示例:$(suffix src/foo.c src-1.0/bar.c hacks) 返回值爲".c .c"
名稱:取前綴函數--basename 功能:從文件名序列中取出各個文件名的前綴部分 返回:返回文件名序列<names>的前綴序列,若是文件沒有前綴,則返回空字符串。 示例:$(basename src/foo.c src-1.0/bar.c hacks)的返回值爲"src/foo src-1.0/bar hacks"
$(addsuffix <suffix>,<names...>) 名稱:加後綴函數-addsuffix 功能:把後綴<suffix>加到 <names>中的每一個單詞後面 返回:返回加事後綴的文件名序列 示例:$(addsuffix .c,foo bar)返回值爲"foo.c bar.c"
$(addprefix <suffix>,<names...>) 名稱:加後綴函數--addsuffix 功能:把後綴<suffix>加到<names>中的每一個單詞後面。 返回:返回加事後綴的文件名序列 示例:$(addprefix .c,foo bar)返回值爲"foo.c bar.c"
$(join <list1>,<list2>) 名稱:鏈接函數--join 功能:把<list2>中的單詞相應地添加到<list1>的單詞後面。若是<list1>中的單詞個數要比<list2>多,那麼<list1>中多出來的單詞將保持原樣。若是<list2>中的單詞數量要比<list1>多,那麼<list2>中多出來的單詞要被複制到<list1>中。 返回:返回鏈接事後的字符串 示例:$(join aaa bbb,111 222 333) 返回值爲"aaa111,bbb222,3333"
foreach函數用於循環。語法以下:
$(foreach <var>,<list>,<text>) 這個函數的意思是,將<list>中的單詞逐一取出放到參數<var>所指定的變量之中,而後再執行<text>所包含的表達式。每次<text>會返回一個字符串,循環過程當中,<text>返回的每一個字符會以空格分隔,當循環結束的時候,<text> 返回的每一個字符串所組成的整個字符串會是foreach函數的返回值。
例子:
names:=a b c d files := $(foreach n,$(names),$(n).o) 執行完畢後,變量$(files)的值爲"a.o b.o c.o d.o"
1.$(if <condition>, <then-part>) 2.$(if <condition>,<then-part>,<else-part>) if函數能夠包含else或者不包含。即if函數的參數能夠是兩個,也能夠是3個。<condition>參數是if的表達式,若是器返回爲非空字符串,那麼這個表達式就爲真,因而<then-part>會被計算,不然<else-part>會被計算。 若是<condition>爲真,那麼<then-part>會爲整個函數的返回值,不然<else-part>會做爲函數的返回值。若是<else-part>沒有定義,那麼函數會返回空字符串。
shell函數將執行shell命令的返回值做爲函數返回值。
例子:
contents:=$(shell cat foo) files:=$(shell echo *.c)
經過構建僞目標all的方式來同時指定多個目標爲終極目標。
例子:
.PHONY:all all:exec1 exec2 這個makefile的終極目標由exec1和exec2構成
foo:foo.o bar.o cc- o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
咱們注意到,上述makefile沒有寫下如何生成foo.o和bar.o這兩個目標的規則和命令,由於make的隱含規則功能爲咱們自動推導這兩個目標的依賴目標。
<n>.o的依賴目標會自動推導爲<n>.c,而且其生成命令爲 $(CC) -c $(CPPFLAGS) $(CFLAGS)
<n>.o的依賴目標會自動推導爲<n>.c或者<n>.cc,而且其生成命令爲 $(CC) -c $(CPPFLAGS) $(CXXFLAGS)
所以建議使用.cc而不是.c或者.cpp做爲C++程序的後綴。
對於下述makefile
x:y.o z.o
若是y.c 與 z.c文件都存在,隱含規則會執行下述命令:
cc -c x.c -o x.o cc -c y.c -o y.o cc -c z.c -o z.o cc x.o y.o z.o -o x rm -f x.o rm -f y.o rm -f z.o
舉例說明,下述makefile文件將全部的目錄下全部的.c文件都編譯成爲.o文件.
%.o:%.c $(CC) -c $(CFLAGS) $< -o $@
上述makefile自定義了一個針對.o文件的隱含規則
foo.a(bar.o)
,那麼$%表示bar.o, $@ 表示foo.a。可使用下述格式來指定函數庫文件及其組成。
archive(member)
例子:
foolib(hack.o foo.o):hack.o foo.o ar cr foolib hack.o foo.o
上述命令在文件foo.o和hack.o文件的基礎之上構建了foolib.a文件。
在進行函數庫打包的過程當中,若多個ar命令同時運行在同一個函數庫打包文件時,可能損壞這個函數庫文件,因此對於這種狀況要謹慎使用make -j 命令。
函數庫在生成過程當中可能也會觸發.o文件的隱含規則