函数
GNUmake支持内置函数和用户自定义函数。函数调用看起来像是变量引用,只不过多了用逗号隔开的参数而已。所有的函数都具有相同的形式:$(function-name
arg1[, argn]
)
$之后是函数的名称,接着是函数的参数,参数之间以逗号隔开。
内置函数
内置函数是内嵌于make程序内的函数,可以直接使用。
字符串函数
$(filter
pattern ..., text
)
filter
函数将text
视为一系列以空格隔开的单词,与pattern
比较之后,返回符合的单词(模式必须与单词相匹配,而不能只匹配一部分)。模式可以有多个,只要符合其中一个就会被匹配。模式中采用%
作为通配符,当有多个%
出现在一个模式中,只有第一个作为通配符,其他的作为字面值。
$(filter-out
pattern ..., text
)
filter-out
函数刚好与filter
相反,用来选出与模式不相符的每个单词。
text = apple pear banana watermelon orange pineapple
filtered = $(filter %apple pear %ge,$(text))
not_filtered = $(filter-out %apple pear %.ge, $(text))
all :
@echo $(filtered)
@echo $(not_filtered)
$ make
apple pear orange pineapple
banana watermelon orange
$(findstring
string...,text
)
此函数将会从text
里搜索string
。如果找到了就返回该字符串,否则返回空string
没有模式匹配功能。
$(subst
search-string,replace-string,text
)
这是一个不具有模式匹配的搜索替换函数。search-string
和replace-string
不必是整个单词。
$(patsubst
search-string,replace-string,text
)
这个替换函数具有模式匹配功能。同样的,只能用一个%
。
sources = hello.c lib.c main.c tom_hello.c tom_lib.c
objects = $(subst .c,.o, $(sources))
has_main.c = $(findstring main.c, $(sources))
has_main = $(findstring main, $(sources))
no_domain = $(findstring domain, $(sources))
patsubsted = $(patsubst tom%,jerry%, $(sources))
all:
@echo $(objects)
@echo $(has_main.c)
@echo $(has_main)
@echo $(no_domain)
@echo $(patsubsted)
$ make
hello.o lib.o main.o tom_hello.o tom_lib.o
main.c
main
hello.c lib.c main.c jerry_hello.c jerry_lib.c
$(words
text
)
此函数返回text
中单词的数量。
$(word
n,text
)
此函数返回text
中的第n
个单词。第一个单词编号为1,如果n
的值大于单词的总数,返回空值。
$(firstword
text
)
返回text
中的第一个单词。等效与$(word 1,text)
。
$(wordlist
start,end,text
)
此函数返回text
中范围从start
(含)到end
(含)的单词。
$(sort
list
)
sort
函数会排序它的list
参数并且移除重复的单词。此函数会返回按照字典顺序的不重复的单词列表,以空格分隔。而且,sort
还会删除前导和结尾的空格。这里的sort
是make
内部实现的函数,不是shell
里的程序。
$(shell
command
)
shell
的参数会被扩展(就像所有其他参数)并且传递给subshell
执行。然后make
读取command
的标准输出,并将之作为函数的返回值。输出中所出现的一系列换行符会被缩减成单一的空格,任何末尾的换行被删除。标准错误并不会被返回。
$(wildcard
pattern...
)
wildcard
的参数是一个模式列表,他会对每个模式进行扩展。如果扩展的模式找不到符合的文件,返回空字符串。该模式为shell的glob匹配。
如果想要判断某个文件是否存在,就可以使用wildcard
:
dot-emacs-exists := $(wildcard ~/.emacs)
$(dir
list...
)
dir
函数返回list
中每个单词的目录部分。
$(notdir
name...
)
返回每个单词的文件名部分。
$(suffix
name...
)
返回由每个单词的扩展名构成的单词列表
$(basename
name...
)
返回由每个单词的非扩展名部分组成的单词列表
$(addsuffix,
suffix,name...
)
返回由name
单词列表中的每个单词前加上suffix
前缀构成的新的单词表。
$(addprefix,
prefix,name...
)
$(join
prefix-list,suffix-list
)
join
函数把prefix-list
的第i个单词与suffix-list
的第i个单词连在一起构成新序列的第i个单词。
流程控制函数
$(if
condition,then-part,else-part
)
if
函数(可不是条件指令ifne,ifeq,ifdef
等)会根据条件表达式的求值结果,从两个宏中选择一个进行扩展。
如果condition
扩展之后包含任何字符(包括空格),求值结果为真,会对then-part
进行扩展;否则,如果condition
扩展之后为空,会对else-part
进行扩展。
make
对condition
求值时,首先会移除前导及末尾的空格。
$(error
text
)
error
函数会使make
输出text
错误信息和当前makefile
的名称行号之后以2
这个状态结束运行。
$(warn
text
)
与error
类似,输出text
的内容,但并不退出。扩展结果为空串。$(foreach
variable,list,body
)
当foreach
函数执行的时候,它将list
中的每个单词作为body
中的variable
进行操作,返回操作的结果。
sources = fat32.c format.c alloca.c main.c
dirs = src bin inc
objects = $(foreach var,$(sources), $(subst .c,.o, $(var)))
full_path = $(foreach var, $(sources), $(addprefix src/, $(var)))
do_mkdirs = $(foreach var, $(dirs), $(shell mkdir -p $(var)))
all:
@echo $(objects)
@echo $(full_path)
@echo $(do_mkdirs)
$ make
fat32.o format.o alloca.o main.o
src/fat32.c src/format.c src/alloca.c src/main.c
#产生了三个文件夹
eval函数
$(eval
contents
)
这个函数很重要。它用来将它后面的文本直接放入make解析器。
首先make会扫描eval的参数并对其中的变量进行扩展,然后make会解析文本进行求值的动作(这里也包含了一次扩展动作,因为普通的在makefile中的文本也会扩展一次),就好像它是输入的文本一样。
eval函数经常用与含有多行文本的宏的展开。一个解析器顶层的宏被扩展成多行文本是不合法的,但是用eval函数可以将展开后的宏直接当做输入的文本,再展开一次之后就放入数据库中了。而eval函数本身的展开结果为空。
注意:定义复杂的宏时要考虑二次展开的问题,然后用eval函数引用这个宏。
用户自定义函数
可以用define定义一个自定义的宏:
define macor-name
contents
endef
用户自定义函数是带有参数的宏,宏定义中的$1
、$2...
是函数的形参,宏的定义就是函数的定义。
调用一个自定义函数的形式是:
$(call
macro-name[,param1 ...]
)
。
call
是一个内置函数,call
扩展它的第一个参数并把其他参数依次替换到宏定义中的$1
、$2...
的地方。macro-name
之后是宏的参数,以逗号隔开。在函数的内部可以使用$0
来访问函数名。
call
的参数传递机制很简单。调用时可以为call
指定任意个参数。如果宏定义内引用了$n
,但是调用时并未指定相应的参数,那么该变量就为空值。如果call
的参数比宏的$n
引用还多,则被忽略。
注意使用函数是不要随意加前导空格,否则可能回产生难以排查的错误。(但降低了可读性)