Chinaunix首页 | 论坛 | 博客
  • 博客访问: 754615
  • 博文数量: 98
  • 博客积分: 4934
  • 博客等级: 上校
  • 技术积分: 1151
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-12 19:55
文章分类

全部博文(98)

文章存档

2014年(1)

2013年(2)

2012年(4)

2011年(25)

2010年(33)

2009年(33)

分类: 嵌入式

2009-09-05 10:15:56

1.6        call函数

call”函数是唯一一个可以创建定制参数化的函数的引用函数。我们可以将一个变量定义为一个复杂的表达式,用“call”函数根据不同的参数对它进行展开来获得不同的结果。

Ø         函数语法:

 $(call VARIABLE,PARAM,PARAM,...)

 Ø         函数功能:在执行时,将它的参数“PARAM”依次赋值给临时变量“$(1)”、“$(2)”(这些临时变量定义在“VARIABLE”的值中,参考下边的例子)…… call函数对参数的数目没有限制,也可以没有参数值,没有参数值的“call”没有任何实际存在的意义。执行时变量“VARIABLE”被展开为在函数上下文有效的临时变量,变量定义中的“$(1)”作为第一个参数,并将函数参数值中的第一个参数赋值给它;变量中的“$(2)”一样被赋值为函数的第二个参数值;依此类推(变量$(0)代表变量“VARIABLE”本身)。之后对变量“VARIABLE 表达式的计算值。

Ø         返回值:参数值“PARAM”依次替换“$(1)”、“$(2)”…… 之后变量“VARIABLE”定义的表达式的计算值。

Ø         函数说明:1. 函数中“VARIBLE”是一个变量名,而不是对变量的引用。因此,通常“call”函数中的“VARIABLE”中不包含“$”(当然,除了此变量名是一个计算的变量名)。2. 当变量“VARIBLE”是一个make内嵌的函数名时(如“if”、“foreach”、“strip”等),对“PARAM”参数的使用需要注意,因为不合适或者不正确的参数将会导致函数的返回值难以预料。3. 函数中多个“PARAM”之间使用逗号分割。4. 变量“VARIABLE”在定义时不能定义为直接展开式!只能定义为递归展开式。

Ø         函数示例:

首先,来看一个简单的例子。

示例1

 reverse =  $(2) $(1)

foo = $(call reverse,a,b)

 

变量“foo”的值为“ba”。这里变量“reverse”中的参数定义顺序可以根据需要来调整,并不是需要按照“$(1)”、“$(2)”、“$(3)”…… 这样的顺序来定义。

看一个稍微复杂一些的例子。我们定义了一个宏“pathsearch”来在“PATH”路径中搜索第一个指定的程序。

示例2

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

LS := $(call pathsearch,ls)

 变量“LS”的结果为“/bin/sh”。执行过程:函数“subst”将环境变量“PATH”转换为空格分割的搜索路径列表;“addsuffix”构造出可能的可执行程序“$(1)”(这里是“ls”)带路径的完整文件名(如:“/bin/$(1)”),之后使用函数“wildcard”匹配,最后“firstword”函数取第一个文件名。

 函数“call”以可以套嵌使用。每一层“call”函数的调用都为它自己的局部变量“$(1)”等赋值,覆盖上一层函数为它所赋的值。

示例3

 map = $(foreach a,$(2),$(call $(1),$(a)))

o = $(call map,origin,o map MAKE)

 那么变量“o”的值就为“file file default”。我们这里使用了origin”函数。我们分析函数的执行过程:首先,“o=$(call map,origin, o map MAKE)”这个函数调用使用了变量“map”所定义的表达式;使用内嵌函数名“origin”作为它的第一个参数值,使用Makefile中的变量“o map MAKE”作为他的第二个参数值。当使用“call”函数每一的展开后等价于“$(foreach a,o map MAKE,$(origin $(a)))”。

Ø        注意:和其它函数一样,“call”函数会保留出现在其参数值列表中的空字符。因此在使用参数值时对空格处理要格外小心。如果参数中存在多余的空格,函数可能会返回一个莫名奇妙的值。为了安全,在变量作为“call”函数参数值之前,应去掉其值中的多余空格(可以使用 strip”函数 )。

1.7        value函数

函数“value”提供了一种在不对变量进行展开的情况下获取其值的方式。注意:并不是说函数会取消之前已经执行过的替换扩展。比如:我们定义了一个直接展开式的变量,此变量在定义过程中对其它变量的引用进行替换展开得到自身的值。我们在使用“value”函数取它的值时,得到的是不包含任何引用的实际值。而不是将定义过程中的替换展开动作取消后包含引用的定义值。就是说它不能取消此变量中已经发生了的替换展开动作。

Ø         函数语法:

 $(value VARIABLE)

 Ø         函数功能:不对变量“VARIBLE”进行任何展开操作,直接返回变量“VARIBALE”代表的值。这里“VARIABLE”是一个变量名,一般不包含“$”(当然,除了计算的变量名),

Ø         返回值:变量“VARIBALE”所定义文本值(不展开其中的变量或者函数应用)。

Ø         函数说明:

示例:

# sample Makefile

FOO = $PATH

all:

@echo $(FOO)

@echo $(value FOO)

执行make时,我们可以看到的结果是:第一行为:“ATH”。这是因为变量“FOO”定义为“$PATH”,所以展开为“ATH”(“$P”为空,参考 5.1 变量的引用 一节)。

第二行才是我们需要显示的系统环境变量“PATH”的值(value函数得到变量“FOO”的值为“$PATH”)。

1.8        eval函数

Ø         函数功能:函数“eval”是一个比较特殊的函数。使用它我们可以在我们的Makefile中构造一个可变的规则结构关系(依赖关系链),其中可以使用其它变量和函数。函数“eval”对它的参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。展开的结果可以包含一个新变量、目标、隐含规则或者是明确规则等。也就是说此函数的功能主要是:根据其参数的关系、结构,对它们进行替换展开。

Ø         返回值:函数“eval”的返回值时空,也可以说没有返回值。

Ø         函数说明:eval”函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容时由make解析时展开的。明确这一点对于使用“eval”函数非常重要。在理解了函数“eval”二次展开的过程后。实际使用时,当函数的展开结果中存在引用(格式为:$(x))时,那么在函数的参数中应该使用“$$”来代替“$”(参考 5.1 变量的引用 一节)。因为这一点,所以通常它的参数中会使用函数“value来取一个变量的文本值。

我们看一个例子。例子看起来似乎非常复杂,因为它综合了其它的一些概念和函数。不过我们可以考虑两点:其一,通常实际一个模板的定义可能比例子中的更为复杂;其二,我们可以实现一个复杂通用的模板,在我们的所有Makefile中包含它,以可作到一劳永逸。相信这一点可能是大多数程序员所推崇的。

示例:

# sample Makefile

PROGRAMS    = server client

server_OBJS = server.o server_priv.o server_access.o

server_LIBS = priv protocol 

client_OBJS = client.o client_api.o client_mem.o

client_LIBS = protocol 

# Everything after this is generic

.PHONY: all

all: $(PROGRAMS) 

define PROGRAM_template

$(1): $$($(1)_OBJ) $$($(1)_LIBS:%=-l%)

ALL_OBJS   += $$($(1)_OBJS)

endef 

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))  

$(PROGRAMS):

       $(LINK.o) $^ $(LDLIBS) -o $@

 clean:

       rm -f $(ALL_OBJS) $(PROGRAMS)

 我们来看一下这个例子:它实现的功能是完成“PROGRAMS”的编译链接。例子中“$(LINK.o)”为“$(CC) $(LDFLAGS)”,意思是对所有的.o文件和指定的库文件进行链接。可参考 9.2 make隐含规则一览 一节

$(foreach prog,$(PROGRAM),$(eval $(call PROGRAM_template,$(prog))))”展开为:

server : $(server_OBJS) –l$(server_LIBS)

client : $(client_OBJS) –l$(client_LIBS)

1.9        origin函数

函数“origin”和其他函数不同,函数“origin”的动作不是操作变量(它的参数)。它只是获取和此变量(参数)相关的信息,告诉我们这个变量的出处(定义方式)。

Ø         函数语法:

$(origin VARIABLE) 

Ø      函数功能:函数“origin”查询参数“VARIABLE”(通常是一个变量名)的出处。

Ø       函数说明:VARIABLE”是一个变量名而不是一个变量的引用。因此通常它不包含“$”(当然,计算的变量名例外)。

Ø       返回值:返回“VARIABLE”的定义方式。用字符串表示。

函数的返回情况有以下几种:

1.       undefined

变量“VARIABLE”没有被定义。

2.       default

变量“VARIABLE”是一个默认定义(内嵌变量)。如“CC”、“MAKE”、“RM”等变量(参考 9.3 隐含变量 一节)。如果在Makefile中重新定义这些变量,函数返回值将相应发生变化。

3.       environment

变量“VARIABLE”是一个系统环境变量,并且make没有使用命令行选项“-e”(Makefile中不存在同名的变量定义,此变量没有被替代)。参考 8.7 make的命令行选项 一节

4.       environment override

变量“VARIABLE”是一个系统环境变量,并且make使用了命令行选项“-e”。Makefile中存在一个同名的变量定义,使用“make -e”时环境变量值替代了文件中的变量定义。 参考 8.7 make的命令行选项 一节

5.       file

变量“VARIABLE”在某一个makefile文件中定义。

6.       command line

变量“VARIABLE”在命令行中定义。

7.       override

变量“VARIABLE”在makefile文件中定义并使用“override”指示符声明。

8.       automatic

变量“VARIABLE”是自动化变量。参考 9.5.3 自动化变量 一节 

函数“origin”返回的关于变量的信息对我们书写Makefile是相当有用的,可以使我们在使用一个变量之前对它的值的合法性进行判断。假设在Makefile其包了另外一个名为bar.mkmakefile文件。我们需要在bar.mk中定义变量“bletch”(无论它是否是一个环境变量),保证“make –f bar.mk”能够正确执行。另外一种情况,当Makefile包含bar.mk,在Makefile包含bar.mk之前有同样的变量定义,但是我们不希望覆盖bar.mk中的“bletch”的定义。一种方式是:我们在bar.mk中使用指示符“override”声明这个变量。但是它所存在的问题时,此变量不能被任何方式定义的同名变量覆盖,包括命令行定义。另外一种比较灵活的实现就是在bar.mk中使用“origin”函数,如下:

ifdef bletch

ifeq "$(origin bletch)" "environment"

bletch = barf, gag, etc.

endif

endif

这里,如果存在环境变量“bletch”,则对它进行重定义。 

ifneq "$(findstring environment,$(origin bletch))" ""

bletch = barf, gag, etc.

endif 

这个例子实现了:即使环境变量中已经存在变量“bletch”,无论是否使用“make -e”来执行Makefile,变量“bletch”的值都是“barf,gag,etc”(在Makefile中所定义的)。环境变量不能替代文件中的定义。

如果“$(origin bletch)”返回“environment”或“environment override”,都将对变量“bletch”重新定义。关于函数“firststring”可参考 7.2 文本处理函数 一节

1.10    shell函数

shell函数不同于除wildcard”函数之外的其它函数。make可以使用它来和外部通信。

Ø        函数功能:函数“shell”所实现的功能和shell中的引用(``)相同。实现了命令的扩展。意味着需要一个shell 命令作为它的参数,而返回的结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数的返回结果中的所有换行符(“\n”)或者一对“\n\r”替换为单空格;并去掉末尾的回车符号(“\n”)或者“\n\r。函数展开式时,它所调用的命令(它的参数)得到执行。(可参考 2.9 make如何解析makefile 一节)。除了对它的引用出现在规则的命令行中和递归的变量定义引用以外,其它决大多数情况下,make在读取Makefile时函数shell就被扩展。

Ø         返回值:函数“shell”的参数在shell中的执行结果。

Ø         函数说明:函数本身的返回值是其参数的执行结果,没有进行任何处理。对结果的处理是由make进行的。当对函数的引用出现在规则的命令行中,命令行在执行时函数引用才被展开。展开过程函数参数的执行时在另外一个shell进程中完成的,因此对于出现在规则命令行的多级“shell”函数引用需要谨慎处理,否则会影响效率(每一级的“shell”函数的参数都会有各自的shell进程)。

示例1

contents := $(shell cat foo)

将变量“contents”赋值为文件“foo”的内容,文件中的行在变量中使用空格(而不是换行符)分割。

示例2

files := $(shell echo *.c)

将变量“files”赋值为当前目录下所有.c文件的列表(文件名之间使用空格分割)。在shell中之行的命令是“echo *.c”,它会返回当前目录下的所有.c文件列表。上例的执行结果和函数“$(wildcard *.c)的结果相同,除非你使用的是一个奇怪的shell

注意:通过上边的两个例子我们可以看到,在引用“shell”函数的变量定义使用直接展开式定义。这样就保证了函数的展开在make读入Makefile的过程中完成的。后续对此变量的引用就不会有展开过程。这样可以防止规则命令行中的变量引用在命令行执行时展开的情况发生(因为展开“shell”函数需要另外的shell进程完成,影响命令的执行效率)。这也是我们建议的方式。

1.11    make的控制函数

make提供了两个控制make运行方式的函数。通常它们用在Makefile中,当make执行过程中检测到某些错误是为用户提供消息,并且可以控制make过程是否继续。

Ø        函数功能:产生致命错误,并提示“TEXT…”信息给用户,之后退出make的执行。需要说明的是:“error”函数是在函数展开式(函数被调用时)才提示信息并结束make进程。因此如果函数出现在命令中或者一个递归的变量定义中时,在读取Makefile时不会出现错误。而只有包含 error”函数引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息“TEXT…”同时make退出执行。

Ø         返回值:空字符

Ø        函数说明:error”函数一般不出现在直接展开式的变量定义中,否则在make读取Makefile时将会提示致命错误。关于递归展开和直接展开可参考 5.2 两种变量定义 一节

假设我们的Makefile中包含以下两个片断;

示例1

 

ifdef ERROR1

$(error error is $(ERROR1))

endif

 

make读取Makefile时,此定义之前已经定义变量“EROOR1,那么make将会提示致命错误信息“$(ERROR1)”并退出。关于“ifdef”可参考 6.2.1.3 关键字“ifdef一小节。

示例2

 

ERR = $(error found an error!)

 

.PHONY: err

err: ; $(ERR)

 

这个例子,在make读取Makefile时不会出现致命错误。只有目标“err”被作为一个目标被执行时才会出现。

Ø         函数功能:函数“warning”类似于函数“error”,区别在于它不会导致致命错误(make不退出),而只是提示“TEXT…”,make的执行过程继续。

Ø         返回值:空字符

Ø         函数说明:用法和“error”类似,展开过程相同。

阅读(1067) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~