Makefile 笔记(4)— GNU make 控制函数
1.1 $(subst FROM,TO,TEXT)
函数名称:
字符串替换函数—subst
函数功能:
把字符串 "TEXT" 中的 "FROM" 字符替换为 "TO"
返回值:
替换后的新字符串
示例:
TEST_1 = "feet on the street" all: @echo $(TEST_1) @echo $(subst ee,EE, $(TEST_1))
替换 "feet on the street" 中的 "ee" 为 "EE",结果得到字符串 "fEEt on the strEEt"。
1.2 $(patsubst PATTERN,REPLACEMENT,TEXT)
函数名称:
模式替换函数—patsubst
函数功能:
搜索 "TEXT" 中以空格分开的单词,将符合模式 "PATTERN" 替换为 "REPLACEMENT"。参数 "PATTREN" 中可以使用模式通配符 "%" 来代表一个单词中的若干字符。
在 "PATTREN" 和 "REPLACEMENT" 中,只有第一个 "%" 被称为模式字符来处理,之后出现的不再作为模式字符(作为一个字符)。
在参数中如果需要将第一个出现的 "%" 作为字符本身而不作为模式字符时,可使用反斜杠 "\" 进行转义处理。
返回值:
替换后的新字符串
函数说明:
参数 "TEXT" 单词之间的多个空格在处理时被合并为一个空格,并忽略前导和结尾空格。
示例:
all: @echo $(patsubst %c,%o,test1.c.c test2.c)
把字符串 "test1.c.c test2.c" 中以 ".c" 结尾的单词替换成以 ".o" 结尾的字符。函数返回结果为 "test1.c.o test2.o"。
1.3 $(strip STRINT)
函数名称:
去空格函数—strip。
函数功能:
去掉字符(若干单词,使用若干空字符分割) "STRINT" 开头和结尾的空字符,并将其中多个连续空字符合并为一个字符。
返回值:
无前导和结尾空字符、使用单一空格分割的多单词字符串。
函数说明:
空字符包括空格、[Tab]等不可显示字符。
示例:
STR = " a b c" all: @echo $(STR) @echo $(strip $(STR))
1.4 $(findstring FIND,IN)
函数名称:
查找字符串函数—findstring。
函数功能:
搜索字符串 "IN",查找 "FIND" 字符串。
返回值:
如果在 "IN" 之中存在 "FIND",则返回 "FIND",否则返回空。
函数说明:
字符串 "IN" 之中可以包含空格、[Tab]。搜索方式为严格的文本匹配。
示例:
all: @echo $(findstring hello,hello world)
输出结果为 "hello"。
1.5 $(filter PATTERN...,TEXT)
函数名称:
过滤函数—filter
函数功能:
过滤字串 "TEXT" 中所有不符合模式 "PATTERN" 的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符 "%",当存在多个模式时,模式表达式之间使用空格分割。
返回值:
空格分割的 "TEXT" 字串中所有符合模式 "PATTERN" 的字串。
函数说明:
"filter" 函数可以用来去除一个变量中的某些字符串。
cc = gcc sources := test1.c test2.c.c test3.s test4.h all: $(sources) $(cc) $(filter %.c %.s,$(sources)) -o test
1.6 $(filter-out PATTERN...,TEXT)
函数名称:
反过滤函数—filter-out
函数功能:
和 "filter" 函数实现的功能相反。过滤掉字串 "TEXT" 中所有符合模式 "PATTERN" 的单词,保留所有不符合此模式的单词。可以有多个模式。当存在多个模式时,模式表达式之间使用空格分割。
返回值:
空格分割的 "TEXT" 字串中所有不符合模式 "PATTERN" 的字串。
函数说明:
"filter-out" 函数也可以用来去除一个变量中的某些字符串(实现和 "filter" 函数相反)。
示例:
objects = test1.c test2.c.c test3.s test4.h mains = test1.c test2.c all: $(sources) @echo $(filter-out $(mains),$(objects))
1.7 $(sort LIST)
函数名称:
排序函数—sort
函数功能:
给字串 "LIST" 中的单词以首字母为准进行排序(升序),并去掉重复的单词。
返回值:
空格分割的没有重复单词的字串。
函数说明:
两个功能,排序和去字串中的重复单词。可以单独使用其中一个功能。
示例:
list_test = foo bar lose foo all: @echo $(sort $(list_test))
1.8 $(word N,TEXT)
函数名称:
去单词函数—word
函数功能:
取字串 "TEXT" 中第 "N" 个单词("N" 的值从1开始)。
返回值:
返回字串 "TEXT" 中第 "N" 个单词。
函数说明:
如果 "N" 值大于字串 "TEXT" 中单词的数目,返回空字符串。如果 "N" 为0,出错。
示例:
list_test = foo bar lose foo all: @echo $(word 2, $(list_test))
1.9 $(words TEXT)
函数名称:
统计单词数目函数—words
函数功能:
计算字串 "TEXT" 中单词的数目。
返回值:
"TEXT" 字串中的单词数。
示例:
list_test = foo bar all: @echo $(words $(list_test))
1.10 $(wordlist S,E,TEXT)
函数名称:
取字串函数—wordlist
函数功能:
从字串 "TEXT" 中取出从 "S" 开始到 "E" 的单词串。"S" 和 "E" 表示单词在字串中位置的数字。
返回值:
字串 "TEXT" 中从第 "S" 到 "E"(包括 "E")的单词字串。
函数说明:
"S" 和 "E" 都是从1开始的数字。如果 "E" 大于 "TEXT" 的数字,则返回从 "S" 开始,到 "TEXT" 结束的单词串;如果 "S" 大于 "E" 的数字,返回空。
示例:
list_test = foo bar baz all: @echo $(wordlist 2,3, $(list_test))
1.11 $(firstword NAMES...)
函数名称:
取首单词函数—firstword
函数功能:
取字串 "NAMES..." 中的第一个单词。
返回值:
字串 "NAMES..." 的第一个单词。
函数说明:
"NAMES" 被认为是使用空格分割的多个单词(名字)的序列。函数忽略 "NAMES..." 中除第一个单词以外的所有的单词。
示例:
list_test = foo bar baz all: @echo $(firstword $(list_test))
输出:
2. 文件名处理函数
2.1 $(dir NAMES...)
函数名称:
取目录函数-dir
函数功能:
从文件名序列 "NAMES..." 中取出各个文件名的目录部分。文件名的目录部分就是包含在文件名的最后一个斜线("/")(包括斜线)之前的部分。
返回值:
空格分割的文件名序列 "NAMES..." 中每一个文件的目录部分。
函数说明:
如果文件名中没有斜线,认为此文件为当前目录("./")下的文件。
示例:
$(dir src/foo.c hacks)
返回值为 "src/ ./"。
2.2 $(notdir NAMES...)
函数名称:
取文件名函数——notdir
函数功能:
从文件名序列 "NAMES..." 中取出非目录部分。目录部分是指最后一个斜线("/")(包括斜线)之前的部分。删除所有文件名中的目录部分,只保留非目录部分。
返回值:
文件名序列 "NAMES..." 中每一个文件的非目录部分。
函数说明:
如果 "NAMES..." 中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空格代替,因此当 "NAMES..." 中存在多个这样的文件名时,返回结果中分割各个文件名的空格数据将不确定。
示例:
$(notdir src/foo.c hacks)
返回值为:"foo.c hacks"
2.3 $(suffix NAMES...)
函数名称:
取后缀函数—suffix
函数功能:
从文件名序列 "NAMES..." 中取出各个文件名的后缀。后缀是文件名中最后一个以点 "." 开始的(包含点号)部分,如果文件名中不包含一个点号,则为空。
返回值:
以空格分割的文件名序列 "NAMES..." 中每一个文件的后缀序列。
函数说明:
"NAMES..." 是多个文件名时,返回值是多个以空格分割的单词序列。如果文件名没有后缀部分,则返回空。
示例:
$(suffix src/foo.c src-1.0/bar.c hacks)
返回值为 ".c .c"。
2.4 $(basename NAMES...)
函数名称:
取前缀函数—basename
函数功能:
从文件名序列 "NAMES..." 中取出各个文件名的前缀部分。前缀部分指的是文件名中最后一个点号之前的部分。
返回值:
空格分割的文件名序列 "NAMES..." 中各个文件的前缀序列。如果文件没有前缀,则返回空字符串。
函数说明:
如果 "NAMES..." 中包含没有后缀的文件名,此文件名不改变。如果一个文件名中存在多个点号,则返回值为文件名的最后一个点号之前的文件名部分。
示例:
$(basename src/foo.c src-1.0/bar.c /home/jack/.font.cache-1 hacks)
返回值为:"src/foo src-1.0/bar /home/jack/.font hacks"。
2.5 $(addsuffix SUFFIX,NAMES...)
函数名称:
加后缀函数—addsuffix
函数功能:
为 "NAMES..." 中的每一个文件名添加后缀 "SUFFIX"。参数 "NAMES..." 为空格分割的文件名序列,将 "SUFFIX" 追加到此序列的每一个文件名的末尾。
返回值:
以单空格分割的添加了后缀 "SUFFIX" 的文件名序列。
示例:
$(addsuffix .c,foo bar)
返回值为 "foo.c bar.c"
2.6 $(addprefix PREFIX,NAMES...)
函数名称:
加前缀函数—addprefix
函数功能:
为 "NAMES..." 中的每一个文件名添加前缀 "PREFIX"。参数 "NAMES..." 是空格分割的文件名序列,将 "SUFFIX" 添加到此序列的每一个文件名之前。
返回值:
以单空格分割的添加了前缀 "PREFIX" 的文件名序列。
示例:
$(addprefix src/,foo bar)
返回值为 "src/foo src/bar"
2.7 $(join LIST1,LIST2)
函数名称:
单词连接函数—join
函数功能:
将字符串 "LIST1" 和字串 "LIST2" 各单词进行对应连接。就是将 "LIST2" 中的第一个单词追加 "LIST1" 第一个单词后合并为一个单词;将 "LIST2" 中的第二个单词追加到 "LIST1" 的第一个单词之后并合并为一个单词,...依次类推。
返回值:
单空格分割的合并后的字(文件名)序列。
函数说明:
如果 "LIST1" 和 "LIST2" 中的字数目不一致时,两者中多余部分将被作为返回序列的一部分。
示例:
$(join a b c,.c .o)
返回值:"a.c b.o c"
2.8 $(wildcard PATTERN)
函数名称:
获取匹配模式文件名函数—wildcard
函数功能:
列出当前目录下所有符合模式 "PATTERN" 格式的文件名。
返回值:
空格分割的、存在当前目录下的所有符合模式 "PATTREN" 的文件名。
函数说明:
"PATTERN" 使用shell可识别的通配符,包括 "?"(单字符)、"*"(多字符)等。
示例:
$(wildcard *.c)
返回值为当前目录下所有 .c 源文件列表。
3. make 控制函数
3.1 foreach 函数
语法:
$(foreach VAR,LIST,TEXT)
函数功能:
如果需要(存在变量或函数的引用),首先展开变量 "VAR" 和 "LIST" 的引用;而表达式 "TEXT" 中的变量引用不展开。执行时把 "LIST" 中使用空格分割的单词依次取出赋值给变量 "VAR",然后执行 "TEXT" 表达式。重复直到 "LIST" 的最后一个单词(为空时结束)。
"TEXT" 中的变量或者函数引用在执行时才被展开,因此如果在 "TEXT" 中存在对 "VAR" 引用,那么 "VAR" 的值在每一次展开式将会得到不同的值。
返回值:
空格分割的多次表达式 "TEXT" 的计算的结果。
例子:
dirs := a b c d files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
定义变量 "files",它的值为四个目录(变量 "dirs" 代表的a、b、c、d四个目录)下的文件列表。例子中,"TEXT" 的表达式为 "$(wildcard $(dir)/*)"。表达式第一次执行时,将展开为 " $(wildcard a/*)";第二次执行时将展开为 "$(wildcard b/*)";第三次展开为 "$(wildcard c/*)";...;依此类推。
函数说明:
函数中参数 "VAR" 是一个局部临时变量,它只在 "foreach" 函数的上下文中有效,它的定义不会影响其它部分定义的同名 "VAR" 变量的值。在函数的执行过程中它是一个“直接展开”式变量。
3.2 if 函数
函数 "if" 提供了一个在函数上下文中实现条件判断的功能。就像 make 所支持的条件语句—ifeq 一样。
函数语法:
$(if CONDITION,THEN-PART[,ELSE-PART])
函数功能:
第一个参数 "CONDITION",在函数执行时忽略其前导和结尾空字符,如果包含对其他变量或者函数的引用则进行展开。
如果 "CONDITION" 的展开结果非空,则条件为真,就将第二个参数 "THEN-PART" 作为函数的计算表达式;若"CONDITION" 的展开结果为空,就将第三个参数 "ELSE-PART" 作为函数的表达式,函数的返回结果为有效表达式的计算结果。
返回值:
根据条件决定函数的返回值是第一个或者第二个参数表达式的计算结果。当不存在第三个参数 "ELSE-PART" ,并且 "CONDITION" 展开为空,函数返回空。
函数说明:
函数的条件表达式 "CONDITION" 决定了函数的返回值只能是 "THEN-PART" 或者 "ELSE-PART" 两个之一的计算结果。
示例:
SUBDIR += $(if $(SRC_DIR) $(SRC_DIR),/home/src)
函数的结果是:若 "SRC_DIR" 变量值不为空,则将变量 "SRC_DIR" 指定的目录作为一个子目录,否则将目录 "/home/src" 作为一个子目录。
3.3 call 函数
"call" 函数是唯一一个可以创建定制化参数函数的引用函数。使用这个函数可以实现对用户自己定义函数引用。可以将一个变量定义为一个复杂的表达式,用 "call" 函数根据不同的参数对它进行展开开获得不同的结果。
函数语法:
$(call VARIABLE,PARAM,PARAM,...)
函数功能:
在执行时,将它的参数 "PARAM" 依次赋值给临时变量 "$(1)"、"$(2)"(这些临时变量定义在 "VARIABLE" 的值中)。call 函数对参数的数目没有限制,也可以没有参数值,没有参数值的 "call" 没有任何实际存在的意义。
执行时变量 "VARIABLE" 被展开为在函数上下文有效的临时变量,变量定义中的 "$(1)" 作为第一个参数,并将函数参数值中的第一个参数赋值给它;变量中的 "$(2)" 一样被赋值为函数的第二个参数值;依此类推。变量$(0)代表变量 "VARIABLE" 本身。
返回值:
参数值 "PARAM" 依次替换 "$(1)"、"$(2)" ... 之后变量 "VARIABLE" 定义的表达式的计算值。
函数说明:
函数中 "VARIABLE" 是一个变量名,而不是变量引用。因此,通常 "call" 函数中的 "VARIABLE" 中不包含 "$"(当然,除非此变量名是一个计算的变量名)。
当变量 "VARIABLE" 是一个 make 内嵌的函数名时(如 "if"、"foreach"、"strip" 等),对 "PARAM" 参数的使用需要注意,因为不合适或者不正确的参数将会导致函数的返回值难以预料。
函数中多个 "PARAM" 之间使用逗号分隔。
变量 "VARIABLE" 在定义时不能定义为直接展开式,只能定义为递归展开式。
示例:
reverse = $(2) $(1) all: @echo $(call reverse,a,b)
输出结果为 "b a"。这里变量 "reverse" 中的参数定义顺序可以根据需要来调整,并不是需要按照 "$(1)"、"$(2)"、"$(3)" ... 这样的顺序来定义。
3.4 value 函数
提供一种在不对变量进行展开的情况下获取变量值的方法。
函数语法:
$(value VARIABLE)
函数功能:
不对变量 "VARIABLE" 进行任何展开操作,直接返回变量 "VARIABLE" 的值。这里 "VARIABLE" 是一个变量名,一般不包含 "$"(除非计算的变量名)。
返回值:
变量 "VARIABLE" 所定义文本值。如果变量定义为递归展开式,其中包含对其他变量或者函数的引用,那么函数不对这些引用进行展开。函数的返回值是包含有引用值。
函数示例:
FOO = $PATH all: @echo $(FOO) @echo $(value FOO)
执行make,可以看到第一行输出结果为:"ATH"。这是因为变量 "FOO" 定义为 "$PATH",所以展开为 "ATH"("$P" 为空)。
第二行才是我们所要显示的系统环境变量 "PATH" 的值(value 函数得到变量 "FOO" 的值为 "$PATH")。
3.5 eval 函数
函数功能:
函数 "eval" 是一个比较特殊的函数。使用它可以在 Makefile 中构造一个可变的规则结构关系(依赖关系链),其中可以使用其他变量和函数。
函数 "eval" 对它的参数进行展开,展开的结果作为 Makefile 的一部分,make可以对展开内容进行语法解析。展开的结果可以包含一个新变量、目标、隐含规则或者是明确规则等。
也就是说,此函数的功能主要是:根据其参数的关系、结构,对它们进行替换展开。
返回值:
函数 "eval" 的返回值是空,也可以说没有返回值。
函数说明:
"eval" 函数执行时会对它的参数进行两次展开。第一次展开过程是由函数本身完成的,第二次是函数展开后的结果被称作为 Makefile 内容时由 make 解析时展开的。
示例:
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,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog)))) 展开为: server : $(server_OBJS) –l$(server_LIBS) client : $(client_OBJS) –l$(client_LIBS)
3.6 origin 函数
函数 "origin" 和其他函数不同,函数 "origin" 的动作不是操作变量(它的参数),它只是获取此变量(参数)相关信息,告诉我们这个额变量的出处(定义方式)。
函数语法:
$(origin VARIABLE)
函数功能:
函数 "origin" 查询参数 "VARIABLE"(一个变量名)的出处。
函数说明:
"VARIABLE" 是一个变量名而不是一个变量的引用。因此通常它不包含 "$"(当然,计算的变量名除外)。
返回值:
返回 "VARIABLE" 的定义方式。用字符串表示。
返回值有以下几种:
undefined
变量 "VARIABLE" 没有被定义。
default
变量 "VARIABLE" 是一个默认定义(内嵌变量)。如:"CC"、"MAKE"、"RM" 等变量。如果在 Makefile 中重新定义这些变量,函数返回值将相应发生变化。
environment
变量 "VARIABLE" 是一个系统环境变量,并且 make 没有使用命令行选项 "-e" (Makefile 中不存在同名的变量定义,此变量没有被替代)。
environment override
变量 "VARIABLE" 是一个系统环境变量,并且 make 使用了命令行选项 "-e"。Makefile 中存在一个同名的变量定义,使用 "make -e" 时环境变量值替代了文件中的变量定义。
file
变量 "VARIABLE" 在某一个 makefile 文件中定义。
command line
变量 "VARIABLE" 在命令行中定义。
override
变量 "VARIABLE" 在 makfile 文件中定义并使用 "override" 指示符声明。
automatic
变量 "VARIABLE" 是自动化变量。
函数 "origin" 返回的变量信息对我们书写 Makefile 是相当有用的、可以使我们在使用一个变量之前对它的值进行合法性判断。
3.7 shell 函数
函数功能:
函数 "shell" 所实现的功能和shell中的引用(``)相同。这就意味着需要一个 shell 命令作为此函数的参数,函数的返回结果是此命令在shell中的执行结果。
make 仅仅对它的返回结果进行处理。make 将函数返回结果中的所有换行符("\n")或者一对 "\r\n" 替换为单空格,并去掉末尾的回车符号("\n")或者( "\r\n")。
进行函数展开式时,它所调用的命令(它的参数)得到执行;除对它的引用出现在规则的命令行和递归变量的定义中之外,其它绝大多数情况下,make是读取解析 Makefile 时完成对函数 shell 的展开。
返回值:
函数 "shell" 的参数(一个 shell 命令)在 shell 环境中的执行结果。
函数说明:
函数本身的返回值是其参数的执行结果,没有进行任何处理;对结果的处理是由 make 进行的。当对函数的引用出现在规则的命令行中,命令行在执行时函数才被展开。
示例:
contents := $(shell cat test1.c) all: echo $(contents)
其中,test1.c 内容如下:
#include <stdio.h> int main(void) { printf("Test\r\n"); }
将变量 "contents" 赋值为文件 "test1.c" 的内容,文件中的换行符在变量中使用空格代替。
3.8 $(error TEXT...)
函数功能:
产生致命错误,并提示 "TEXT..." 信息给用户,并退出 make 的执行。需要说明的是:"error" 函数是在函数展开式(函数被调用时)才提示信息并结束 make 进程。因此,如果函数出现在命令中或者一个递归的变量定义时,在读取 Makefile 时不会出现错误,而只有包含 "error" 函数的引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息 "TEXT..." 同时退出 make。
返回值:空。
函数说明:
"error" 函数一般不出现在直接展开式的变量定义中,否则在 make 读取 Makefile 时将会提示致命错误。
示例1:
ERROR1 = 'test' ifdef ERROR1 contents = $(error error is $(ERROR1)) endif
make 读取解析 Makefile 时,如果已经定义变量 "ERROR1",make 将会提示致命错误信息 "$(ERROR1)" ,并退出。
示例2:
ERR = $(error found an error!) .PHONY: err err:;$(ERR)
这个例子,在 make 读取 Makefile 时不会出现致命错误。只有目标 "err" 被作为一个目标被执行时才会出现。
函数功能:
函数 "warning" 类似于函数 "error",区别在于它不会导致致命错误(make 不会退出),而只是提示 "TEXT...",make 的执行过程继续。
返回值:空。
函数说明:
用法和 "error" 类似,展开过程相同。