make 要點簡記

make 要點簡記

1.隱式推導

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

2.在規則中使用通配符

objects = $(wildcard *.o)表示將object變量定義爲全部.o文件組成的集合。在定義變量的時候,不能使用通配符,不然上述變量objects的值就爲*.o,除非使用wildcard函數.git

3.引用其餘的makefile文件

能夠經過include 命令引用其餘的makefile文件,以下所示:github

include foo.make *.mk

上面表示引用當前目錄下的foo.make文件以及全部後綴爲mk的文件。shell

4.make 的工做方式

1. 讀入全部的makefile
2. 讀入被include 的其餘makefile
3. 初始化文件中的變量
4. 推導隱晦規則,分析全部規則
5. 爲全部的目標文件建立依賴關係鏈
6. 根據依賴關係,肯定那些目標須要從新生成
7. 執行生成命令函數

5.文件搜尋

爲了指明更多的文件搜索路徑,能夠定義vpath變量。vpath的定義方式有下面3種:學習

  1. vpath 爲符合模式pattern的文件指定搜索路徑爲directories
  2. vpath 清除符合模式爲pattern的文件搜索路徑
  3. vpath 清除全部已經設置的文件搜索路徑
vpath %.h ../header #表示要求make在"../headers"目錄下搜索全部以".h"結尾的文件

6.靜態模式

靜態模式能夠更加容易地定義多目標的規則,可讓咱們的規則更有彈性。
語法:code

<targets ...>:<target-pattern>:<prereq-patterns>
    <commands>

在上面的命令中:orm

  1. targets定義了一系列的目標文件,是目標的一個集合,能夠有通配符。
  2. target-pattern指明瞭target的模式,也就是目標集模式。
  3. prereq-patterns是目標的依賴模式,它是對target-pattern造成的模式再進行一次依賴目標的定義。

例子:排序

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 $@

7.嵌套執行make

在一些大型的工程中,咱們會把不一樣的模塊或者不一樣功能的源文件放在不一樣的目錄中,咱們能夠在每一個目錄中都書寫一個該目錄下的makefile文件,這有利於維護咱們的makefile.

例如咱們有一個子目錄叫subdir, 這個目錄下有一個makefile文件,來指明瞭這個目錄下的文件的編譯規則,那麼咱們總控的makefile文件能夠這麼寫。

subsystem:
    cd subdir $$(MAKE)

其等價於:

subsystem:
    $(MAKE) -C subdir

若是想要傳遞變量到下級makefile,可使用這樣的聲明:

export <variable ...>

若是不想讓某些變量傳遞到下級makefile中,能夠這樣聲明:

unexport <variable ...>

8.變量中的變量

在定義變量的值時,可使用其它變量來構造變量的值。有下述兩種方式:

1. 採用=定義變量

使用=定義變量可使用未定義的值,也能夠是後面定義的值,以下所示:

foo = $(bar)
$bar=huh?

上面例子中$(foo)最終的值爲huh?

2.採用:=定義變量

使用:=定義變量,必須使用當前已經定義好的變量,不能使用當前未定義的變量。

9.追加變量值

咱們可使用"+="操做符給變量追加值。例如:

objects: main.o foo.o bar.o utils.o
objects+=another.o

這樣,上面例子中的$(objects)最終的值爲main.o foo.o bar.o utils.o another.o

10.使用條件判斷

用兩個例子來介紹這一部分的內容:
例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

11.函數

1.字符串處理函數
1.subst
$(subst <from>,<to>,<text>)
名稱:字符串替換函數-subst函數
功能:將字符串<test>中的<from>字符串替換爲<to>
返回:函數返回被替換事後的字符串 
2.patsubst
$(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
3.strip
$(strip <string>)
名稱:去空格函數
功能:去掉<string>字符串中開頭和結尾的空字符串
返回:返回去掉空格的字符串值
4.findstring

$(findstring , )
名稱:查找字符串函數-findstring
功能:在字符串 中查找 字符串
返回:若是找到,返回 ,不然返回空字符串

5.filter
$(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"

6.filter-out
$(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
7.sort
$(sort <list>)
名稱:排序函數-sort
功能:給字符串<list>中的單詞排序
返回:返回排序後的字符串
示例:$(sort foo bar lose)返回"bar foo lose"
備註:sort函數會去除list之中重複的單詞
8.word
$(word <n>,<text>)
名稱:去單詞函數-word
功能:取出字符串text中的第<n>個單詞(從1開始)
返回:返回字符串<text> 中的第<n>個單詞,若<n>比 <text>中的單詞數要大,那麼返回空字符串
示例:$(word 2,foo bar baz) 返回值爲bar
2.文件名操做函數
1.dir
$(dir <names ...>)
名稱:取目錄函數 --dir
功能:從文件名序列<names>中取出目錄部分,目錄部分指的是最後一個反斜槓 "/"以前的部分,若是沒有反斜槓,則返回「./」
返回:返回文件名序列<names>中的目錄部分
示例:$(dir src/foo.c hacks)返回值爲 "src/ ./"
2.notdir
$(notdir <names...>)
名稱:取文件函數
功能:從文件名序列<names>中取出非目錄的部分。菲目錄部分是最後一個/以後的部分
返回:返回文件名序列<names>中的非目錄部分
示例:$(notdir src/foo.c hacks)返回值爲"foo.c hacks"
3.suffix
$(suffix <names...>)
名稱:取後綴函數--suffix
功能:從文件名序列<names> 中取出各個文件名的後綴
返回:返回文件名序列<names>的後綴序列,若是文件沒有後綴,則返回空字符串。
示例:$(suffix src/foo.c src-1.0/bar.c hacks) 返回值爲".c .c"
4.basename
名稱:取前綴函數--basename
功能:從文件名序列中取出各個文件名的前綴部分
返回:返回文件名序列<names>的前綴序列,若是文件沒有前綴,則返回空字符串。
示例:$(basename src/foo.c src-1.0/bar.c hacks)的返回值爲"src/foo src-1.0/bar hacks"
5.addsufix
$(addsuffix <suffix>,<names...>)
名稱:加後綴函數-addsuffix
功能:把後綴<suffix>加到 <names>中的每一個單詞後面
返回:返回加事後綴的文件名序列
示例:$(addsuffix .c,foo bar)返回值爲"foo.c bar.c"
6.addprefix
$(addprefix <suffix>,<names...>)
名稱:加後綴函數--addsuffix
功能:把後綴<suffix>加到<names>中的每一個單詞後面。
返回:返回加事後綴的文件名序列
示例:$(addprefix .c,foo bar)返回值爲"foo.c bar.c"
7.join
$(join <list1>,<list2>)
名稱:鏈接函數--join
功能:把<list2>中的單詞相應地添加到<list1>的單詞後面。若是<list1>中的單詞個數要比<list2>多,那麼<list1>中多出來的單詞將保持原樣。若是<list2>中的單詞數量要比<list1>多,那麼<list2>中多出來的單詞要被複制到<list1>中。 
返回:返回鏈接事後的字符串 
示例:$(join aaa bbb,111 222 333) 返回值爲"aaa111,bbb222,3333"
3.foreach函數

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"
4.if函數
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>沒有定義,那麼函數會返回空字符串。
5.shell函數

shell函數將執行shell命令的返回值做爲函數返回值。
例子:

contents:=$(shell cat foo)
files:=$(shell echo *.c)

12.構建多個目標

經過構建僞目標all的方式來同時指定多個目標爲終極目標。
例子:

.PHONY:all
all:exec1 exec2
這個makefile的終極目標由exec1和exec2構成

13.隱含規則

foo:foo.o bar.o
cc- o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

咱們注意到,上述makefile沒有寫下如何生成foo.o和bar.o這兩個目標的規則和命令,由於make的隱含規則功能爲咱們自動推導這兩個目標的依賴目標。

隱含規則一覽
1.C程序

<n>.o的依賴目標會自動推導爲<n>.c,而且其生成命令爲 $(CC) -c $(CPPFLAGS) $(CFLAGS)

2.C++程序

<n>.o的依賴目標會自動推導爲<n>.c或者<n>.cc,而且其生成命令爲 $(CC) -c $(CPPFLAGS) $(CXXFLAGS)

所以建議使用.cc而不是.c或者.cpp做爲C++程序的後綴。

連接object文件的隱含規則

對於下述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

14.隱含規則使用的變量

命令
  1. AR 函數庫打包的程序,默認爲ar
  2. AS 彙編語言編譯程序,默認爲as
  3. CC C語言編譯程序,默認爲"cc"
  4. CXX C++語言編譯程序,默認爲"g++"
  5. RM 刪除文件命令,默認爲rm
參數
  1. ARFLAGS 函數庫打包程序AR命令的參數,默認爲rv
  2. ASFLAGS 彙編語言編譯器參數
  3. CFLAGS C語言編譯器參數
  4. CXXFLAGS C++語言編譯器參數
  5. CPPFLAGS C預處理器參數
  6. LDFLAGS 連接器參數

15.自定義模式規則

舉例說明,下述makefile文件將全部的目錄下全部的.c文件都編譯成爲.o文件.

%.o:%.c
    $(CC) -c $(CFLAGS) $< -o $@

上述makefile自定義了一個針對.o文件的隱含規則

16.自動化變量

  1. $@ 表示規則中的目標文件集,在模式規則中,若是有多個目標,那麼 $@ 就是匹配於目標中模式定義的集合。
  2. $% 僅當目標是函數庫文件時,表示規則中的目標成員名。例如一個目標是foo.a(bar.o),那麼$%表示bar.o, $@ 表示foo.a。
  3. $< 依賴目標中的第一個目標文字。若是依賴目標是以模式%定義的,那麼$<將是符合模式的一系列文件集,請注意是一個一個取出來的。
  4. $? 全部比目標新的依賴目標的幾個,以空格分隔。
  5. $^ 全部依賴目標的集合。以空格分隔。若是在依賴目標中有多個重複的。那麼這個變量會去除重複的依賴目標,只保留一份。
  6. $+ 是全部依賴目標的集合,可是不去除重複的依賴目標。

17.使用make更新函數庫文件

可使用下述格式來指定函數庫文件及其組成。

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文件的隱含規則

18. 上述學習過程及造成的代碼保存在個人代碼倉庫了,歡迎訪問!

makefile

相關文章
相關標籤/搜索