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”为空)。
第二行才是我们需要显示的系统环境变量“PATH”的值(value函数得到变量“FOO”的值为“$PATH”)。
Ø函数功能:函数“eval”是一个比较特殊的函数。使用它可以在Makefile中构造一个可变的规则结构关系(依赖关系链),其中可以使用其它变量和函数。函数“eval”对它的参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。展开的结果可以包含一个新变量、目标、隐含规则或者是明确规则等。也就是说此函数的功能主要是:根据其参数的关系、结构,对它们进行替换展开。
Ø返回值:函数“eval”的返回值时空,也可以说没有返回值。
Ø函数说明:“eval”函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容时由make解析时展开的。明确这一过程对于使用“eval”函数非常重要。理解了函数“eval”二次展开的过程后。实际使用时,如果在函数的展开结果中存在引用(格式为:$(x)),那么在函数的参数中应该使用“$$”来代替“$”。因为这一点,所以通常它的参数中会使用函数“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文件和指定的库文件进行链接。
“$(foreach prog,$(PROGRAM),$(eval $(call PROGRAM_template,$(prog))))”展开为:
server : $(server_OBJS) –l$(server_LIBS)
client : $(client_OBJS) –l$(client_LIBS)
函数“origin”和其他函数不同,函数“origin”的动作不是操作变量(它的参数)。它只是获取此变量(参数)相关的信息,告诉我们这个变量的出处(定义方式)。
Ø函数语法:
$(origin VARIABLE)
Ø函数功能:函数“origin”查询参数“VARIABLE”(一个变量名)的出处。
Ø函数说明:“VARIABLE”是一个变量名而不是一个变量的引用。因此通常它不包含“$”(当然,计算的变量名例外)。
Ø返回值:返回“VARIABLE”的定义方式。用字符串表示。
函数的返回情况有以下几种:
1.undefined
变量“VARIABLE”没有被定义。
2.default
变量“VARIABLE”是一个默认定义(内嵌变量)。如“CC”、“MAKE”、“RM”等变量。如果在Makefile中重新定义这些变量,函数返回值将相应发生变化。
3.environment
变量“VARIABLE”是一个系统环境变量,并且make没有使用命令行选项“-e”(Makefile中不存在同名的变量定义,此变量没有被替代)。
4.environment override
变量“VARIABLE”是一个系统环境变量,并且make使用了命令行选项“-e”。Makefile中存在一个同名的变量定义,使用“make -e”时环境变量值替代了文件中的变量定义。
5.file
变量“VARIABLE”在某一个makefile文件中定义。
6.command line
变量“VARIABLE”在命令行中定义。
7.override
变量“VARIABLE”在makefile文件中定义并使用“override”指示符声明。
8.automatic
变量“VARIABLE”是自动化变量。
函数“origin”返回的变量信息对我们书写Makefile是相当有用的,可以使我们在使用一个变量之前对它值的合法性进行判断。假设在Makefile其包了另外一个名为bar.mk的makefile文件。我们需要在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”重新定义。
shell函数不同于除“wildcard”函数之外的其它函数。make可以使用它来和外部通信。
Ø函数功能:函数“shell”所实现的功能和shell中的引用(``)相同。实现对命令的扩展。这就意味着需要一个shell命令作为此函数的参数,函数的返回结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数返回结果中的所有换行符(“\n”)或者一对“\n\r”替换为单空格;并去掉末尾的回车符号(“\n”)或者“\n\r”。进行函数展开式时,它所调用的命令(它的参数)得到执行。除对它的引用出现在规则的命令行和递归变量的定义中以外,其它决大多数情况下,make是在读取解析Makefile时完成对函数shell的展开。
Ø返回值:函数“shell”的参数(一个shell命令)在shell环境中的执行结果。
Ø函数说明:函数本身的返回值是其参数的执行结果,没有进行任何处理。对结果的处理是由make进行的。当对函数的引用出现在规则的命令行中,命令行在执行时函数才被展开。展开时函数参数(shell命令)的执行是在另外一个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进程完成,影响命令的执行效率)。这也是我们建议的方式。
make提供了两个控制make运行方式的函数。通常它们用在Makefile中,当make执行过程中检测到某些错误是为用户提供消息,并且可以控制make过程是否继续。
Ø函数功能:产生致命错误,并提示“TEXT…”信息给用户,并退出make的执行。需要说明的是:“error”函数是在函数展开式(函数被调用时)才提示信息并结束make进程。因此如果函数出现在命令中或者一个递归的变量定义中时,在读取Makefile时不会出现错误。而只有包含“error”函数引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息“TEXT…”同时退出make。
Ø返回值:空
Ø函数说明:“error”函数一般不出现在直接展开式的变量定义中,否则在make读取Makefile时将会提示致命错误。
假设我们的Makefile中包含以下两个片断;
示例1:
ifdef ERROR1
$(error error is $(ERROR1))
endif
make读取解析Makefile时,如果只起那已经定义变量“EROOR1”,make将会提示致命错误信息“$(ERROR1)”并退出。
示例2:
ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR)
这个例子,在make读取Makefile时不会出现致命错误。只有目标“err”被作为一个目标被执行时才会出现。
Ø函数功能:函数“warning”类似于函数“error”,区别在于它不会导致致命错误(make不退出),而只是提示“TEXT…”,make的执行过程继续。
Ø返回值:空
Ø函数说明:用法和“error”类似,展开过程相同。