Skip to content

LV110-函数

本文主要是makefile——条件判断和函数相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

一、函数的调用

make 提供了一些函数供 Makefile 调用,函数调用后,函数的返回值可以当做变量来使用。函数的调用与变量很类似,一般语法格式如下:

makefile
$(<function> arg1, arg2, ...)
# 或者
${<function> arg1, arg2, ...}

function 就是函数名, argN 是函数的参数。

注意

(1)函数名与参数之间通过空格( space )分隔开。

(2)函数的多个参数之间使用 , 分割开来,并且在函数中可以使用变量。

(3)为了风格的统一,函数和变量的括号最好一样,如使用 $(subst a,b,$(x)) 这样的形式。

二、字符串处理函数

1. 字符串替换函数

1.1 subst

字符串替换函数 subst 一般语法格式如下:

makefile
$(subst <source_str>,<target_str>,<text>)

函数说明】该函数会将字符串 text 中的 source_str 字符串替换成 target_str 。

返回值】函数返回被替换过后的字符串。

注意】 <source_str>,<target_str>,<text> 的 , 号之间最好不要有空格,否则可能替换后会多个空格哦。

1.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o
var2 := $(subst .o,.c,$(var1))

test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

makefile
var1=main.o test.o
var2=main.c test.c

2. 模式字符串替换函数

2.1 patsubst

模式字符串替换函数 patsubst 一般语法格式如下:

makefile
$(patsubst <pattern>,<replacement>,<text>)

函数说明】该函数会查找 <text> 中的单词(单词以“空格”、“Tab”或“回车”,“换行”分隔)是否符合模式 <pattern> ,如果匹配的话,则以 <replacement> 替换。

返回值】函数返回被替换过后的字符串。

注意

(1) <pattern>,<replacement>,<text> 的 , 号之间最好不要有空格,否则可能替换后会多个空格哦。

(2) <pattern> 可以包括通配符 % , 表示任意长度的字符串。如果 <replacement> 中也包含 % ,那么, <replacement> 中的这个 % 将是 <pattern> 中的那个 % 所代表的字串。(另外可以用 \ 来转义一些字符,例如,以 % 来表示真实含义的 % 字符)。

2.2 使用实例

这个函数与变量替换的时候很类似,可以看下边的例子。 Makefile 文件内容如下:

makefile
var1 := main.o test.o
var2 := $(patsubst %.o,%.c,$(var1))
var3 := $(var1:.o=.c)
var4 := $(var1:%.o=%.c)
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"
	@echo "var4=$(var4)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o
var2=main.c test.c
var3=main.c test.c
var4=main.c test.c

3.去空格函数

3.1 strip

去空格函数 strip 一般语法格式如下:

makefile
$(strip <string>)

函数说明】该函数会去掉字符串 <string> 中开头和结尾的空字符,并且将字符串中的空格合并成为一个空格。

返回值】函数返回被去掉空格的字符串值。

3.2 使用实例

Makefile 文件内容如下:

makefile
var1 := ma  in.o             tes    t.o
var2 := $(strip $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

makefile
var1=ma  in.o             tes    t.o
var2=ma in.o tes t.o

4. 查找字符串函数

4.1 findstring

查找字符串函数 findstring 一般语法格式如下:

makefile
$(findstring <find_str>,<string>)

函数说明】该函数会在字符串 <string> 中查找 <find_str> 字符串。

返回值】函数如果找到要查找的字符串 <find_str> ,那么返回 <find_str> ,否则返回空字符串。

注意

(1) <find_str>,<string> 的 , 号之间最好不要有空格,否则可能会与预期的不太一样。

4.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o
var2 := $(findstring main,$(var1))
var3 := $(findstring hello,$(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o
var2=main
var3=

5. 过滤函数

5.1 filter

过滤函数 filter 一般语法格式如下:

makefile
$(filter <pattern...>,<text>)

函数说明】该函数会过滤出 <text> 中符合模式 <pattern> 的字符串,可以有多个 <pattern> 。

返回值】函数返回符合模式 <pattern> 的字符串。

注意

(1) <pattern...>,<text> 的 , 号之间最好不要有空格,否则可能会与预期的不太一样。

5.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o main.c test.s test.c
var2 := $(filter %.c %.s,$(var1))
var3 := $(filter %.o,$(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o main.c test.s test.c
var2=main.c test.s test.c
var3=main.o test.o

6. 反过滤函数

6.1 filter-out

反过滤函数 filter-out 一般语法格式如下:

makefile
$(filter-out <pattern...>,<text>)

函数说明】该函数会过滤出 <text> 中不符合模式 <pattern> 的字符串(就是会把符合的给去掉),可以有多个 <pattern> 。

返回值】函数返回不符合模式 <pattern> 的字符串。

注意

(1) <pattern...>,<text> 的 , 号之间最好不要有空格,否则可能会与预期的不太一样。

6.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o main.c test.s test.c
var2 := $(filter-out %.c %.s,$(var1))
var3 := $(filter-out %.o,$(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o main.c test.s test.c
var2=main.o test.o
var3=main.c test.s test.c

7. 排序函数

7.1 sort

排序函数 sort 一般语法格式如下:

makefile
$(sort <list>)

函数说明】该函数给字符串 <list> 中的单词排序(升序)。

返回值】函数返回排序后的字符串。

7.2 使用实例

Makefile 文件内容如下:

makefile
var1 := a.o b.c d.s a.c x.c 
var2 := $(sort $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=a.o b.c d.s a.c x.c 
var2=a.c a.o b.c d.s x.c

8. 取单词函数

8.1 word

取单词函数 word 一般语法格式如下:

makefile
$(word <n>,<text>)

函数说明】该函数会取字符串 <text> 中第 n 个单词。( n 从 1 开始)。

返回值】函数返回字符串 <text> 中第 n 个单词。如果 n 比 <text> 中的单词数要大,那么返回空字符串。

8.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o main.c test.s test.c
var2 := $(word 1, $(var1))
var3 := $(word 3, $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o main.c test.s test.c
var2=main.o
var3=main.c

9. 单词个数统计函数

9.1 words

单词个数统计函数 words 一般语法格式如下:

shell
$(words <text>)

函数说明】该函数会统计字符串 <text> 中的单词个数。

返回值】函数返回字符串 <text> 中的单词数。

9.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o main.c test.s test.c
var2 := $(words $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o main.c test.s test.c
var2=5

10. 首单词函数

10.1 firstword

首单词函数 firstword 一般语法格式如下:

makefile
$(firstword <text>)

函数说明】该函数会取出字符串 <text> 中的第一个单词。

返回值】函数返回字符串 <text> 中的第一个单词。

10.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.o test.o main.c test.s test.c
var2 := $(firstword $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.o test.o main.c test.s test.c
var2=main.o

11. 综合应用

  • 指定编译器头文件搜索路径
makefile
CFLAGS += = $(patsubst %,-I %,$(subst :, ,$(VPATH)))

我们先假设 VPATH:=./src : ./include ,那么:

CFLAGS是代表C编译器的选项
+=是代表追加赋值
$(VPATH)文件搜索路径,在这里表示 ./src : ./include
$(subst :, ,$(VPATH))调用 subst 字符串替换函数,将 ./src : ./include 中的 : 替换为空值(将会产生一个空格),最终得到字符串
./src ./include
patsubst模式字符串替换函数,将会把 ./src ./include 的每个单词进行整体匹配,会得到
-I ./src -I ./include

所以最终我们会得到 cc 或 gcc 搜索头文件路径的参数。放入 Makefile 文件,

makefile
VPATH := ./src : ./include
var1 := $(VPATH)
var2 := $(subst :, ,$(VPATH))
CFLAGS += $(patsubst %,-I %,$(subst :, ,$(VPATH)))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "CFLAGS=$(CFLAGS)"

接着执行 make test ,会看到输出如下:

shell
var1=./src : ./include
var2=./src   ./include
CFLAGS=-I ./src -I ./include

若修改为以下内容,限制会更强一点:

makefile
CFLAGS += $(patsubst %,-I %,$(filter %include,$(subst :, ,$(VPATH))))

这样最终就只剩下 -I ./include 了。

三、文件名操作函数

1. 取目录函数

1.1 dir

取目录函数 dir 一般语法格式如下:

makefile
$(dir <names...>)

函数说明】该函数会从文件名序列 <names...> 中取出目录部分。目录部分是指最后一个反斜杠( / )之前的部分。如果没有反斜杠,那么返回 ./ 。

返回值】函数返回文件名序列的目录部分。

1.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c ./include/test.h ./src/test.c
var2 := $(dir $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c ./include/test.h ./src/test.c
var2=./ ./include/ ./src/

2. 取文件函数

2.1 notdir

取文件函数 notdir 一般语法格式如下:

makefile
$(notdir <names...>)

函数说明】该函数会从文件名序列 <names...> 中取出非目录部分。非目录部分是指最后一个反斜杠( / )之后的部分。

返回值】函数返回文件名序列的目录部分。

2.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c ./include/test.h ./src/test.c
var2 := $(notdir $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c ./include/test.h ./src/test.c
var2=main.c test.h test.c

3. 取后缀函数

3.1 suffix

取后缀函数 suffix 一般语法格式如下:

makefile
$(suffix <names...>)

【函数说明】该函数会从文件名序列 <names...> 中取出各个文件的后缀。

【返回值】函数返回文件名序列 <names ...> 的后缀序列,如果文件没有后缀,则返回空字串。

3.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c ./include/test.h ./src/test.c
var2 := $(suffix $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c ./include/test.h ./src/test.c
var2=.c .h .c

4. 取前缀函数

4.1 basename

取前缀函数 basename 一般语法格式如下:

makefile
$(basename <names...>)

函数说明】该函数会从文件名序列 <names...> 中取出各个文件的前缀部分。

返回值】函数返回文件名序列 <names ...> 的前缀序列,如果文件没有前缀,则返回空字串。

注意】取出的前缀会包含目录部分(如果有目录部分的话)。

4.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c ./include/test.h ./src/test.c
var2 := $(basename $(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c ./include/test.h ./src/test.c
var2=main ./include/test ./src/test

5. 加后缀函数

5.1 addsuffix

加后缀函数 addsuffix 一般语法格式如下:

makefile
$(addsuffix <suffix>,<names...>)

函数说明】该函数会把后缀 <suffix> 加到 <names...> 中的每个单词后面。

返回值】函数返回加过后缀的文件名序列。

5.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c ./include/test.h ./src/test.c
var2 := $(addsuffix -QN,$(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c ./include/test.h ./src/test.c
var2=main.c-QN ./include/test.h-QN ./src/test.c-QN

6. 加前缀函数

6.1 addprefix

加前缀函数 addprefix 一般语法格式如下:

makefile
$(addprefix <prefix>,<names...>)

函数说明】该函数会把前缀 <prefix> 加到 <names...> 中的每个单词前面。

返回值】函数返回加过前缀的文件名序列。

6.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c include/test.h src/test.c
var2 := $(addprefix ./,$(var1))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c include/test.h src/test.c
var2=./main.c ./include/test.h ./src/test.c

7. 连接函数

7.1 join

连接函数 join 一般语法格式如下:

makefile
$(join <list1>,<list2>)

函数说明】该函数会把 <list2> 中的单词对应地加到 <list1> 的单词后面。

返回值】函数返回连接过后的字符串。

注意】如果 <list1> 的单词个数要比 <list2> 的多,那么, <list1> 中的多出来的单词将保持原样。如果 <list1> 的单词个数要比 <list2> 少,那么, <list2> 多出来的单词将被复制到 <list1> 中。

7.2 使用实例

Makefile 文件内容如下:

makefile
var1 := 111 112 
var2 := -221 -222 -223
var3 := 331
var4 := $(join $(var1),$(var2))
var5 := $(join $(var3),$(var2))
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"
	@echo "var4=$(var4)"
	@echo "var5=$(var5)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=111 112 
var2=-221 -222 -223
var3=331
var4=111-221 112-222 -223
var5=331-221 -222 -223

8. 获取匹配模式文件名函数

8.1 wildcard

获取匹配模式文件名函数 wildcard 一般语法格式如下:

makefile
$(wildcard  <pattern>)

函数说明】该函数会列出当前目录下所有符合模式的 <pattern> 格式的文件名,可以有多个 <pattern> ,中间用空格隔开。

返回值】函数返回返回值为空格分隔并且存在当前目录下的所有符合模式 <pattern> 的文件名。

注意

(1)在这个函数中似乎不太适合使用 % 来匹配,可以使用其他的通配符来进行匹配。

8.2 使用实例

Makefile 文件内容如下:

makefile
var1 := $(wildcard *.c *.o)
test:
	@echo "var1=$(var1)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c test.c main.o test.o

便会列出当前目录下所有的 .c 和 .o 文件。

四、其它常用函数

1. foreach 函数

1.1 foreach

循环函数 foreach 一般使用格式如下:

makefile
$(foreach <var>,<list>,<text>)

函数说明】把参数 <list> 中的单词逐一取出放到参数 <var> 所指定的变量中,然后再执行 <text> 所包含的表达式。每一次 <text> 会返回一个字符串。

循环过程中, <text> 的返所返回的每个字符串会以空格分割,最后当整个循环结束的时候, <text> 所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值。

所以 <var> 最好是一个变量名, <list> 可以是一个表达式,而 <text> 中一般会只用 <var> 这个参数来一次枚举 <list> 中的单词。

注意】 foreach 中的 <var> 参数是一个临时的局部变量, foreach 函数执行完后,参数 <var> 的变量将可用,其作用域只在 foreach 函数当中。

1.1 使用实例

Makefile 文件内容如下:

makefile
var1 := a b c d
var2 := $(foreach n,$(var1),./$(n).o)
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=a b c d
var2=./a.o ./b.o ./c.o ./d.o

$(var1) 中的单词会被挨个取出,并存到变量 n 中, $(n).o 每次根据 $(n) 计算出一个值,这些值以空格分隔,最后作为 foreach 函数的返回值。

2. if 函数

2.1 if

条件函数 if 一般使用格式如下:

makefile
$(if <condition>,<then-part>) 
# 或者
$(if <condition>,<then-part>,<else-part>)

函数说明】 <condition> 参数是 if 的表达式,如果其返回的是非空的字符串,那么这个表达式就相当于返回真,于是, <then-part> 就会被计算,否则 <else-part> 会被计算。

返回值

如果 <condition> 为真(非空字符串),那么 <then-part> 会是整个函数的返回值。

如果 <condition> 为假(空字符串),那么 <else-part> 将会是这个函数的返回值。此时如果 <else-part> 没有被定义(也就是说没有这一部分),那么整个函数返回空字串符。所以, <then-part> 和 <else-part> 只会有一个被计算。

2.2 使用实例

Makefile 文件内容如下:

makefile
var1 := a b c d
var2 := 
var3 := $(if $(var1),$(var1),false)
var4 := $(if $(var2),$(var2))
var4 := $(if $(var2),$(var2),false)
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"
	@echo "var4=$(var4)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=a b c d
var2=
var3=a b c d
var4=false

3. call 函数

3.1 call

传递参数函数 call 一般使用格式如下:

makefile
$(call <expression>,<parm1>,<parm2>...)

函数说明】 <expression> 参数是 call 的表达式,我们可以在表达式 <expression> 中定义许多的参数,然后通过 call 函数向表达式传入 <parm1>,<parm2>... 参数。

返回值】 <expression> 的返回值就是函数的返回值。

注意

(1) <expression> 可以定义为以下格式:

makefile
var1 = $(1) $(2) $(3)

$(1) $(2) $(3) 分别表示第一个参数,第二个参数,第三个参数,参数的次序是可以自定义的,不一定是顺序的。另外说明一点就是,这里定义的时候不能用 := 否则无法对传入变量进行引用。

(2)传入参数多余或者少于表达式中定义的参数,都不会报错,若引用参数的编号超出了传入参数数量,会多出一个空格,下边例子中就有。

3.2 使用实例

Makefile 文件内容如下:

makefile
var1 = $(1) $(2) $(3)
var2 = $(3) $(1) $(2)
var3 :=  $(call var1,a,b,c)
var4 :=  $(call var2,a,b,c)
var5 :=  $(call var1,a,b)
var6 :=  $(call var2,a,b)
var7 :=  $(call var1,a,b,c,d)
var8 :=  $(call var2,a,b,c,d)
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"
	@echo "var4=$(var4)"
	@echo "var5=$(var5)"
	@echo "var6=$(var6)"
	@echo "var7=$(var7)"
	@echo "var8=$(var8)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=  
var2=  
var3=a b c
var4=c a b
var5=a b 
var6= a b
var7=a b c
var8=c a b

4. origin 函数

4.1 origin

溯源函数(哈哈🤣,我自己起的名字,感觉叫着方便) origin 一般使用格式如下:

makefile
$(origin <variable>)

函数说明】不操作变量的值,只是告诉我们 <variable> 这个变量是哪里来的。

返回值】这个函数的返回值就多了:

undefinedvariable从来没有定义过,origin 函数返回这个值“undefined”。
defaultvariable是一个默认的定义,比如“CC”这个变量,
environmentvariable是一个环境变量,并且当 Makefile 被执行时,“-e”参数没有被打开。
filevariable这个变量被定义在 Makefile 中。
command linevariable这个变量是被命令行定义的。
overridevariable是被 override 指示符重新定义的。
automaticvariable是一个命令运行中的自动化变量。

4.2 使用实例

Makefile 文件内容如下:

makefile
var1 := main.c
var2 :=  $(origin var1)
var3 :=  $(origin CC)
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=main.c
var2=file
var3=default

5 shell 函数

5.1 shell

shell 函数一般使用格式如下:

makefile
$(shell <command>)

函数说明】 <command> 为函数的参数,可以是 shell 中的命令。这个函数就是可以调用 shell 命令,它与反引号 ` 功能相同。

返回值】 shell 命令运行结果。

注意】这个函数会新生成一个 Shell 程序来执行命令,所以我们需要注意其运行性能,如果我们的 Makefile 中有一些比较复杂的规则,并大量使用了这个函数,那么对于我们的系统性能是有害的。特别是 Makefile 的隐晦的规则可能会让 shell 函数执行的次数比想像的多得多。

5.2 使用实例

Makefile 文件内容如下:

makefile
var1 := $(shell pwd)
var2 := $(shell ls)
var3 :=  pwd 
var4 :=  ls 
test:
	@echo "var1=$(var1)"
	@echo "var2=$(var2)"
	@echo "var3=$(var3)"
	@echo "var4=$(var4)"

然后在终端中执行 make test 命令,可以看到输出如下:

shell
var1=/mnt/hgfs/sharedfiles/2Linux/test/LV02/05Makefile/test1
var2=main main.c main.o Makefile test.c test.h test.o
var3=/home/hk/0sharedfiles/2Linux/test/LV02/05Makefile/test1
var4=main
main.c
main.o
Makefile
test.c
test.h
test.o