函数(七)

本文详细介绍了Makefile中的函数用法,包括函数调用语法、字符串操作函数、文件名操作函数、条件函数、foreach函数等。通过对函数的掌握,可以更好地在Makefile中处理文本、计算文件、执行recipe,提高自动化构建的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

8 函数

函数允许你在makefile中处理文本,用于计算文件,操作和执行recipes。在函数调用中提供函数名和函数操作的参数。函数处理的结果替换到函数在makefile中调用的位置,就像变量被替换一样。

8.1 函数调用语法

函数调用类似于变量引用。它可以出现在任何变量引用可以出现的地方,使用与变量引用同样的扩展规则。函数调用语法如下:

$(function arguments)

或者:

${function arguments}

这里function是函数名,是make函数列表中的一个。可以使用call创建自己的函数。

arguments表示函数的参数,与函数名用若干空格或tab隔开,如果有多个参数,用逗号隔开。空格和逗号不是参数值的一部分。函数调用时两边的分隔符(圆括号或花括号)只能成对出现在变量中,其它种类的分隔符可以单独出现。如果参数中包含其它的函数调用或变量引用,最好使用相同种类的分隔符,例如,写成$(subst a,b,$(x)),而不是$(subst a,b,${x})。这样更清晰,因为只有一种类型的分隔符需要被匹配。

写作参数的文本通过变量和函数调用处理,最终生成参数的值,也就是函数操作的文本。替换的顺序和参数出现的顺序一致。

逗号、未配对的圆括号或花括号不可以出现在参数文本中,前导空格不可以当作第一个参数文本的一部分。这些字符通过变量替换放到参数值当中。下面例子中,前面定义的变量commaspace,它们的值分别是逗号和空格字符,然后需要这些字符的地方,使用这两个变量替换即可:

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的文本替换:每个在text中的from都替换成to。结果是替换后的字符串,例如:

    $(subst ee,EE,feet on the street)
    

    结果为fEEt on the strEEt

  • $(patsubst pattern,replacement,text)

    查找text中用空格分隔的匹配pattern模板的单词,并用replacement替换之。这里pattern可能包含%通配符,匹配一个单词的任意多个任意字符。如果replacement中也包含%,那这个%pattern中匹配的部分替换。只有patternreplacement中的第一个%会被当作通配符,之后的%表示字符本身。
    patsubst函数调用中,%字符可以使用\引用。反斜杆\也可以引用反斜杆本身。当比较文件名或进行词干替换之前,作为引用字符的\会被移除。例如,模板the\%weird\\%pattern\\使用通配符%分割为the%weird\pattern\\两部分。最后两个反斜杆被保留,因为它们不能影响%字符。
    单词之间的空格被视为一个空格,开头和结尾的空格被忽略。
    例如:

    $(patsubst %.c,%.o,x.c.c bar.c)
    

    结果为x.c.o bar.o

    $(var:pattern=replacement)
    

    等效于:

    $(patsubst pattern,replacement,$(var))
    

    第二个简化patsubst的一个普遍用法是替换文件的后缀:

    $(var:suffix=replacement)
    

    等效于:

    $(patsubst %suffix,%replacement,$(var))
    

    例如,有一个objects文件列表:

    objects = foo.o bar.o baz.o
    

    为获得对应的源文件列表,可以写作:

    $(objects:.o=.c)
    

    而不是:

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

    用来移除字符串开头和结尾的空格,并且字符串中的的连续多个空格会替换为一个。因此,$(strip a b c )结果为'a b c'
    函数strip在连接条件指令时非常有用。当使用ifeqifneq将字符串与空字符串''比较时,你通常想让只包含空格的字符串作为空字符串。
    因此,下面的语句可能得不到想要的结果:

    .PHONY: all
    ifneq "$(needs_made)" ""
    all: $(needs_made)
    else
    all:;@echo 'Nothing to make!'
    endif
    

    ifneq指令中的变量引用$(needs_made)替换为函数调用$(strip $(needs_made)),会使程序更健壮。

  • $(findstring find,in)

    in中查找find。如果找到,返回find,否则结果为空。你可以在条件测试中使用这个函数判断给定字符串中是否包含一个特定子字符串。下面两个例子:

    $(findstring a,a b c)
    $(findstring a,b c)
    

    分别返回'a'''

  • $(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.cbar.cbaz.sugh.h,但是在编译时只使用foo.cbar.cbaz.s

  • $(filter-out pattern...,text)

    返回text中所有不匹配pattern的以空格分隔的单词,删除所有匹配的单词。相对于函数filter
    例如,给定:

    objects=main1.o foo.o main2.o bar.o
    mains=main1.o main2.o
    

    下面的语句生成一个不包含mains中文件的所有objects文件列表:

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

    按字典顺序排列list中的单词,移除重复的单词。输出是一个用单个空格分开的单词列表。因此:

    $(sort foo bar lose)
    

    返回bar foo lose
    如果你不关心单词顺序时,可以使用sort删除列表中重复的单词。

  • $(word n,text)

    返回text中第n个单词。n从1开始。如果n大于text中的单词数,返回空字符。例如:

    $(word 2, foo bar baz)
    

    返回'bar'

  • $(wordlist s,e,text)

    返回text中以序号s开始,序号e结尾(包含e)的单词列表。s以1开始,e可以以0开始。如果s大于text中单词数,返回空字符。如果e大于text中的单词数,返回s开始的所有单词的列表。如果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)功能相同,但是firstword函数由于它简单的格式被保留。

  • $(lastword names...)

    参数names是一系列以空格分开的名字。函数返回列表中最后一个名字。例如:

    $(lastword foo bar)
    

    结果为'bar'$(lastword foo bar)$(word $(words text),text)功能相同。

这儿有一个使用substpatsubst的实用示例。假设一个makefile使用VPATH变量指定了一个make查找prerequisites文件的目录列表。这个示例展示了如何让C编译器在相同的目录列表中查找头文件。

变量VPATH的值是一列由冒号分开的目录,例如'src:../headers'。首先subst函数将冒号替换为空格:

$(subst :, ,$(VPATH))

结果为'src ../headers'。然后patsubst函数用于将每个目录名加上-I标志。这些可以加入到变量CFLAGS的值当中,它会自动传递给C编译器,像这些:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

这会将文本'-Isrc -I../headers'追加到变量CFLAGS中。override指令用于保证新值被赋给CFLAGS,即使CFLAGS的值是通过命令行参数指定的。

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中文件名没有.时,结果字符串中后缀数量将少于names中文件名数量。
    例如:

    $(suffix src/foo.c src-1.c hacks)
    

    结果为'.c .c'

  • $(basename names...)

    获取names中每个文件名除后缀以外的所有部分。如果文件名中包含.,那么basename就是文件名开始到最后一个.为止的所有部分(不包含.)。目录中的.保留。如果文件名中没有.,那么整个文件名都是basename。例如:

    $(basename src/foo.c src-1.0/bar hacks)
    

    结果为'src/foo src-1.0/bar hacks'

  • $(addsuffix suffix,names...)

    参数names是一个以空格分隔的名字列表,suffix是一个单元。将suffix添加到names中每个名字的后面,最后返回由空格分隔的添加了suffix的名字列表。例如:

    $(addsuffix .c,foo bar)
    

    结果是'foo.c bar.c'

  • $(addprefix prefix,names...)

    参数names是一个以空格分隔的名字列表,prefix是一个单元。将prefix添加到names中每个名字的开头,最后返回由空格分隔的添加了prefix的名字列表。例如:

    $(addprefix src/,foo bar)
    

    结果为'src/foo src/bar'

  • $(join list1,list2)

    将两个参数中的单词分别连接在一起:两个参数中第一个单词连接成为新列表的第一个单词,以此类推。因此结果中的第n个单词是两个参数中第n个单词连接而成的。如果一个参数比另一个参数有更多的单词,那么多余的单词会原封不动的放到结果列表中。
    例如,'$(join a b,.c .o)'结果为'a.c b.o'
    列表中单词间的空格不会保留,被替换为一个空格。
    这个函数可以组合dirnotdir的结果,生成提供给这两个函数的原始文件名列表。

  • $(wildcard pattern)

    参数pattern是一个文件名模板,其中包含通配符。函数wildcard的结果是以空格分隔的目录中存在的文件名列表,它们匹配模板pattern

  • $(realpath names...)

    返回names中每个文件名的绝对目录。其中不包含.或者..等相对字符,也不包含任何重复的路径分隔符。失败时返回空字符串。

  • $(abspath names...)

    返回names中每个文件名的绝对目录。其中不包含.或者..等相对字符,也不包含任何重复的路径分隔符。注意,与realpath不同,abspath不解决符号链接,也不要求文件名指向已存在的文件或目录。使用wildcard函数测试存在性。

8.4 条件函数

有三个函数提供了条件扩展。这些函数有一个关键部分是,并不是所有的参数在初始化时就被展开。只有那些需要被展开的参数才会被展开。

  • $(if condition,then-part[,else-part])

    if函数在一个函数环境中提供条件展开(与GNU make中的条件指令不同,如ifeq)。
    第一个参数condition会先去除所有的前导空格和后缀空格,然后再展开。如果展开成任何非空字符串,那么condition为真。如果展开成空字符串,则为假。
    如果condition是真,第二个参数then-part被计算,其结果为整个if函数的结果。
    如果condition是假,第三个参数else-part被计算,其结果为整个if函数的结果。如果没有第三个参数,if返回空字符串。
    注意,只有then-partelse-part中的一个会被计算。因此,它们有副作用(例如,调用shell函数)。

  • $(or condition1[,condition2[,condition3...]])

    or函数提供了一种或操作,每个参数按顺序展开。如果有参数展开为非空字符串,则展开停止,并且展开结果就是该字符串。如果在所有参数展开之后,没有非空字符串,则表达式结果为空字符串。

  • $(and condition1[,condition2[,condition3...]])

    and函数提供了一种与操作,每个参数按顺序展开。如果任意一个展开为空字符串,则结果为空字符串。如果全部展开后没有空字符串,则结果为最后展开的字符串。

8.5 foreach函数

foreach函数与其它函数有很大不同。它会重复使用一段文本,每次都使用不同的文本替换它。其类似于shell sh中的for命令,也类似于C-shell csh中的foreach命令。

foreach的语法为:

    $(foreach var,list,text)

前两个参数varlist会最先展开,text不会同时展开。var展开的变量名依次设置成list展开的每个单词,并且同时text被展开。text中可能会包含var变量名,所以每次循环都有不同的值。

结果是多个text,其数量等于list中的单词数。这些text会使用空格连接在一起,作为foreach的结果。

下面的简单示例中,变量files是目录dirs中的所有文件的名字列表:

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

text$(wildcard $(dir)/*)dir第一个值是a,因此产生的结果为$(wildcard a/*)。第二个展开的是$(wildcard b/*)。第三个展开的是$(wildcard c/*)

与下面的例子有相同的结果:

files := $(wildcard a/* b/* c/* d/*)

text比较复杂时,可以用一个额外的变量来表示text

find_files = $(wildcard $(dir)/*)
dirs := a b c d
files := $(foreach dir,$(dirs),$(find_files))

其中使用=find_files定义为一个递归展开变量,因此可以在foreach指令中重复展开。简单展开变量不能在这儿使用,因为wildcard只会在定义find_files时被调用一次。

foreach并不会一直影响变量var,在foreach调用结束后,var又会变为调用foreach之前的样子。varlist获取的值只在执行foreach期间暂时存在。如果varforeach之前没有定义,那么在foreach之后也没有定义。

在使用复杂的变量表达式生成变量时要千万小心,因为一些有效的变量名会产生奇怪的效果,它们可能并不是你想要的。例如:

files := $(foreach Esta-escrito-en-espanol!,b c ch,$(find_files))

只有在变量find_files的值引用名为Esta-escrito-en-espanol!(这是一个很长的名字,不是吗?)的变量时才有用,但这很有可能会出错。

8.6 file函数

file函数允许makefile读写一个文件。支持两种写模式:覆盖,文本被写到文件开头,文件中之前的数据被丢弃;追加,文本写道文件的结尾,文件原有内容保留。在这两种模式下,如果文件之前不存在,都会创建文件。如果文件不能被打开写入,或者写操作失败,都会引发致命错误。

当写一个文件时,file函数扩展为一个空字符串。

当读一个文件时,file函数会扩展为文件中的内容,会去掉新行字符。如果读取一个不存在的文件,则扩展为空字符串。

file函数的语法为:

$(file op filename[,text])

file函数被计算,它的所有参数会被最先展开,然后文件filename会在op模式下被打开。

操作符op可以是>,表示文件将被新内容覆盖。>>表示追加,<表示读取文件内容。filename表示要读写的文件名。在操作符和文件名之前可以使用空格分隔。

当读文件时,不可以提供text参数。

当写文件时,text会被写入到文件中。如果text结尾没有新行字符,则会在写入时添加新行字符(即使text为空字符串)。如果text没有提供,则不会写入任何字符。

当系统命令行大小有限时,file函数很有用,例如,你的recipe运行的命令可以从文件中读取。参数前的@用于指定一个包含多个参数的文件,你可以将recipe写成如下形式:

program: $(OBJECTS)
        $(file >$@.in,$^)
        $(CMD) $(CMDFLAGS) @$@.in
        $rm $@.in

先将依赖文件$(OBJECTS)写入到文件program.in中,然后命令行$(CMD)中使用这些参数。

如果命令需要从输入文件中得到分为多行的参数,可以写成下面的形式:

program: $(OBJECTS)
        $(file >$@.in) $(foreach O,$^,$(file >>$@.in,$O))
        $(CMD) $(CMDFLAGS) @$@.in
        @rm $@.in

现将使用$(file >$@.in)创建文件,不写入任何东西。使用$(foreach O,$^,$(file >>$@.in,$O)$(OBJECTS)中的依赖项一次写入文件program.in中,每次写一行,一行中只有一个参数。

8.7 call函数

call是唯一可以用来创建新的参数化函数的函数。你可以给一个变量写一个很复杂的表达式,然后使用call调用不同的值来扩展它。

call函数的语法是:

$(call variable,param,param,...)

make扩展这个函数时,将会把每个参数依次赋值给变量$(1)$(2)等。变量$(0)variable。参数数量没有限制,没有上限,也没有下限,但是没有参数的call没有意义。

variable在这些临时赋值情形下展开成一个make变量。因此,variable值中的$(1)表示call中的第一个param

注意variable是变量名,而不是变量的引用。因此不需要$和括号。(当然,你也可以使用变量引用,如果你想要这个变量名不是一个常数的话。)

在将param参数赋值给临时变量时,call会先展开它们。这意味着variable值中包含指定展开规则的内建函数(如foreachif)的引用不能如你所望的工作。

用一些示例容易说明这一点。

这个宏只是保存它的参数:

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或其它类似内容。如果PATH的值是/bin:/home/bin$(subst :, ,$(PATH))结果为/bin /home/bin$(addsufix /$(1),$(subst :, ,$(PATH)))结果为/bin/$(1) /home/bin/$(1)$(firstword $(wildcard $(addsufix /$(1),$(subst :, ,$(PATH)))))结果为/bin/$(1)$(call /bin/$(1),ls)结果为/bin/ls

call函数可以嵌套使用。每次递归调用,call都有自己的$(1)等,其会掩盖上层call的值。例如,下面是一种map函数的实现:

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

现在你可以在一条语句中映射一个只包含一个参数的函数为多个值,如origin

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

可以类似展开为

$(call $(foreach a,$(2),$(call $(1),$(a))),origin,o map MAKE)

$(foreach a,o map MAKE,$(call origin,$(a))

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

最终o包含诸如file file default的内容。

最后有一个警告,在call的参数中添加空格要千万小心。和其它函数一样,包含在第二个之后的参数中的空格被保留,这会产生奇怪的影响。通常最安全的方式是在将参数提供给call时,去掉多余的空格。

8.8 value函数

value函数提供了一种不展开变量而使用其中的值的方式。请注意,已经展开过的变量并不会不展开。例如,你创建了一个简单展开变量,它的值在定义时就已经展开了,此时使用value函数会与使用这个变量具有相同的效果。

value的语法如下:

$(value variable)

注意variable是变量名,而不是变量引用。因此不需要写$或括号。(当然,你也可以使用变量引用,那样你就会得到一个不固定的变量名。)

这个函数的结果是包含变量值的字符串,而没有任何展开过程。例如,在下面的makefile中:

FOO = PATH

all:
        @echo $(FOO)
        @echo $(value FOO)

第一个输出行是ATH,因为$P会展开为一个make变量;而第二个输出行是你的环境变量$PATH的当前值,因为value函数避免了展开。

value函数通常与eval函数一起使用。

8.9 eval函数

eval函数非常特殊:它允许你定义新的非固定结构,它是其它变量和函数的结果。eval函数的参数被展开,然后展开的结果以makefile语法来对待。展开的结果可以定义新的make变量、目标、隐含或显式规则等。

eval函数的结果总是空字符串,因此它可以放在makefile中的任何地方,而不会引起语法错误。

一定要知道,eval参数被展开两次。第一次被eval函数展开,展开的结果被当作makefile时会被再次展开。这意味着在使用eval时,你需要转义$value函数在这种情形下很有用,可以用它来包含不需要的表达式。

下面是一个如何使用eval的例子。这个示例结合了一些其它概念和函数。尽管这看起来太过复杂,但仅仅是写出规则。考虑两件事:首先,PROGRAM_template中的模板定义应该比这儿的更复杂。其次,你可能将本例中复杂但通用的部分放到另一个makefile中,然后在所有的makefile中包含它。那么, 你的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)_OBJS) $$($(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)

8.10 origin函数

origin函数与其它函数不同之处在于其不是操作变量的值,而是告诉你一些关于变量的事。这儿它告诉你变量从哪来。

origin语法如下:

$(origin variable)

注意variable是要查询的变量名,不是变量引用,因此不需要写$和括号。(但是,你可以使用变量引用,那样就可以使用不固定的变量名。

这个函数的结果告诉你这个变量如何被定义:

  • undefined

    未定义

  • default

    变量有默认定义。注意,如果你重新定义了一个默认变量,origin函数将返回之后定义的origin

  • environment

    变量从环境中继承,用来提供给make。

  • environment override

    变量从环境中继承,但由于使用-e选项,覆盖了makefile中变量的一个设置。

  • file

    变量在makefile中定义。

  • command line

    变量在命令行中被定义。

  • override

    变量在makefile中使用override指令定义。

  • automatic

    变量是一个自动变量。

这些信息可以用于确定变量的值是否可信。例如,假设有一个makefile foo,它包含另一个makefile bar。当运行命令make -f bar时,你想要在bar中定义一个变量bletch,即使环境中包含bletch的定义。然而,如果foo在包含bar之前定义了bletch,你又不想覆盖这个定义。这可以在foo中使用override指令,在bar中定义之前定义bletch。然而,override也可以覆盖任何命令行的定义。因此,bar应该包含:

ifdef bletch
ifeq "$(origin bletch)" "environment"
bletch = barf, gag, etc.
endif
endif

如果bletch已经在环境中定义,这会重新定义它。

如果想要覆盖已经在环境中定义的bletch(甚至包含-e选项),可以写作:

ifneq "$(findstring environment,$(origin bletch)" ""
bletch = barf, gag, etc.
endif

如果$(origin bletch)返回environmentenvironment override,重定义就会发生。

8.11 flavor函数

origin函数一样,flavor函数也不操作变量的值,而是描述变量的某些特性。它告诉你变量的特征。

flavor的语法如下:

$(flavor variable)

注意variable是变量名,而不是变量引用,因此不需要写$或括号(但是你也可以使用变量引用,这样就可以得到不固定的变量名)。

这个函数返回的结果是一个用于描述变量variable的特征的字符串:

  • undefined

    变量未定义。

  • recursive

    变量是递归展开变量。

  • simple

    变量是简单展开变量。

8.12 控制Make的函数

这些函数控制make的运行方式。通常,它们为使用者提供makefile的信息,或者发现任何导致make终止运行的环境错误信息。

  • $(error text...)

    产生一个内容为text的错误信息。注意,这个错误信息会在函数被计算时产生。因此,如果你把它放在recipe或者递归变量赋值的右边,它会在之后被计算。text会在错误产生之前展开。
    例如:

    ifdef ERROR1
    $(error error is $(ERROR1))
    endif
    

    当读取makefile时,如果变量ERROR1已经被定义,则会产生错误信息。或者:

    ERR = $(error found an error!)
    
    .PHONY: err
    err: ; $(ERR)
    

    make运行时,当目标err被调用,就会产生一个错误信息。

  • $(warning text...)

    这个函数工作方式类似于error函数,除非make不存在。text被展开,结果信息被显示,但是处理过程会继续。
    这个函数的展开结果是一个空字符串。

  • $(info text...)
    这个函数只是将它的展开的参数打印到标准输出。不会条件makefile文件名和行号。这个函数的执行结果是一个空字符串。

8.13 shell函数

shell函数不像任何其它函数,除了wildcard。它与make的外部世界进行交流。

shell函数的作用与大多数shell中反引号的作用相同,它执行命令展开。这意味着,它将一个shell命令作为参数,并且计算这个命令的结果。make对这个结果的唯一处理就是将每一个新行字符转换成空格。如果最后有一个换行符,则只会简单的去掉它。

shell函数调用的命令,只有在函数调用被展开时运行。因为这个函数调用会运行一个新的shell,你需要仔细考虑使用shell函数在处理递归展开变量和简单展开变量时的不同。

在使用shell函数,或者!=赋值操作之后,它的退出状态保存在变量.SHELLSTATUS中。

下面是使用shell函数的一些示例:

contents := $(shell cat foo)

contents设置为文件foo的内容,行与行之间用空格分开。

files := $(shell echo *.c)

files设置为*.c的展开。除非make使用一个非常奇怪的shell,否则这个结果与$(wildcard *.c)相同(只要至少存在一个.c文件)。

8.14 guile函数

如果GNU make被创建时支持GNU Guile作为一个嵌入式扩展语言,那么guile函数将可用。guile函数有一个参数,通常这个参数最先被make展开,然后将其传递给GNU Guile的求值器。求值器的结果被转换成一个字符串,被用作makefile中guile函数的展开结果。

可以通过检查变量.FEATURES中的单词guile来确定是否支持GNU Guile。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值