最近兩天花時(shí)間讀了一下陳皓老師的《跟我一起寫Makefile》,這篇文章非常地詳細(xì)地介紹了make的用法以及Makefile的常用寫法,感謝耗子老師~本文是在學(xué)習(xí)陳皓的《跟我一起寫Makefile》的過(guò)程中,對(duì)一些比較實(shí)用的內(nèi)容做的筆記绢记。
Makefile介紹:
-
Makefile的規(guī)則
target...:prerequisites... command ... ...
1.target可以是目標(biāo)文件、可執(zhí)行文件炉旷、偽目標(biāo)
2.prerequisites是生成target所需要的文件或目標(biāo)
3.command是需要執(zhí)行的命令窘行,其所在行必須以一個(gè)tab開始
4.核心規(guī)則:如果prerequisites中有一個(gè)或以上的文件比target更新的話,那么command所定義的命令就會(huì)被執(zhí)行
make會(huì)在makefile里尋找第一個(gè)目標(biāo)文件,如果找到則將其作為最終的目標(biāo)文件
makefile的變量是一個(gè)字符串,類似于C語(yǔ)言中的宏
-
make具有自動(dòng)推導(dǎo)的功能罐盔,比如有一個(gè)目標(biāo)是any.o,那么any.c就會(huì)是any.o的依賴文件(如果我們沒(méi)有定義這個(gè)規(guī)則)。還有下面這個(gè)例子:
objects = main.o first.o second.o \ third.o end.o edit : $(objects) cc -o edit $(objects)
makefile中只有行注釋,使用'#'字符
include關(guān)鍵字可以把別的makefile包含進(jìn)來(lái):
include filename;
如果文件沒(méi)有指定絕對(duì)或相對(duì)路徑,make會(huì)在當(dāng)前目錄下尋找,然后試圖在指定的編譯參數(shù)里尋找包含路徑
書寫規(guī)則:
在makefile中,'-'號(hào)一般用來(lái)表示出錯(cuò)時(shí)不要退出,繼續(xù)執(zhí)行
make支持三個(gè)通配符:"*" "?" "~"
-
如果想要讓通配符在變量中展開,比如objects是所有.o文件名的集合,可以這樣:
objects:=$(wildcard \*.o)
Makefile中的特殊變量VPATH用來(lái)添加文件路徑,多個(gè)路徑間以:號(hào)分隔六孵,例如:
VPATH = src:../headers
全小寫的vpath是make的關(guān)鍵字劫窒,它可以指定不同文件在不同的目錄中主巍。其用法如下:
- vpath pattern directories 為符合pattern模式的文件指定搜索目錄directories
- vpath pattern 清除符合模式pattern的文件的搜索目錄
- vpath 清除所有已經(jīng)設(shè)置了的文件搜索目錄
vpath中的pattern需要包含%,比如:
vpath %.h ../headers 要求make在"../headers"目錄下搜索所有頭文件(如果當(dāng)前目錄沒(méi)有的話)
- .PHONY是一個(gè)特殊標(biāo)記,顯式地指出一個(gè)目標(biāo)是偽目標(biāo),這樣make不會(huì)認(rèn)為其是一個(gè)文件去尋找其生成規(guī)則了
- 目標(biāo)和偽目標(biāo)都可以成為依賴挪凑,比如:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
$@這個(gè)變量表示著目前規(guī)則中所有目標(biāo)的集合
-
靜態(tài)模式的語(yǔ)法:
<target ...> : <target-pattern> : <prereq-patterns ...>
...
targets定義一系列目標(biāo),可以有通配符,是目標(biāo)的一個(gè)集合
target-pattern是指明targets的模式,也就是目標(biāo)集模式
prereq-patterns是目標(biāo)的依賴模式,它對(duì)target-pattern形成的模式再進(jìn)行一次依賴目標(biāo)的定義.
例子:
objects = foo.o bar.o all: $(objects) $(objects): %.o : %.c $(CC) -c $(CFLAGS) $< -o $@
其中$<表示所有依賴的目標(biāo)集(foo.c bar.c), $@表示所有的目標(biāo)集(foo.o bar.o)
書寫命令:
- make通常會(huì)將執(zhí)行的命令輸出到屏幕上,在命令前加@可以阻止這個(gè)輸出
- 如果有多條命令并且后面的命令需要用到前面的接鍋,需要卸載同一行上檬果,之間用;隔開
例如:exec: cd /home/david pwd exec: cd /home/david; pwd
執(zhí)行第一個(gè)exec, pwd打印當(dāng)前目錄,第二個(gè)是/home/david
- 如果我們有一個(gè)子目錄叫subdir,在總的makefile里可以這樣寫:
或者是:subsys: cd subdir; $(MAKE)
subsys: $(MAKE) -C subdir
- 如果要給下層makefile傳遞參數(shù),可以這樣:
export var=value
或者:
var=value
export
如果要傳遞所有變量,只需要export就可以了选脊。如果要取消變量,使用unexport恳啥。
- SHELL和MAKEFLAGS這兩個(gè)變量不管是否export,都要傳遞到下層的makefile中去
- make的-w參數(shù)在嵌套執(zhí)行中比較有用,作用是讓我們看到當(dāng)前的工作目錄.使用-C參數(shù)時(shí)-w會(huì)自動(dòng)打開
- make可以將相同的命令序列定義為一個(gè)變量:
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
使用變量
- 如果要在Makefile中使用"$"字符,需要用"$$"表示
- 定義變量可以直接用=,也可以用:=钝的。兩者的區(qū)別在于:=使用時(shí),- - 只能用已經(jīng)定義號(hào)的變量,而不是在使用時(shí)再賦值
?=操作符表示如果左邊的變量未定義時(shí)將右值賦給它,否則什么也不做.例如:
nullstr:=
space:=$(nullstr) #end of the line
這里需要注意的是,nullstr是空變量,space這行中,最后的)和#之間有一個(gè)空格,所以space被定義成了空格硝桩。
- 變量值的替換:
${var:a=b}表示將var中以a結(jié)尾的內(nèi)容替換成b碗脊,比如:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
那么${bar} = a.c b.c c.c,上面一句等同于:
bar := ${foo:%.o=%.c}
- +=號(hào)左邊的變量如果沒(méi)有定義過(guò),等同于=
- 如果一個(gè)變量在前面有定義,那么同一文件里賦值給該變量的=號(hào)會(huì)繼承前一次操作的賦值符,也就是=或:=
使用用條件判斷
- 條件表達(dá)式的語(yǔ)法:
conditional-directive
text-if-true
endif
以及:
conditional-directive
text-if-true
else
text-if-false
endif
其中conditional-directive表示條件關(guān)鍵字,包括:
ifeq (arg1, arg2)
ifneq (arg1, arg2)
ifdef var
ifndef var
需要注意的是,ifdef只是測(cè)試一個(gè)變量是否有值,并不會(huì)擴(kuò)展它。也就是說(shuō)如果你定義了一個(gè)空的變量empvar,ifdef empvar 返回true
使用函數(shù)
-
函數(shù)調(diào)用語(yǔ)法:
$(function args)
函數(shù)和參數(shù)之間有空格害碾,多個(gè)參數(shù)之間用,隔開
字符串操作函數(shù)
$(subst from,to,text)
將text中的from替換為to
$(patsubst pattern,replacement,text)
查找text中的單詞是否符合模式pattern,如果匹配則以replacement替換.pattern可以包括通配符%表示任意長(zhǎng)度的字符串.如果replace中也包含%,那么pattern中%代表的字符串和replace中一樣慌随,返回替換過(guò)的字符串
$(strip string)
去掉string中開頭和結(jié)尾的空字符, 返回去掉空格的字符串
$(findstring find,in)
在in中查找find字符串,找到則返回find,否則返回空字符串
$(filter pattern...,text)
以pattern模式過(guò)濾text中的單詞,返回符合模式的字符串
$(filter-out pattern..., text)
與filter相反
$(sort list)
給list中的單詞按照升序排序,會(huì)去掉重復(fù)單詞
$(word n,text)
取出text中的第n個(gè)單詞
$(wordlist ss,e,text)
從text中取出從ss到e的單詞串,b和e是數(shù)字
$(words text)
統(tǒng)計(jì)并返回text中的單詞數(shù)
$(firstword text)
取出text中的第一個(gè)單詞
文件名操作函數(shù)
$(dir names...)
從names中取出目錄部分
$(notdir names...)
和dir相反
$(suffix name...)
從names中取出各個(gè)文件名的后綴
$(basename names...)
從names中取出哥哥文件的前綴
$(addsuffix suffix,names...)
把后綴suffix加到names中每個(gè)單詞后面
$(addprefix prefix,names...)
把prefix加到names中每個(gè)單詞前面
$(join list1,list2)
把list2中的單詞加到list1的對(duì)應(yīng)的單詞后面
foreach 函數(shù)
$(foreach var,list,text)
把參數(shù)list中的單詞逐一取出放到var指定的變量中,然后執(zhí)行text所包含的表達(dá)式, 其中的var作用于僅存在于foreach中
if函數(shù)
$(if condition,then-part)$(if condition,then-part,else-part)
call函數(shù)
$(call expression;,param1;,param2;,param3;...)
origin函數(shù)
$(origin var)
origin函數(shù)告訴我們var是在哪里定義的,其返回值包括:undefined,default,environment,file,command line,override,automatic
- shell函數(shù)
shell函數(shù)和"`"是相同的功能
- 控制make的函數(shù)
error text
產(chǎn)生致命的錯(cuò)誤信息
warning text
產(chǎn)生警告信息
隱含規(guī)則
自動(dòng)化變量
$@: 表示規(guī)則中的目標(biāo)文件集笛园。在模式規(guī)則中研铆,如果有多個(gè)目標(biāo),那么凶赁,“$@”就是匹配于目標(biāo)中模式定義的集合虱肄。
$%: 僅當(dāng)目標(biāo)是函數(shù)庫(kù)文件中交煞,表示規(guī)則中的目標(biāo)成員名攻冷。例如偶垮,如果一個(gè)目標(biāo)是“foo.a(bar.o)”,那么“$%”是“bar.o”丧蘸,“$@”就是“foo.a”渡紫。如果目標(biāo)不是函數(shù)庫(kù)文件(Unix 下是[.a],Windows 下是[.lib])侈沪,那么其值為空。
$<: 依賴目標(biāo)中的第一個(gè)目標(biāo)名字晚凿。如果依賴目標(biāo)是以模式(即“%”)定義的亭罪,那么“$<;”將是符合模式的一系列的文件集。注意歼秽,其是一個(gè)一個(gè)取出來(lái)的应役。
$?: 所有比目標(biāo)新的依賴目標(biāo)的集合。以空格分隔。
$^: 所有的依賴目標(biāo)的集合箩祥。以空格分隔院崇。如果在依賴目標(biāo)中有多個(gè)重復(fù)的,那個(gè)這個(gè)變量會(huì)去除重復(fù)的依賴目標(biāo)袍祖,只保留一份底瓣。
$+: 這個(gè)變量很像“$^”,也是所有依賴目標(biāo)的集合蕉陋。只是它不去除重復(fù)的依賴目標(biāo)。
$*: 這個(gè)變量表示目標(biāo)模式中“%”及其之前的部分垦梆。如果目標(biāo)是“dir/a.foo.b”杠氢,并且目標(biāo)的模式是“a.%.b”,那么,“$”的值就是“dir/a.foo”晃琳。這個(gè)變量對(duì)于構(gòu)造有關(guān)聯(lián)的文件名是比較有較围段。如果目標(biāo)中沒(méi)有模式的定義灸芳,那么“$”也就不能被推導(dǎo)出,但是,如果目標(biāo)文件的后綴是make 所識(shí)別的寻定,那么“$”就是除了后綴的那一部分。例如:如果目標(biāo)是“foo.c”,因?yàn)椤?c”是make 所能識(shí)別的后綴名处硬,所以件豌,“$”的值就是“foo”。這個(gè)特性是GNU make 的惫谤,很有可能不兼容于其它版本的make险污,所以窖式,你應(yīng)該盡量避免使用“$”阁簸,除非是在隱含規(guī)則或是靜態(tài)模式中饶米。如果目標(biāo)中的后綴是make所不能識(shí)別的匈棘,那么“$”就是空值队秩。