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文件的隐含规则