makefile函数
用法
函数调用后,返回值可以当变量使用。
函数调用,很像变量使用,以$来标识,语法如下:
$(<function> <arguments>)
或${<function> <arguments>}
函数名与参数间“空格”分隔,参数间以“逗号”分隔。为使风格统一,函数和变量的括号最好一样,如:
$(subst a,b,$(x))
makefile常用函数
字符串处理函数
1. $(wildcard <pattern...>--通配符--被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表--如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空--$(wildcard *.c)
2. $(subst <from>,<to>,<text>) --字符串替换函数--把子串text中from替换成to--函数返回被替换过后的字符串--$(subst ee,EE,feet on the street)
3. $(patsubst <pattern>,<replacement>,<text>)--模式字符串替换函数--查找text中单词(单词以空格、Tab或回车、换行分隔)是否符合模式pattern,如果匹配则用replacement替换。通配符%表示任意长度子串--函数返回被替换过后的字符串--$(patsubst %.c,%.o,x.c.c bar.c)
4. $(strip <string>)--去掉空格函数--去掉string子串开头和结尾的空字符--返回被去掉空格的字符串值--$(strip a b c )
5. $(findstring <find>,<in>)--查找字符串函数--在in中查找find子串--如果找到返回find,否则返回空字符串--$(findstring a,a b c)
6. $(filter <pattern...>,<text>)--过滤函数--以pattern模式过滤text子串中的单词,保留符合pattern模式的单词,可有多个模式--返回符合pattern模式的子串--$(filter %.c %s,$(source))
7. $(filter-out <pattern...>,<text>)--反过滤函数
8. $(sort <list>)--排序函数(升序)--返回排序后的字符串--sort会去掉list中相同的单词
9. $(word <n>,<text>)--取字符串text中第n个单词(从1开始)--返回第n个单词,若n比text大就返回空字符串。
10. $(wordlist <s>,<e>,<text>)--取单词串函数--取从s到e的单词串
11. $(words <text>)--单词个数统计函数--返回text中单词数
12. $(firstword <text>--首单词函数
文件名操作函数
1. $(dir <names...>)--取目录函数--没有反斜杠返回./
2. $(notdir <names...)--取文件函数
3. $(suffix <names...>)--区后缀函数
4. $(basename <names...)--取前缀函数
5. $(addsuffix <suffix>,<names...>)--加后缀函数
6. $(addprefix <prefix>,<names...>)--加前缀函数
7. $(join <list1>,<list2>)--连接函数--把list2中的单词对应地加到list1的单词后面--返回连接后的字符串--$(join aaa bbb,111 222 333)返回aaa111 bbb222 333
value函数
函数“value”提供了一种在不对变量进行展开的情况下获取变量值的方法。注意:并不是 说函数会取消之前已经执行过的替换扩展。比如:定义了一个直接展开式的变量,此变量在定义过程中对其它变量的引用进行替换而得到自身的值。在使用 “value”函数取这个变量进行取值时,得到的是不包含任何引用值。而不是将定义过程中的替换展开动作取消后包含引用的定义值。就是说此过程不能取消此 变量在定义时已经发生了的替换展开动作。
函数语法:
$(value VARIABLE)
函数功能:不对变量“VARIBLE”进行任何展开操作,直接返回变量“VARIBALE”的值。这里“VARIABLE”是一个变量名,一般不包含“$”(除非计算的变量名)。
返回值:变量“VARIBALE”所定义文本值(如果变量定义为递归展开式,其中包含对其他变量或者函数的引用,那么函数不对这些引用进行展开。函数的返回值是包含有引用值)。
foreach函数
个函数是用来做循环用的
(foreach <var>,<list>,<text>)
这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 然后再执行<text>所包含的表达式。 每一次<text>会返回一个字符串, 循环过程中, <text>的所返回的每个字符串会以空格分隔, 最后当整个循环结束时, <text>所返回的每个字符串 所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 所以,<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用 <var>这个参数来依次枚举<list>中的单词。
举个例子: names := a b c d files := $(foreach n,$(names),$(n).o) 上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根 据 “$(n)” 计算出一个值, 这些值以空格分隔, 最后作为foreach函数的返回, 所以, $(files)是“a.o b.o c.o d.o”。
注意, foreach中的<var>;参数是一个临时的局部变量, foreach函数执行完后, 参数<var>; 的变量将不在作用,其作用域只在foreach函数当中。
if函数
(if <condition>,<then-part> )
或是
$(if <condition>,<then-part>,<else-part>)
可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是 三个。<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,<then-part>会被计算,否则<else-part>会被计算。 而if函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整 个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的 返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。 所以,<then-part>和<else-part>只会有一个被计算。
call函数
all函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式, 这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。其语法是:
$(call <expression>,<parm1>,<parm2>,<parm3>...)
当 make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参 数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是call函数的返回值。
例如: reverse = $(1) $(2) foo = $(call reverse,a,b) 那么,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如
everse = $(2) $(1) foo = $(call reverse,a,b) 此时的foo的值就是“b a”。
eval函数
与shell eval等同,两次展开。
函数功能:函数“eval”是一个比较特殊的函数。使用它可以在Makefile中构造一 个可变的规则结构关系(依赖关系链),其中可以使用其它变量和函数。函数“eval”对它的参数进行展开,展开的结果作为Makefile的一部 分,make可以对展开内容进行语法解析。展开的结果可以包含一个新变量、目标、隐含规则或者是明确规则等。也就是说此函数的功能主要是:根据其参数的关 系、结构,对它们进行替换展开。
返回值:函数“eval”的返回值时空,也可以说没有返回值。
函数说明:“eval”函
数执行时会对它的参数进行两次展开。第一次展开过程是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容时由make解析时展开的。
明确这一过程对于使用“eval”函数非常重要。理解了函数“eval”二次展开的过程后。实际使用时,如果在函数的展开结果中存在引用(格式
为:$(x)),那么在函数的参数中应该使用“$$”来代替“$”。因为这一点,所以通常它的参数中会使用函数“value”来取一个变量的文本值。
$(eval var)返回为空。这个特性被大量使用于宏定义define中。
看下面这个例子:
define import_target include $(1) _PREFIXID := $(if$2, $2, TARGET) $(_ PREFIXID) endef
按上面定义的宏,当去计算a:=$(call import_target)时,几乎总是会报错。
原因是宏定义只是简单的做字符串替换,一经替换后,原来看起来是一行语句,一下子就变成多行,从而导致makefile解析错误。
于是只能使用“\”将各个语句连接为一行。
define import_target include $(1) \ _PREFIXID := $(if $2, $2,TARGET) \ $(_ PREFIXID) endef
这样做相当于
include $(1) _PREFIXID := $(if $2, $2, TARGET) $(_ PREFIXID)
显然,肯定还是报解析错误。最终解决方案,将各个子语句使用$(eval )包裹,
define import_target $(eval include $(1)) \ $(eval _PREFIXID := $(if $2,$2, TARGET)) \ $(_ PREFIXID) endef
解析时,makefile先对$(1)做展开,假设结果为xxx,这是第一次;然后执行include xxx,这是第二次展开。执行完后,整个$(eval include $(1))表达式返回值为空。这样解析错误解决了,而且 import_target的返回结果又正好是_ PREFIXID的值。
origin函数
origin函数不像其它的函数, 他并不操作变量的值, 他只是告诉你你的这个变量是哪里来的?其语法是:
$(origin <variable>)
注意, <variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用 “$”字符。
Origin函数会以其返回值来告诉你这个变量的“出生情况”,下面,是origin 函数的返回值:
“undefined” 如果<variable>从来没有定义过,origin函数返回这个值“undefined”。
“default” 如果<variable>是一个默认的定义,比如“CC”这个变量。
“ environment” 如果<variable>是一个环境变量,并且当Makefile被执行时,“-e”参数没有被 打开。
“ file” 如果<variable>这个变量被定义在Makefile中。
“ command line” 如果<variable>这个变量是被命令行定义的。
“ override” 如果<variable>;是被override指示符重新定义的。
“ automatic” 如果<variable>是一个命令运行中的自动化变量。
shell函数
shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量, 如:
contents := $(shell cat foo)
files := $(shell echo *.c)
注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果你的Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有 害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你想像的多得多。
控制make的函数
make提供了一些函数来控制make的运行。 通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,你是让make继续执行,还是停止。
$(error <text ...>) 产生一个致命的错误, <text ...>是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。例如
$(warning <text..>)函数很像error函数, 只是它并不会让make退出, 只是输出一段警告信息, 而make 继续执行。
参考:
1. 《跟我一起写Makefile》