makefile(函数调用)_linux


文本转换函数


函数允许您在makefile文件中处理文本、计算文件、操作使用命令等。在函数调用时您必须指定函数名以及函数操作使用的参数。函数处理的结果将返回到makefile文件中的调用点,其方式和变量替换一样。
8.1函数调用语法
函数调用和变量引用类似,它的格式如下:
$(function arguments)
或这样:

${function arguments}
这里‘function’是函数名,是make内建函数列表中的一个。当然您也可以使用创建函数call创建的您自己的函数。‘arguments’是该函数的参数。参数和函数名之间是用空格或Tab隔开,如果有多个参数,它们之间用逗号隔开。这些空格和逗号不是参数值的一部分。包围函数调用的定界符,无论圆括号或大括号,可以在参数中成对出现,在一个函数调用中只能有一种定界符。如果在参数中包含变量引用或其它的函数调用,最好使用同一种定界符,如写为‘$(subst a,b,$(x))', 而不是 `$(subst a,b,${x})'。这是因为这种方式不但比较清楚,而且也有在一个函数调用中只能有一种定界符的规定。
为每一个参数写的文本经过变量替换或函数调用处理,最终得到参数的值,这些值是函数执行必须依靠的文本。另外,变量替换是按照变量在参数中出现的次序进行处理的。
逗号和不成对出现的圆括号、大括号不能作为文本出现在参数中,前导空格也不能出现在第一个参数中。这些字符不能被变量替换处理为参数的值。如果需要使用这些字符,首先定义变量comma和space,它们的值是单独的逗号和空格字符,然后在需要的地方因用它们,如下例:
comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# bar is now `a,b,c'.
这里函数subst的功能是将变量foo中的空格用逗号替换,然后返回结果。
8.2字符串替换和分析函数
这里有一些用于操作字符串的函数:
$(subst from,to,text)

在文本‘text’中使用‘to’替换每一处‘from’。例如:
$(subst ee,EE,feet on the street)
结果为‘fEEt on the street’。
$(patsubst pattern,replacement,text)

寻找‘text’中符合格式‘pattern’的字,用‘replacement’替换它们。这里‘pattern’中包含通配符‘%’,它和一个字中任意个数的字符相匹配。如果‘replacement’中也含有通配符‘%’,则这个‘%’被和‘pattern’中通配符‘%’匹配的文本代替。在函数patsubst中的‘%’可以用反斜杠(‘\’)引用。引用字符‘%’的反斜杠可以被更多反斜杠引用。引用字符‘%’和其它反斜杠的反斜杠在比较文件名或有一个stem(径)代替它之前从格式中移出。使用反斜杠引用字符‘%’不会带来其它麻烦。例如,格式‘the\%weird\\%pattern\\'是‘the%weird\' 加上通配符‘%'然后和字符串‘pattern\\'连接。最后的两个反斜杠由于不能影响任何统配符‘%’所以保持不变。在字之间的空格间被压缩为单个空格,前导以及结尾空格被丢弃。例如:

$(patsubst %.c,%.o,x.c.c bar.c)
的结果为:‘x.c.o bar.o'。替换引用是实现函数patsubst功能一个简单方法:
$(var:pattern=replacement)
等同于 :

$(patsubst pattern,replacement,$(var))
另一个通常使用的函数patsubst的简单方法是:替换文件名的后缀。
$(var:suffix=replacement)
等同于:

$(patsubst %suffix,%replacement,$(var))
例如您可能有一个OBJ文件的列表:
objects = foo.o bar.o baz.o
要得到这些文件的源文件,您可以简单的写为:

$(objects:.o=.c)
代替规范的格式:

$(patsubst %.o,%.c,$(objects))
$(strip string)

去掉前导和结尾空格,并将中间的多个空格压缩为单个空格。这样,‘$(strip a b c )'结果为‘a b c’。函数strip和条件语句连用非常有用。当使用ifeq或ifneq把一些值和空字符串‘’比较时,您通常要将一些仅由空格组成的字符串认为是空字符串(参阅 makefile中的条件语句)。如此下面的例子在实现预期结果时可能失败:
.PHONY: all
ifneq   "$(needs_made)" ""
all: $(needs_made)
else
all:;@echo 'Nothing to make!'
endif
在条件指令ifneq中用函数调用‘$(strip $(needs_made))'代替变量引用‘$(needs_made)'将不再出现问题。
$(findstring find,in)

在字符串‘in’中搜寻‘find’,如果找到,则返回值是‘find’,否则返回值为空。您可以在一个条件中使用该函数测试给定的字符串中是否含有特定的子字符串。这样,下面两个例子:
$(findstring a,a b c)
$(findstring a,b c)
将分别产生值‘a’和‘’。对于函数findstring的特定用法参阅 测试标志的条件语句
$(filter pattern...,text)

返回在‘text’中由空格隔开且匹配格式‘pattern...’的字,对于不符合格式‘pattern...’的字移出。格式用‘%’写出,和前面论述过的函数patsubst的格式相同。函数filter可以用来变量分离类型不同的字符串。例如:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
        cc $(filter %.c %.s,$(sources)) -o foo
表明‘foo' 依靠‘foo.c',‘bar.c',‘baz.s' 和‘ugh.h';但仅有‘foo.c',‘bar.c' 和 ‘baz.s' 指明用命令编译。
$(filter-out pattern...,text)

返回在‘text’中由空格隔开且不匹配格式‘pattern...’的字,对于符合格式‘pattern...’的字移出。只是函数filter的反函数。例如:
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
下面产生不包含在变量‘mains’中的OBJ文件的文件列表:

$(filter-out $(mains),$(objects))
$(sort list)

将‘list’中的字按字母顺序排序,并取掉重复的字。输出是由单个空格隔开的字的列表。
$(sort foo bar lose)
返回值是‘bar foo lose’。顺便提及,由于函数sort可以取掉重复的字,您就是不关心排序也可以使用它的这个特点。
这里有一个实际使用函数subst和patsubst的例子。假设一个makefile文件使用变量VPATH指定make搜寻依赖文件的一系列路径(参阅 VPATH:依赖搜寻路径)。这个例子表明怎样告诉C编译器在相同路径列表中搜寻头文件。
变量VPATH的值是一列用冒号隔开的路径名,如‘src:../headers'。首先,函数subst将冒号变为空格:
$(subst :, ,$(VPATH))
这产生值‘src ../headers'。然后,函数patsubst为每一个路径名加入‘-|’标志,这样这些路径可以加到变量CFLAGS中,就可以自动传递给C编译器:
override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))
结果是在以前给定的变量CFLAGS的值后追加文本‘-Isrc -I../headers’。Override指令的作用是即使以前使用命令参数指定变量CFLAGS的值,新值也能起作用。参阅 override指令
8.3文件名函数
其中几个内建的扩展函数和拆分文件名以及列举文件名相关联。下面列举的函数都能执行对文件名的特定转换。函数的参数是一系列的文件名,文件名之间用空格隔开(前导和结尾空格被忽略)。列表中的每一个文件名都采用相同的方式转换,而且结果用单个空格串联在一起。
$(dir names...)

抽取‘names’中每一个文件名的路径部分,文件名的路径部分包括从文件名的开始到最后一个斜杠(含斜杠)之前的一切字符。如果文件名中没有斜杠,路径部分是‘./’。如:
$(dir src/foo.c hacks)
产生的结果为 ‘src/ ./’。

$(notdir names...)

抽取‘names’中每一个文件名中除路径部分外一切字符(真正的文件名)。如果文件名中没有斜杠,则该文件名保持不变,否则,将路径部分移走。一个文件名如果仅包含路径部分(以斜杠结束的文件名)将变为空字符串。这是非常不幸的,因为这意味着在结果中如果有这种文件名存在,两文件名之间的空格将不是由相同多的空格隔开。但现在我们并不能看到其它任何有效的代替品。例如:
$(notdir src/foo.c hacks)
产生的结果为‘foo.c hacks’。
$(suffix names...)

抽取‘names’中每一个文件名的后缀。如果文件名中(或含有斜杠,且在最后一个斜杠后)含有句点,则后缀是最后那个句点以后的所有字符,否则,后缀是空字符串。如果结果为空意味着‘names’没有带后缀文件名,如果文件中含有多个文件名,则结果列出的后缀数很可能比原文件名数目少。例如:
$(suffix src/foo.c src-1.0/bar.c hacks)
产生的结果是‘.c .c’。
$(basename names...)

抽取‘names’中每一个文件名中除后缀外一切字符。如果文件名中(或含有斜杠,且在最后一个斜杠后)含有句点,则基本名字是从开始到最后一个句点(不包含)间的所有字符。如果没有句点,基本名字是整个文件名。例如:
$(basename src/foo.c src-1.0/bar hacks)
产生的结果为‘src/foo src-1.0/bar hacks’。

$(addsuffix suffix,names...)

参数‘names’作为一系列的文件名,文件名之间用空格隔开;suffix作为一个单位。将Suffix(后缀)的值附加在每一个独立文件名的后面,完成后将文件名串联起来,它们之间用单个空格隔开。例如:
$(addsuffix .c,foo bar)
结果为‘foo.c bar.c’。

$(addprefix prefix,names...)

参数‘names’作为一系列的文件名,文件名之间用空格隔开;prefix作为一个单位。将preffix(前缀)的值附加在每一个独立文件名的前面,完成后将文件名串联起来,它们之间用单个空格隔开。例如:
$(addprefix src/,foo bar)
结果为‘src/foo src/bar’。

$(join list1,list2)

将两个参数串联起来:两个参数的第一个字串联起来形成结果的第一个字,两个参数的第二个字串联起来形成结果的第二个字,以此类推。如果一个参数比另一个参数的字多,则多余的字原封不动的拷贝到结果上。例如,‘$(join a b,.c .o)'产生‘a.c b.o'。字之间多余的空格不再保留,它们由单个空格代替。该函数可将函数dir、notdir的结果合并,产生原始给定的文件列表。
$(word n,text)

返回‘text’中的第n个字。N的合法值从1开始。如果n比‘text’中的字的数目大,则返回空值。例如:
$(word 2, foo bar baz)
返回 ‘bar’。

$(wordlist s,e,text)

返回‘text’中的从第s个字开始到第e个字结束的一列字。S、e的合法值从1开始。如果s比‘text’中的字的数目大,则返回空值;如果e比‘text’中的字的数目大,则返回从第s个字开始到‘text’结束的所有字;如果s比e大,不返回任何值。例如:
$(wordlist 2, 3, foo bar baz)
返回`bar baz'。

$(words text)

返回‘text’中字的数目。这样‘text’中的最后一个字是‘$(word $(words text),text)’。
$(firstword names...)

参数‘names’作为一系列的文件名,文件名之间用空格隔开;返回第一个文件名,其余的忽略。例如:
$(firstword foo bar)
产生结果‘foo’。 虽然 $(firstword text) 和 $(word 1,text)的作用相同,但第一个函数因为简单而保留下来。

$(wildcard pattern)

参数‘pattern’是一个文件名格式,典型的包含通配符(和shel中的文件名一样)。函数wildcard的结果是一列和格式匹配的且文件存在的文件名,文件名之间用一个空格隔开,参阅 在文件名中使用通配符
8.4函数foreach
函数foreach和其它函数非常不同,它导致一个文本块重复使用,而且每次使用该文本块进行不同的替换;它和shell sh中的命令for及C-shell csh中的命令foreach类似。
函数foreach语法如下:
$(foreach var,list,text)
前两个参数,‘var’和‘list’,将首先扩展,注意最后一个参数‘text’此时不扩展;接着,对每一个‘list’扩展产生的字,将用来为‘var’扩展后命名的变量赋值;然后‘text’引用该变量扩展;因此它每次扩展都不相同。
结果是由空格隔开的‘text’ 在‘list’中多次扩展的字组成的新的‘list’。‘text’多次扩展的字串联起来,字与字之间由空格隔开,如此就产生了函数foreach的返回值。
这是一个简单的例子,将变量‘files’的值设置为 ‘dirs’中的所有目录下的所有文件的列表:
dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
这里‘text’是‘$(wildcard $(dir)/*)’。第一个为变量dir发现的值是‘a’,所以产生函数foreach结果的第一个字为‘$(wildcard a/*)’;第二个重复的值是‘b’,所以产生函数foreach结果的第二个字为‘$(wildcard b/*)’;第三个重复的值是‘c’,所以产生函数foreach结果的第三个字为‘$(wildcard c/*)’;等等。该例子和下例有共同的结果:
files := $(wildcard a/* b/* c/* d/*)
如果‘text’比较复杂,您可以使用附加变量为它命名,这样可以提高程序的可读性:
find_files = $(wildcard $(dir)/*)
dirs := a b c d
files := $(foreach dir,$(dirs),$(find_files))
这里我们使用变量find_file。我们定义变量find_file时,使用了‘=’,因此该变量为递归调用型变量,这样变量find_file所包含的函数调用将在函数foreach控制下在扩展;对于简单扩展型变量将不是这样,在变量find_file定义时就调用函数wildcard。
函数foreach对变量‘var’没有长久的影响,它的值和变量特色在函数foreach调用结束后将和前面一样,其它从‘list’得到的值仅在函数foreach执行时起作用,它们是暂时的。变量‘var’在函数foreach执行期间是简单扩展型变量,如果在执行函数foreach之前变量‘var’没有定义,则函数foreach调用后也没有定义。参阅 变量的两个特色
当使用复杂变量表达式产生变量名时应特别小心,因为许多奇怪的字符作为变量名是有效的,但很可能不是您所需要的,例如:
files := $(foreach Esta escrito en espanol!,b c ch,$(find_files))
如果变量find_file扩展引用名为‘Esta escrito en espanol!’变量,上例是有效的,但它极易带来错误。
8.5函数if
函数if对在函数上下文中扩展条件提供了支持(相对于GNU make makefile文件中的条件语句,例如ifeq指令,参阅 条件语句的语法)。
一个函数if的调用,可以包含两个或三个参数:
$(if condition,then-part[,else-part])
第一个参数‘condition’,首先把前导、结尾空格去掉,然后扩展。如果扩展为非空字符串,则条件‘condition’为‘真’;如果扩展为空字符串,则条件‘condition’为‘假’。
如果条件‘condition’为‘真’,那么计算第二个参数‘then-part’的值,并将该值作为整个函数if的值。
如果条件‘condition’为‘假’,第三个参数如果存在,则计算第三个参数‘else-part’的值,并将该值作为整个函数if的值;如果第三个参数不存在,函数if将什么也不计算,返回空值。
注意仅能计算‘then-part’和‘else-part’二者之一,不能同时计算。这样有可能产生副作用(例如函数shell的调用)。
8.6函数call
函数call是唯一的创建新的带有参数函数的函数。您可以写一个复杂的表达是作为一个变量的值,然后使用函数call用不同的参数调用它。

函数call的语法为:

$(call variable,param,param,...)
当make扩展该函数时,它将每一个参数‘param’赋值给临时变量$(1)、$(2)等;变量$(0)的值是变量‘variable’。对于参数‘param’的数量无没有最大数目限制,也没有最小数目限制,但是如果使用函数call而没有任何参数,其意义不大。

变量‘variable’在这些临时变量的上下文中被扩展为一个make变量,这样,在变量‘variable’中对变量‘$(1)’的引用决定了调用函数call时对第一个参数‘param’的使用。

注意变量‘variable’是一个变量的名称,不是对该变量的引用,所以,您不能采用‘$’和圆括号的格式书写该变量,当然,如果您需要使用非常量的文件名,您可以在文件名中使用变量引用。

如果变量名是内建函数名,则该内建函数将被调用(即使使用该名称的make变量已经存在)。函数call在给临时变量赋值以前首先扩展参数,这意味着,变量‘variable’对内建函数的调用采用特殊的规则进行扩展,象函数foreach或if,它们的扩展结果和您预期的结果可能不同。下面的一些例子能够更清楚的表达这一点。

该例子时使用宏将参数的顺序翻转:

reverse = $(2) $(1)

foo = $(call reverse,a,b)
这里变量foo的值是‘b a’。

下面是一个很有意思的例子:它定义了一个宏,使用该宏可以搜寻变量PATH包含的所有目录中的第一个指定类型的程序:

pathsearch = $(firstword $(wildcard $(addsufix /$(1),$(subst :, ,$(PATH)))))

LS := $(call pathsearch,ls)
现在变量LS的值是‘/bin/ls’或其它的类似的值。

在函数call中可以使用嵌套。每一次递归调用都可以为它自己的局部变量‘$(1)’等赋值,从而代替上一层函数call赋的值。例如:这实现了映像函数功能。

map = $(foreach a,$(2),$(call $(1),$(a)))
现在您可以映像(map)仅有一个参数的函数,如函数origin,一步得到多个值:

o = $(call map,origin,o map MAKE)
最后变量o包含诸如‘file file default’这样的值。

警告:在函数call的参数中使用空格一定要十分小心。因为在其它函数中,第二个或接下来的参数中的空格是不删除的,这有可能导致非常奇怪的结果。当您使用函数call时,去掉参数中任何多余的空格才是最安全的方法。

8.7函数origin
函数origin不想一般函数,它不对任何变量的值操作;它仅仅告诉您一些关于一个变量的信息;它特别的告诉您变量的来源。

函数origin的语法:

$(origin variable)
注意变量‘variable’是一个查询变量的名称,不是对该变量的引用所以,您不能采用‘$’和圆括号的格式书写该变量,当然,如果您需要使用非常量的文件名,您可以在文件名中使用变量引用。

函数origin的结果是一个字符串,该字符串变量是怎样定义的:

‘undefined'

如果变量‘variable’从没有定义。

‘default'

变量‘variable’是缺省定义,通常和命令CC等一起使用,参阅 隐含规则使用的变量。注意如果您对一个缺省变量重新进行了定义,函数origin将返回后面的定义。

‘environment'

变量‘variable’作为环境变量定义,选项‘-e’没有打开(参阅 选项概要)。
‘environment override'

变量‘variable’作为环境变量定义,选项‘-e’已打开(参阅 选项概要)。
‘file'

变量‘variable’在makefile中定义。
‘command line'

变量‘variable’在命令行中定义。

‘override'

变量‘variable’在makefile中用override指令定义(参阅 override指令)。

‘automatic'

变量‘variable’是自动变量,定义它是为了执行每个规则中的命令(参阅 自动变量)。
这种信息的基本用途(其它用途是满足您的好奇心)是使您要了解变量值的依据。例如,假设您有一个名为‘foo’的makefile文件,它包含了另一个名为‘bar’的makefile文件,如果在环境变量中已经定义变量‘bletch’,您希望运行命令‘make –f bar’在makefile文件‘bar’中重新定义变量‘bletch’。但是makefile文件‘foo’在包括makefile文件‘bar’之前已经定义了变量‘bletch’,而且您也不想使用override指令定义,那么您可以在makefile文件‘foo’中使用override指令,因为override指令将会重载任何命令行中的定义,所以其定义的优先权超越以后在makefile文件‘bar’中的定义。因此makefile文件‘bar’可以包含:
ifdef bletch
ifeq "$(origin bletch)" "environment"
bletch = barf, gag, etc.
endif
endif
如果变量‘bletch’在环境中定义,这里将重新定义它。
即使在使用选项‘-e’的情况下,您也要对来自环境的变量‘bletch’重载定义,则您可以使用如下内容:
ifneq "$(findstring environment,$(origin bletch))" ""
bletch = barf, gag, etc.
endif
如果‘$(origin bletch)’返回‘environment’或‘environment override’,这里将对变量‘bletch’重新定义。参阅 字符串替换和分析函数
8.8 函数shell
除了函数wildcard之外,函数shell和其它函数不同,它是make与外部环境的通讯工具。函数shell和在大多数shell中后引号(’)执行的功能一样:它用于命令的扩展。这意味着它起着调用shell命令和返回命令输出结果的参数的作用。Make仅仅处理返回结果,再返回结果替换调用点之前,make将每一个换行符或者一对回车/换行符处理为单个空格;如果返回结果最后是换行符(和回车符),make将把它们去掉。由函数shell调用的命令,一旦函数调用展开,就立即执行。在大多数情况下,当makefile文件读入时函数shell调用的命令就已执行。例外情况是在规则命令行中该函数的调用,因为这种情况下只有在命令运行时函数才能扩展,其它调用函数shell的情况和此类似。
这里有一些使用函数shell的例子:
contents := $(shell cat foo)
将含有文件foo的目录设置为变量contents的值,是用空格(而不是换行符)分离每一行。
files := $(shell echo *.c)
将‘*.c’的扩展设置为变量files的值。除非make使用非常怪异的shell,否则这条语句和‘wildcard *.c’的结果相同。
8.9 控制make的函数
这些函数控制make的运行方式。通常情况下,它们用来向用户提供makefile文件的信息或在侦测到一些类型的环境错误时中断make运行。
$(error text...)

通常‘text’是致命的错误信息。注意错误是在该函数计算时产生的,因此如果您将该函数放在命令的脚本中或递归调用型变量赋值的右边,它直到过期也不能计算。‘text’将在错误产生之前扩展,例如:
ifdef ERROR1
$(error error is $(ERROR1))
endif
如果变量ERROR01已经定义,在将makefile文件读入时产生致命的错误。或,
ERR = $(error found an error!)

.PHONY: err
err: ; $(ERR)
如果err目标被调用,在make运行时产生致命错误。
$(warning text...)

该函数和函数error工作的方式类似,但此时make不退出,即虽然‘text’扩展并显示结果信息,但make仍然继续执行。扩展该函数的结果是空字符串。

转自http://atia.blog.sohu.com/94779764.html

猜你喜欢

转载自wanghaozhao.iteye.com/blog/1968841