Makefile规则的样式如下:
目标(target)... : 依赖(prerequiries)...
<Tab>命令(command)
命令被执行的两个条件:依赖文件比目标文件新,或者目标文件未生成。
(一)Makefile语法
1、通配符%(%.c表示所有.c文件,%.o表示所有.o文件)
$@表示目标
$<表示第一个依赖文件
$^表示所有的依赖文件
例如:
2、假想目标:.PHONY
例如:
当该目录下有名为clean的文件的时候,执行make clean并不会起效果,如图:
现在我们的目录里面有名为“clean”的文件,目标文件是有的,并且没有依赖文件,没有办法判断依赖文件的时间。这种写法会导致:有同名的"clean"文件时,就没有办法执行make clean操作。解决办法:我们需要把目标定义为假象目标,用关键子PHONY
(注意:执行make[目标],也可以不带目标名,若无目标则默认第一个目标。直接执行make的时候,会在makefile里面找到第一个目标然后执行下面的命令来生成第一个目标;当执行make clean时,会在makefile中找到clean目标,然后执行里面的命令。)
3、变量(即时变量,延时变量)
A := xxx # A的值即刻确定,再定义时即确定。
B = xxx # B的值使用到的时候才确定
:= 定义为即时变量
= 定义为延时变量
?= 定义为延时变量,是第一次定义才起效,若在前面该变量已定义则忽略这句
+= 定义为附加,它是即时变量还是延时变量取决于前面的定义
例如:
执行make,结果如下:
分析:1)A := $(C)
A为即时变量。定义时即确定,由于刚开始时C的值为空,所以A的值也为空。
2)B = $(C)
B为延时变量,只有使用到时它的值才会被确定下来,执行make时,会解析Makefile里面所有的变量,首先解析C = abc,然后解析C += 123,此时C = abc 123,所以B的值为abc 123
3)D ?= ounijiang
D变量在前面未定义,所以D的值为ounijiang,若在前面添加D=100ask,则最后D的值为100ask(还可以通过命令行存入变量的值,例如:make D = ???,这样Makefile中的D ?= ounijiang就不起作用了)
(二)Makefile常用函数
函数调用的格式:$(函数名 函数参数)或者$(函数名 函数参数)
函数名与参数之间是用Tab或者空格隔开,若有多个参数,则用逗号隔开。这些空格和逗号不是参数值的一部分。
(字符串替换和分析函数)
1、$(subst from,to,text)
解析:在文本"test"中使用‘to’来替换每一处的‘from’
例如:$(subst ee,EE,feet on the street)
结果为‘fEEt on the strEEt’。
2、$(patsubst pattern,replacement,text)
解析:寻找`text’中符合格式`pattern’的字,用`replacement’替换它们。另外,‘pattern’和‘replacement’中可以使用通配符。
例如:$(patsubst %.c,%.o,x.c.c bar.c)
结果为:‘x.c.o bar.o’
3、$(strip string)
解析:去掉前导和结尾空格,并将中间的多个空格压缩为单个空格。
例如:$(strip a b c)
结果为:'a b c'
4、$(findstring find,in)
解析:在字符串'in'中搜寻'find',如果找到,则返回值是'find',否则返回值为空。
例如:$(findstring a,a b c)和$(findstring a,b c)
结果为:'a'和' '
5、$(filter pattern...,text)
解析:返回在'text'中由空格隔开且匹配格式'pattern...'的字,去除不符合格 式'pattern...'的字。
例如:$(filter %.c %.s,cwy.c dxt.c dxt.s cwy.h)
结果为:'cwy.c dxt.c dxt.s'
6、$(filter-out pattern...,text)
解析:返回在'text'中由空格隔开且不匹配格式'pattern...'的字,去除符合格式'pattern...'的字。它是函数filter的反函数。
例如:$(filter-out %.c %.s,cwy.c dxt.c dxt.s cwy.h)
结果为:'cwy.h'
7、$(sort list)
解析:将‘list’中的字按字母顺序排序,并去掉重复的字。输出由单个空格隔开的字的列表。
例如:$(sort ttl cmos rss)
结果为:'cmos rss ttl'
(文件名函数)
1、$(dir names...)
解析:抽取‘names...’中每一个文件名的路径部分,文件名的路径部分包括从文件名的首字符到最后一个斜杠(含斜杠)之前的一切字符。
例如:$(dir src/foo.c hacks)
结果为:'src/ ./'
2、$(notdir names...)
解析:抽取‘names...’中每一个文件名中除路径部分外一切字符(真正的文件名)。
例如:$(notdir src/foo.c hacks)
结果为:'foo.c hacks'
3、$(suffix names...)
解析:抽取‘names...’中每一个文件名的后缀。
例如:$(suffix src/foo.c src-1.0/bar.c hacks)
结果为:'.c .c'
4、$(basename names...)
解析:抽取‘names...’中每一个文件名中除后缀外一切字符。
例如:$(basename src/foo.c src-1.0/bar hacks)
结果为:'src/foo src-1.0/bar hacks'
5、$(addsuffix suffix,names...)
解析:参数‘names...’是一系列的文件名,文件名之间用空格隔开;suffix是一个后缀名。将 suffix(后缀)的值附加在每一个独立文件名的后面,完成后将文件名串联起来,它们之间用单个空格隔开。
例如:$(addsuffix .c,foo bar)
结果为:'foo.c bar.c'
6、$(addprefix prefix,names...)
解析:参数‘names’是一系列的文件名,文件名之间用空格隔开;prefix 是一个前缀名。将 preffix(前缀)的值附加在每一个独立文件名的前面,完成后将文件名串联起来,它们之间用单个空格隔开。
例如:$(addprefix src/,foo bar)
结果为:'src/foo src/bar'
7、$(wildcard pattern)
解析:参数‘pattern’是一个文件名格式,包含有通配符(通配符和 shell 中的用法一样)。函数 wildcard 的结果是一列和格式匹配的且真实存在的文件的名称,文件名之间用一个空格隔开。当该目录下有1.c、2.c、1.h、2.h时
例如:c_src := $(wildcard *.c)
结果为:'1.c 2.c'
(其他函数)
1、$(foreach var,list,text)
解析:前两个参数,‘var’和‘list’,将首先扩展,注意最后一个参数 ‘text’ 此时不扩展;接着,对每一个 ‘list’ 扩展产生的字,将用来为 ‘var’ 扩展后命名的变量赋值;然后 ‘text’ 引用该变量扩展;因此它每次扩展都不相同。结果是由空格隔开的 ‘text’。在 ‘list’ 中多次扩展的字组成的新的 ‘list’。‘text’ 多次扩展的字串联起来,字与字之间由空格隔开,如此就产生了函数 foreach 的返回值。
例如:
结果:
2、$(if condition,then-part[,else-part])
首先把第一个参数‘condition’的前导空格、结尾空格去掉,然后扩展。如果扩展为非空字符串,则条件‘condition’为‘真’;如果扩展为空字符串,则条件‘condition’为‘假’。 如果条件‘condition’为‘真’,那么计算第二个参数‘then-part’的值,并将该值作为整个函数 if 的值。 如果条件‘condition’为‘假’,并且第三个参数存在,则计算第三个参数‘else-part’的值,并将该值作为整个函数 if 的值;如果第三个参数不存在,函数 if 将什么也不计算,返回空值。 注意:仅能计算‘then-part’和‘else-part’二者之一,不能同时计算。这样有可能产生副作用(例如函数 shell 的调用)。
3、$(origin variable)
变量‘variable’是一个查询变量的名称,不是对该变量的引用。所以,不能采用‘$’和圆括号的格式书写该变量,当然,如果需要使用非常量的文件名,可以在文件名中使用变量引用。 函数 origin 的结果是一个字符串,该字符串变量是这样定义的:
4、$(shell command arguments)
解析:函数 shell 是 make 与外部环境的通讯工具。函数 shell 的执行结果和在控制台上执行‘command arguments’的结果相似。不过如果‘command arguments’ 164 / 566 的结果含有换行符(和回车符),则在函数 shell 的返回结果中将把它们处理为单个空格,若返回结果最后是换行符(和回车符)则被去掉。比如当前目录下有文件 1.c、2.c、1.h、2.h,则:
例如:c_src := $(shell ls *.c)
结果:'1.c 2.c'
(Makefile规则使得能够根据文件更新的时间戳来决定哪些文件需要重新编译,这使得可以避免编译已经编译过的、没有变化的程序,可以大大提高编译效率。)