Makefile中的函数
GNU make 支持内置函数以及用户自定义函数。函数调用(function invocation) 看起来非常像变量引用(variable reference) , 不过前者包含了一个或多个被逗号隔开的参数。大部分内置数扩展后多半会被赋值给一个变量或是传递给一个子shell 。用户自定义函数则存储在变量或宏中,而且会接收调用者(caller) 传来的一个或多个参数。
一、Make的内嵌函数
GNU Make的函数提供了处理文件名、变量、文本和命令的方法。函数将使得我们的Makefile书写得更加灵活和健壮。可以在需要的地方调用函数来处理指定的文本(需要处理的文本作为函数的参数),函数在调用它的地方被替换为它的处理结果。
1.1 函数的调用
make函数的调用格式类似于变量的引用格式,以$开头表示一个引用的过程。语法格式如下:
$(<function> <arguments>)
或是:
${<function> <arguments>}
<function>为make内嵌的函数名。对于用户自己的函数需要通过make的“call”函数来间接调用。<arguments>为函数的参数。函数名和参数之间以“空格”分隔。多个参数间以逗号( , ) 分隔。- 以“$”开头,使用成对的圆括号或花括号把函数名和参数括起。建议在变量的引用和函数的引用中统一使用圆括号。
1.2 文本(字符串)处理函数
1.2.1 字符串替换函数——subst
$(subst <from>,<to>,<text>)
- 功能:把字串
<text>中的<from>字符串替换成<to>。 - 返回:函数返回被替换过后的字符串。
- 示例:
$(subst ee,EE,feet on the street)
# 把feet on the street 中的ee 替换成EE ,返回结果是fEEt on the strEEt 。
1.2.2 模式替换函数——patsubst
$(patsubst <pattern>,<replacement>,<text>)
- 功能:搜索
<text>中以空格分开的单词,将符合模式<pattern>替换为<replacement>。参数<text>单词之间的多个空格在处理时被合并为一个空格,并忽略前导和结尾空格。 <pattern>可以包括通配符% ,表示任意长度的字串。如果<replacement>中也包含% ,那么,<replacement>中的这个% 将是<pattern>中的那个% 所代表的字串。- 返回:函数返回被替换过后的字符串。
- 示例:
$(patsubst %.c,%.o,x.c.c bar.c)
# 把字串x.c.c bar.c 符合模式%.c 的单词替换成%.o ,返回结果是x.c.o bar.o
1.2.3 去空格函数——strip
$(strip <string>)
- 功能:去掉
<string>字串中开头和结尾的空字符,并将其中多个连续空字符合并为一个空字符。空字符包括空格、[Tab]等不可显示字符。 - 返回:无前导和结尾空字符、使用单一空格分割的多单词字符串。
- 示例:
str = a b c
lostr = $(strip $(str))
# 结果是“a b c”。
1.2.4 查找字符串函数——findstring
$(findstring <find>,<in>)
- 功能:在字串
<in>中查找<find>字串。字串<in>之中可以包含空格、[Tab]。搜索需要是严格的文本匹配。 - 返回:如果找到,那么返回
<find>,否则返回空字符串。 - 示例:
$(findstring a,a b c)
$(findstring a,b c)
# 第一个函数返回 a 字符串,第二个返回空字符。
1.2.5 过滤函数——filter
$(filter <pattern...>,<text>)
• 名称:过滤函数
• 功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式,模式表达式由空格分隔。
• 返回:返回由空格分隔所有符合模式<pattern>的字串。
• 示例:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)cc $(filter %.c %.s,$(sources)) -o foo
# $(filter %.c %.s,$(sources)) 返回的值是foo.c bar.c baz.s 。
1.2.6 反过滤函数——filter-out
$(filter-out <pattern...>,<text>)
- 功能:以
<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割 - 返回:返回不符合模式
<pattern>的字串。 - 示例:
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
# $(filter-out $(mains),$(objects)) 返回值是foo.o bar.o 。
1.2.7 排序函数——sort
$(sort <list>)
- 功能:给字符串
<list>中的单词以首字母排序(升序),并去掉重复的单词。 - 返回:空格分隔的没有重复单词的已排序的字符串。
- 示例:
$(sort foo bar lose)
# 返回bar foo lose 。
1.2.8 取单词函数——word
$(word <n>,<text>)
- 功能:取字符串
<text>中第<n>个单词。(<n>的值从1开始) - 返回:返回字符串
<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回空字符串。如果<n>为0,则出错! - 示例:
$(word 2, foo bar baz)
# 返回值是bar 。
1.2.9 取字串函数——wordlist
$(wordlist <s>,<e>,<text>)
- 功能:从字符串
<text>中取从<s>开始到<e>的单词串。<s>和<e>表示单词在字符串中位置的数字(都是从1开始的数字)。 - 返回:返回字符串
<text>中从第<s>个单词到第<e>个单词之间的字符串。如果<s>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束的单词串。 - 示例:
$(wordlist 2, 3, foo bar baz)
# 返回值是bar baz 。
1.2.10 统计单词数目函数——words
$(words <text>)
- 功能:统计
<text>中字符串中的单词个数。 - 返回:返回
<text>中的单词数。 - 示例:
$(words, foo bar baz)
# 返回值是3。所以取<text> 中最后的一个单词:$(word $(words <text>),<text>) 。
1.2.11 取首单词函数——firstword
$(firstword <text>)
- 功能:取字符串
<text>中的第一个单词。 - 返回:返回字符串
<text>的第一个单词。 - 示例:
$(firstword foo bar)
# 返回值是foo。。函数“firstword”实现的功能等效于“$(word 1,<text>) ”
1.3 文件名处理函数
1.3.1 取目录函数—dir
$(dir <names...>)
- 功能:从文件名序列
<names>中取出目录部分。目录部分是指最后一个反斜杠(/ )之前的部分。如果没有反斜杠,那么返回./ 。 - 返回:返回文件名序列
<names>的目录部分。 - 示例:
$(dir src/foo.c hacks)
# 返回值是src/ ./ 。
1.3.2 取文件名函数——notdir
$(notdir <names...>)
- 功能:从文件名序列
<names>中取出非目录部分。非目录部分是指最后一个反斜杠(/ )之后的部分。
如果<names>中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空串代替,因此当<names>中存在多个这样的文件名时,返回结果中分割各个文件名的空格数目将不确定! - 返回:返回文件名序列
<names>的非目录部分。 - 示例:
$(notdir src/foo.c hacks)
# 返回值是foo.c hacks 。
1.3.3 取后缀函数——suffix
$(suffix <names...>)
- 功能:从文件名序列
<names>中取出各个文件名的后缀。后缀是文件名中最后一个以点“.”开始的部分(包含点号),如果文件名中不包含一个点号,则为空。 - 返回:返回以空格分隔的文件名序列
<names>的后缀序列,如果文件没有后缀,则返回空字串。 - 示例:
$(suffix src/foo.c src-1.0/bar.c hacks)
# 返回值是.c .c。
1.3.4 取前缀函数——basename
$(basename <names...>)
- 功能:从文件名序列
<names>中取出各个文件名的前缀部分。前缀部分指的是文件名中最后一个点号之前的部分。如果<names>中包含没有后缀的文件名,此文件名不改变。 - 返回:返回文件名序列
<names>的前缀序列。 - 示例:
$(basename src/foo.c src-1.0/bar.c hacks)
# 返回值是src/foo src-1.0/bar hacks 。
1.3.5 加后缀函数——addsuffix
$(addsuffix <suffix>,<names...>)
- 功能:把后缀
<suffix>加到<names>中的每个单词后面。 - 返回:返回加过后缀的文件名序列。
- 示例:
$(addsuffix .c,foo bar)
# 返回值是foo.c bar.c 。
1.3.6 加前缀函数——addprefix
$(addprefix <prefix>,<names...>)
- 功能:把前缀
<prefix>加到<names>中的每个单词后面。 - 返回:返回加过前缀的文件名序列。
- 示例:
$(addprefix src/,foo bar)
# 返回值是src/foo src/bar 。
1.3.7 单词连接函数——join
$(join <list1>,<list2>)
- 功能:把
<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list1>中。 - 返回:返回连接过后的字符串。
- 示例:
$(join aaa bbb , 111 222 333)
# 返回值是aaa111 bbb222 333 。
1.3.8 获取匹配模式文件名函数——wildcard
$(wildcard <pattern>)
- 功能:列出当前目录下所有符合模式
<pattern>格式的文件名。<pattern>使用shell可识别的通配符,包括“?”(单字符)、“*”(多字符)等。 - 返回:空格分割的、存在当前目录下的所有符合模式
<pattern>的文件名。 - 示例:
$(wildcard *.c)
# 返回值为当前目录下所有.c源文件列表。
1.4 foreach 函数
函数“foreach”是一个循环函数。类似于Linux的shell中的循环(for语句)。
函数语法:
$(foreach <var>,<list>,<text>)
- 功能:把参数
<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach 函数的返回值。<text>中的变量或者函数引用在执行时才被展开,因此如果在<text>中存在对<var>的引用,那么<var>的值在每一次展开式将会到的不同的值。 - 返回:空格分割的多次表达式
<text>的计算的结果。 - 示例:
names := a b c d
files := $(foreach n,$(names),$(n).o)
# $(files) 的值是a.o b.o c.o d.o。
注意,foreach 中的<var>参数是一个临时的局部变量,foreach 函数执行完后,参数<var>的变量将不在作用,其作用域只在foreach 函数当中。
变量<var>的名字,我们建议使用一个单词、最好能够表达其含义的名字,不要使用一个奇怪的字符串作为变量名。虽然在可能在执行时没有错误,但是可能会让人很费解。
1.5 if 函数
函数“if”提供了一个在函数上下文中实现条件判断的功能。就像make所支持的条件语句ifeq一样。
函数语法:
$(if <condition>,<then-part>)
或是
$(if <condition>,<then-part>,<else-part>)
- 功能:
<condition>参数在函数执行时忽略其前导和结尾空字符并展开。如果其结果为非空字符串,则条件为真,把第二个参数<then-part>作为函数的表达式,函数的返回值就是第二表达式的计算结果;如果<condition>的展开结果为空,则<else-part>作为函数的表达式,返回结果为第三个表达式的计算结果。 - 返回:根据条件决定函数的返回值是
<then-part>或者<else-part>两个之一的计算结果。当不存在第三个参数<else-part>,并且<condition>展开为空,函数返回空。 - 示例:
SUBDIR += $(if $(SRC_DIR) $(SRC_DIR),/home/src)
# 函数的结果是:如果“SRC_DIR”变量值不为空,则将变量“SRC_DIR”指定的目录作为一个子目录;否则将目录“/home/src”作为一个子目录。
1.6 call 函数
“call”函数是唯一一个可以创建定制参数化的函数的引用函数。我们可以将一个变量定义为一个复杂的表达式,用“call”函数根据不同的参数对它进行展开来获得不同的结果。
函数语法:
$(call <variable>,<parm1>,<parm2>,...,<parmn>)
- 功能:在执行时,将参数
<parm>依次赋给定义在<variable>参数中的临时变量,如$(1)、$(2)等。执行时变量<variable>被展开为在函数上下文有效的临时变量,变量定义中的$(1)作为第一个参数,并将函数参数值中的第一个参数赋值给它;变量中的$(2)一样被赋值为函数的第二个参数值;依此类推(变量$(0)代表变量“VARIABLE”本身)。之后对变量<variable>表达式的计算值。函数中多个<parm>之间使用逗号分割。 - 返回:替换之后
<variable>表达式的计算值。 - 示例:
reverse = $(2) $(1)
foo = $(call reverse,a,b)
# 变量 foo 的值是a b。
<variable>参数定义顺序可以根据需要来调整,并不是需要按照$(1)、$(2)、$(3)……这样的顺序来定义。
reverse = $(2) $(1)
foo = $(call reverse,a,b)
# 此时的 foo 的值就是b a。
“call”函数会保留出现在其参数值列表中的空字符。因此在使用参数值时对空格处理要格外小心。如果参数中存在多余的空格,函数可能会返回一个莫名奇妙的值。为了安全,在变量作为“call”函数参数值之前,应去掉其值中的多余空格(可以使用“strip”函数)。
函数“call”以可以套嵌使用。每一层“call”函数的调用都为它自己的局部变量$(1)等赋值,覆盖上一层函数为它所赋的值。
1.7 origin 函数
origin 函数不像其它的函数,他并不操作变量的值,他只是告诉你这个变量是哪里来的。
函数语法:
$(origin <variable>)
- 功能:查询参数
<variable>(通常是一个变量名)的出处。 - 返回:返回
<variable>的定义方式。用字符串表示。
<variable>是变量的名字,不应该是引用。所以不要在<variable>中使用$字符。(当然,计算的变量名例外)。
函数的返回情况有以下几种:
- undefined: 如果````` 从来没有定义过,origin 函数返回这个值undefined
- default: 如果
<variable>是一个默认的定义,比如“CC”这个变量。 - environment: 如果
<variable>是一个环境变量,并且make没有使用命令行选项“-e”。 - file: 如果
<variable>这个变量被定义在Makefile 中。 - command line: 如果
<variable>这个变量是被命令行定义的。 - override: 如果
<variable>是被override 指示符重新定义的。 - automatic: 如果
<variable>是一个命令运行中的自动化变量。
函数“origin”返回的关于变量的信息对我们书写Makefile是相当有用的,可以使我们在使用一个变量之前对它的值的合法性进行判断。比如,内核顶层Makefile判断变量O是否来源于命令行,并定义内核编译输出目录:
ifeq ("$(origin O)", "command line")KBUILD_OUTPUT := $(O)
endif
1.8 shell 函数
函数语法:
$(shell command)
需要一个shell 命令作为它的参数,而返回的结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数的返回结果中的所有换行符(“\n”)或者一对“\n\r”替换为单空格;并去掉末尾的回车符号(“\n”)或者“\n\r”。函数展开式时,它所调用的命令(它的参数)得到执行。
我们可以用操作系统命令以及字符串处理命令awk,sed 等等命令来生成一个变量,如:
contents := $(shell cat foo)
files := $(shell echo *.c)
1.9 make 的控制函数
make提供了两个控制make运行方式的函数。通常它们用在Makefile中,当make执行过程中检测到某些错误是为用户提供消息,并且可以控制make过程是否继续。
(1) 错误信息
函数语法:
$(error TEXT…)
产生致命错误,并提示“TEXT…”信息给用户,之后退出make的执行。
需要说明的是:“error”函数是在函数展开式(函数被调用时)才提示信息并结束make进程。因此如果函数出现在命令中或者一个递归的变量定义中时,在读取Makefile时不会出现错误。而只有包含“error”函数引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息“TEXT…”同时make退出执行。
# 示例一:
ifdef ERROR_001$(error error is $(ERROR_001))
endif# 示例二:
ERR = $(error found an error!)
.PHONY: err
err:$(ERR)
示例一会在变量ERROR_001 定义了后执行时产生error 调用,而示例二则在目录err 被执行时才
发生error 调用。
(2)警告信息
函数语法:
$(warning TEXT…)
仅提示“TEXT…”警告信息给用户,make的执行过程继续。
二、用户自定义函数
Makefile 支持强大的自定义函数功能,通过define和endef关键字来定义自定义函数(实际上是多行变量),可以通过call函数调用并传递参数。自定义函数可以包含多个命令和内置函数,并且可以接受参数。
基本语法:
define 函数名
函数体
endef
调用自定义函数:
$(call 函数名, 参数1, 参数2, ...)
在函数体中,我们可以使用$(1), $(2)等来引用传递的参数。注意,$(0)是函数名。
