LV010-变量简介
一、变量简介
1. Makefile 中的变量
变量,我们并不陌生, Makefile 又不是编程语言,为什么也需要变量呢?我们什么需要创建变量呢?创建变量的目的其实就是用来代替一个文本字符串:系列文件的名字、传递给编译器的参数、需要运行的程序、需要查找源代码的目录、我们需要输出信息的目录等。
在 Makefile 中的定义的变量,就像是 C/C++ 语言中的宏一样,他代表了一个文本字串,在 Makefile 中执行的时候其会自动原模原样地展开在所使用的地方。其与 C/C++ 所不同的是,我们可以在 Makefile 中改变其值。在 Makefile 中,变量可以使用在“目标”,“依赖目标”,“命令”或是 Makefile 的其它部分中。
2. 变量的命名规则
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 :、# 、= 或是空字符(空格、回车等)。
变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。传统的 Makefile 的变量名是全大写的命名方式,但一般推荐使用大小写搭配的变量名,如:MakeFlags。这样可以避免和系统的变量冲突,而发生意外的事情。
有一些变量是很奇怪字串,如
3. 可以用在哪里?
变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
4. 变量的定义
4.1 定义格式
变量定义的基本格式如下:
variable=value【说明】
(1)变量的不需要使用数据类型。变量的名称可以由大小写字母、阿拉伯数字和下划线构成。
(2)等号左右的空白符没有明确的要求,因为在执行 make 的时候多余的空白符会被自动的删除。
(3) value 表示值列表,既可以是零项,又可以是一项或者是多项。例如,
VALUE_LIST = one two three(4)变量定义时是可以换行书写的,只是需要在每行结尾处添加一个 \ 。
4.2 使用实例
Makefile 文件内容如下:
a = main.o \
test.o \
test.h
test:
@echo "a=$(a)"然后在终端中执行 make test ,可以看到终端会有以下内容输出:
a=main.o test.o test.h5. 变量的使用
5.1 使用格式
上边定义的变量如何引用呢?引用格式如下:
$(VALUE_LIST)
# 或者
${VALUE_LIST}在使用时,需要给在变量名前加上 $ 符号,但最好用小括号 () 或 是大括号 {} 把变量给包括起来。如果要使用真实的 $ 字符,那么需要用 $$ 来表示。
5.2 使用实例
变量会在使用它的地方精确地展开,就像 C/C++ 中的宏一样,例如:
foo = c
prog.o : prog.$(foo)
$(foo)$(foo) -$(foo) prog.$(foo)展开后如下所示:
prog.o : prog.c
cc -c prog.c5. 变量的类型
5.1 有哪些类型变量?
Makefile 中的变量可以分为多种类型,下面通过表格来总结说明:
| 变量类型 | 定义/来源 | 说明 | 示例 |
| 自定义变量 | Makefile 中手动定义 | 用户自己在 Makefile 中定义的变量,使用 =、:=、+=、?= 等赋值符号。这是最基础的变量类型 | CC = gcc |
| 命令行变量 | make 命令传入 | 通过 make 命令行传入的变量,如 make VAR = value。它的优先级最高,会覆盖 Makefile 中的同名变量。 | make CC = g++ |
| 系统环境变量 | 操作系统环境(shell) | 执行 make 命令时,从父进程 Shell 继承的环境变量,make 运行时会自动加载到 Makefile 中。默认情况下,其优先级低于 Makefile 中的定义。 | $PATH, $ HOME |
| 预定义变量 (隐含变量) | make 内置 | Make 工具为了支持其隐含规则而预先定义的变量,通常为编译器、链接器等工具链相关。这是“自定义变量”的一个特例(由 Make 预先定义)。 | CC, CFLAGS, AR |
| 自动化变量 | make 自动生成 | 由 Makefile 自动产生,在规则的命令部分,其值依赖于当前规则的目标和依赖文件。它们只能被引用,不能被赋值。 | $@, $ <, $^等 |
| 目标变量 | Makefile 中定义 | 为特定目标设置的局部变量,其作用域仅限于该目标及其依赖链中的规则。 | target: VAR = value |
| 模式变量 | Makefile 中定义 | 通过模式规则定义的变量,作用于匹配特定模式的文件 | %.o: CFLAGS =-Wall |
| Shell 赋值变量 | make 命令中执行 shell 命令 | 通过$(shell cmd)或`cmd`执行 shell 命令,并将其标准输出作为变量的值。注意:这与在 Shell 环境中设置的变量(系统环境变量)是两回事。 | files := $(shell ls *.c) |
5.2 变量优先级总结
当不同类型的变量同名时,优先级从高到低排列如下:
| 优先级 | 变量类型 | 说明 |
| 1 | 命令行变量 | make 命令传入的参数,优先级最高 |
| 2 | Makefile 中定义的变量 | 包括自定义变量、目标变量、模式变量等 |
| 3 | 系统环境变量 | 操作系统环境变量,make -e 时优先级提升 |
| 4 | 预定义变量 | make 内置的默认变量 |
5.3 变量作用范围
| 变量类型 | 作用范围 |
| 自定义变量(全局) | 整个 Makefile 文件 |
| 目标变量 | 仅在指定目标及其连带规则中有效 |
| 模式变量 | 仅在匹配模式的目标规则中有效 |
| 自动化变量 | 仅在规则命令中有效 |
二、变量的赋值
上边我们定义变量的时候使用的符号是 = ,但是其实在 Makefile 中,赋值方式有四种:
| 符号 | 说明 |
| := | 简单赋值,编程语言中常规理解的赋值方式,只对当前语句的变量有效。 |
| = | 递归赋值,赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。 |
| ?= | 条件赋值,如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。 |
| += | 追加赋值,原变量用空格隔开的方式追加一个新值。 |
1. 简单赋值 :=
我们在 Makefile 文件中添加以下内容:
a:=sumu1
b:=${a} 123456@qq.com
a:=sumu2
test:
@echo "a=${a}"
@echo "b=${b}"会看到输出的结果如下:
sumu@virtual-machine:~/hk/alpha$ make
a=sumu2
b=sumu1 123456@qq.com用这种方式定义的变量,会在变量的定义点,按照被引用的变量的当前值进行展开 。这种方式定义的变量不能使用后面的变量,只能使用前面已定义好了的变量。
2. 递归赋值 =
我们在 Makefile 文件中添加以下内容:
a=sumu1
b=${a} 123456@qq.com
a=sumu2
test:
@echo "a=${a}"
@echo "b=${b}"会看到如下打印:
sumu@virtual-machine:~/hk/alpha$ make
a=sumu2
b=sumu2 123456@qq.com这意味着,即便 a 是在 b 后边进行了修改,但是变量 b 依然引用了 a 修改之后的值。这种赋值方式优点是可以向后引用变量,缺点是不能对该变量进行任何扩展,例如:
a=sumu1
b=${a} 123456@qq.com
a=$(a)
test:
@echo "a=${a}"
@echo "b=${b}"这样会造成一种死循环,应该是会直接报错的:
Makefile:3: *** Recursive variable 'a' references itself (eventually)。 停止。3. 条件赋值 ?=
我们在 Makefile 文件中添加以下内容:
a:=sumu1
b:=${a} 123456@qq.com
a?=sumu2
test:
@echo "a=${a}"
@echo "b=${b}"得到的打印信息如下:
sumu@virtual-machine:~/hk/alpha$ make
a=sumu1
b=sumu1 123456@qq.com若是将第一行的 a:= sumu1 删除,则输出结果是这样的:
sumu@virtual-machine:~/hk/alpha$ make
a=sumu2
b= 123456@qq.com这种赋值方式其实就等价于:
ifeq ($(origin a), undefined)
a = sumu2
endif【说明】什么是 ifeq ?其实是 Makefile 中的条件判断,后边会详细解释,这里简单了解下就可以了。
4. 追加赋值
我们在 Makefile 文件中添加以下内容:
a:=sumu1
b:=${a} 123456@qq.com
a+=${b}
test:
@echo "a=${a}"
@echo "b=${b}"会有如下打印:
sumu@virtual-machine:~/hk/alpha$ make
a=sumu1 sumu1 123456@qq.com
b=sumu1 123456@qq.com三、override 指示符
如果有变量是通常 make 的命令行参数设置的,那么 Makefile 中对这个变量的赋值会被忽略。如果想在 Makefile 中设置这类参数的值,那么,我们可以使用“override”指示符。其语法是:
override <variable>; = <value>;
override <variable>; := <value>;当然,还可以追加
override <variable>; += <more text>;对于多行的变量定义,我们用 define 指示符,在 define 指示符前,也同样可以使用 override 指示符, 如:
override define foo
bar
endef四、 多行变量
还有一种设置变量值的方法是使用 define 关键字。使用 define 关键字设置变量的值可以有换行,这 有利于定义一系列的命令(前面我们讲过“命令包”的技术就是利用这个关键字)。
define 指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以 endef 关键字结束。其工 作方式和“=”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以 [Tab] 键开头,所以如果你用 define 定义的命令变量中没有以 Tab 键开头,那么 make 就不会把其认为是命令。
下面的这个示例展示了 define 的用法:
define two-lines
echo foo
echo $(bar)
endef五、不同类型的变量
这一部分实际说明的是 makefile 文件之外的那一些变量:
1. 预定义变量
内嵌隐含规则(后面会学习)的命令中, 所使用的变量都是预定义的变量。我们将这些变量称为“隐含变量”。这些变量允许对它进行修改:在 Makefile 中、通过命令行参数或者设置系统环境变量的方式来对它进行重定义。
1.1 常见预定义变量
在 Makefile 中也存在着一些预定义变量:
| AR | 库文件维护程序的名称,默认值为 ar。AS 汇编程序的名称,默认值为 as。 |
| CC | C 编译器的名称,默认值为 cc。CPP C 预编译器的名称,默认值为$(CC) –E。 |
| CXX | C++编译器的名称,默认值为 g++。 |
| FC | FORTRAN 编译器的名称,默认值为 f77 |
| RM | 文件删除程序的名称,默认值为 rm -f |
| ARFLAGS | 库文件维护程序的选项,无默认值。 |
| ASFLAGS | 汇编程序的选项,无默认值。 |
| CFLAGS | C 编译器的选项,无默认值。 |
| CPPFLAGS | C 预编译的选项,无默认值。 |
| CXXFLAGS | C++编译器的选项,无默认值。 |
| FFLAGS | FORTRAN 编译器的选项,无默认值。 |
1.2 使用实例
我们在 Makefile 文件中添加以下内容:
test:
@echo "CC = $(CC)"
@echo "AR = $(AR)"
@echo "RM = $(RM)"
@echo "CXX= $(CXX)"将会有以下打印信息:
sumu@virtual-machine:~/hk/alpha$ make
CC = cc
AR = ar
RM = rm -f
CXX= g++2. 目标变量
前面我们所讲的在 Makefile 中定义的变量都是“全局变量”,在整个文件,我们都可以访问这些变 量。当然,“自动化变量”除外,如 $< 等这种类量的自动化变量就属于“规则型变量”,这种变量的值依 赖于规则的目标和依赖目标的定义。
当然,我也同样可以 为某个目标设置局部变量,这种变量被称为“Target-specific Variable”,它可以 和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。
2.1 语法格式
<target ...> : <variable-assignment><variable-assignment> 可以是各种赋值表达式,如 = 、 := 、 += 或是 ?= 。目标变量 可以和全局变量同名,因为它的 作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效,而不会影响规则链以外的全局变量的值。
2.2 使用实例
Makefile 文件内容如下:
a := a.o b.o c.o
test : a := main.o test.o
test :
@echo "a=$(a)"
a:
@echo "a=$(a)"然后我们在终端中执行 make test ,会发现终端输出信息如下:
a=main.o test.o接着我们在终端中再执行 make a ,会发现终端中输出的信息如下:
a=a.o b.o c.o这也就说明了,目标变量仅仅在它自己的规则中有效,并且 make 会优先使用规则内定义的局部变量。
3. 系统环境变量
make 运行时的系统环境变量可以在 make 开始运行时被载入到 Makefile 文件中,但是如果 Makefile 中已定义了这个变量,或是这个变量由 make 命令行带入,那么系统的环境变量的值将被覆盖。(如果 make 指定了“-e”参数,那么,系统环境变量将覆盖 Makefile 中定义的变量)
因此,如果我们在环境变量中设置了 CFLAGS 环境变量,那么我们就可以在所有的 Makefile 中使用 这个变量了。这对于我们使用统一的编译参数有比较大的好处。如果 Makefile 中定义了 CFLAGS,那么 则会使用 Makefile 中的这个变量,如果没有定义则使用系统环境变量的值,一个共性和个性的统一,很 像“全局变量”和“局部变量”的特性。
当 make 嵌套调用时(后面会学习),上层 Makefile 中定义的变量会以系统环境变 量的方式传递到下层的 Makefile 中。当然,默认情况下,只有通过命令行设置的变量会被传递。而定义 在文件中的变量,如果要向下层 Makefile 传递,则需要使用 exprot 关键字来声明。
当然,并不推荐把许多的变量都定义在系统环境中,这样,在我们执行不用的 Makefile 时,拥有的是同一套系统变量,这可能会带来更多的麻烦。
4. 模式变量
在 GNU 的 make 中,还支持模式变量(Pattern-specific Variable),通过上面的目标变量中,我们 知道,变量可以定义在某个目标上。模式变量的好处就是,我们可以给定一种“模式”,可以把变量定义 在符合这种模式的所有目标上。
我们知道,make 的“模式”一般是至少含有一个 % 的,所以,我们可以以如下方式给所有以 .o 结 尾的目标定义目标变量:
%.o : CFLAGS = -O同样,模式变量的语法和“目标变量”一样:
<pattern ...>; : <variable-assignment>;
<pattern ...>; : override <variable-assignment>;override 同样是针对于系统环境传入的变量,或是 make 命令行指定的变量。
5. 自动化变量
关于自动化变量可以理解为由 Makefile 自动产生的变量。在模式规则中,规则的目标和依赖的文件名代表了一类的文件。规则的命令是对所有这一类文件的描述。我们在 Makefile 中描述规则时,依赖文件和目标文件是变动的,显然在命令中不能出现具体的文件名称,否则模式规则将失去意义。
那么模式规则命令中该如何表示文件呢?就需要使用“自动化变量”,自动化变量的取值根据执行的规则来决定,取决于执行规则的目标文件和依赖文件。
5.1 常用自动化变量
自动化变量可以理解为由 Makefile 自动产生的变量。
| $* | 不包含扩展名的目标文件名称(当文件名中存在目录时,也会包含目录部分)。例如, (1)main.o: main.c test.h 中 $* 就代表 main 。 (2)这个变量还表示目标模式中 % 及其之前的部分。如果目标是 dir/a.foo.b ,并且目标的模式是 a.%.b ,那么,$* 的值就是 dir/a.foo 。 【说明】在模式规则和静态模式规则中,代表“茎”。“茎”是目标模式中“%”所代表的部分(当文件名中存在目录时,“茎”也包含目录部分)。 |
| $@ | 表示规则的目标文件完整名称名。如果目标是一个文档文件(Linux 中,一般成 .a 文件为文档文件,也成为静态的库文件),那么它代表这个文档的文件名。在多目标模式规则中,它代表的是触发规则被执行的文件名。 |
| $% | 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是 "foo.a(bar.o)",那么,"$%"就是" bar.o ","$@" 就是 " foo.a "。如果目标不是函数库文件(Unix 下是 [.a],Windows 下是 [.lib]),那么,其值为空。 |
| $< | 规则的第一个依赖的文件名。如果是一个目标文件使用隐含的规则来重建,则它代表由隐含规则加入的第一个依赖文件。如果依赖目标是以模式(即 % )定义的,那么 $< 将是符合模式的一系列的文件集。注意,其是一个一个取出来执行的 |
| $? | 所有比目标文件更新的依赖文件列表,空格分隔。如果目标文件是静态库文件,代表的是库文件(.o 文件)。 |
| $^ | 代表的是所有依赖文件列表,使用空格分隔。如果目标是静态库文件,它所代表的只能是所有的库成员(.o 文件)名。一个可重复的文件出现在目标的依赖中,变量“$^”只记录它的第一次引用的情况。就是说变量“$^”会去掉重复的依赖文件(自动去重)。 |
| $+ | 所有的依赖文件,以空格分开。类似“$^”,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合。 |
(1)在上述所列出来的自动量变量中。四个变量($@ 、$< 、$% 、$* )在扩展时只会有一个文件,而另三个的值是一个文件列表。
(2)茎的概念在后边静态模式的笔记中会有说明。
(3)对于 $< ,为了避免产生不必要的麻烦,我们最好给 $ 后面的那个特定字符都加上圆括号,比如,$(<) 就要比 $< 要好一些。。
5.2 自动变量中的D与F
GNU make 中在这些变量中加入字符 "D" 或者 "F" 就形成了一系列变种的自动化变量,这些自动化变量可以对文件的名称进行操作。这是 GNU make 中老版本的特性,在新版本中,我们使用函数 dir 或 notdir 就可以做到了。D 的含义就是 Directory,就是目录,F 的含义就是 File,就是文件。
| 变量名 | 功能 |
| $(@D) | 表示 $@ 的目录部分(不以斜杠作为结尾),如果 $@ 值是 dir/foo.o ,那么 $(@D) 就是 dir ,而如果 $@ 中没有包含斜杠的话,其值就是 . (当前目录)。 |
| $(@F) | 表示 $@ 的文件部分,如果 $@ 值是 dir/foo.o ,那么 $(@F) 就是 foo.o ,$(@F) 相当于函数$(notdir $@) 。 |
| $(*D), $(*F) | 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,$(*D) 返回 dir ,而 $(*F) 返回 foo |
| $(%D), $(%F) | 分别表示了函数包文件成员的目录部分和文件部分。这对于形同 archive(member) 形式的目标中的 member 中包含了不同的目录很有用。 |
| $(<D), $(<F) | 分别表示依赖文件的目录部分和文件部分。 |
| $(^D), $(^F) | 分别表示所有依赖文件的目录部分和文件部分。(无相同的) |
| $(+D), $(+F) | 分别表示所有依赖文件的目录部分和文件部分。(可以有相同的) |
| $(?D), $(?F) | 分别表示被更新的依赖文件的目录部分和文件部分。 |
5.3 使用实例
我们创建一个Makefile文件,需要注意一点,在 Makefile 中,@echo " $$(@D) = $(@D) ← 目标目录"这句会报错,原因分析如下:
(1)$$ 会被 Make 转换为单个 $ 传递给 Shell。
(2)当我们使用双引号 " 时,Shell 会尝试展开 $(...)。
(3)在 Shell 中,$(...) 意味着“运行括号里的命令”。当Shell 收到了 $(@D),它试图运行名为 @D 的命令,但系统中没有这个命令,所以报错 /bin/sh: @D : 未找到命令。
.PHONY: all prepare clean demo check_new
# 默认目标
all: demo
# ============================================
# 1. 准备演示文件 (make prepare)
# ============================================
prepare:
@echo "=== 创建演示文件 ==="
@mkdir -p demo_dir
@echo "源文件 1" > demo_dir/source1.c
@sleep 1
@echo "源文件 2" > demo_dir/source2.c
@echo "头文件" > demo_dir/header.h
@echo "=== 文件创建完成 ==="
# ============================================
# 2. 主演示目标 (make demo)
# ============================================
# 注意:| demo_dir 是顺序依赖,用于演示 $|
demo: demo_dir/source1.c demo_dir/source2.c demo_dir/source2.c demo_dir/header.h | demo_dir
@echo ""
@echo "============================================"
@echo " 7 个自动变量及其 D/F 参数演示 "
@echo "============================================"
@echo ""
@echo "【1. 目标文件】"
@echo '$$@ = ' $@
@echo '$$(@D) = ' $(@D)
@echo '$$(@F) = ' $(@F)
@echo ""
@echo "【2. 第一个依赖】"
@echo '$$< = ' $<
@echo '$$(<D) = ' $(<D)
@echo '$$(<F) = ' $(<F)
@echo ""
@echo "【3. 所有依赖 (去重)】"
@echo '$$^ = ' $^
@echo '$$(^D) = ' $(^D)
@echo '$$(^F) = ' $(^F)
@echo ""
@echo "【4. 所有依赖 (不去重)】"
@echo '$$+ = ' $+
@echo '$$ (+D) = ' $(+D)
@echo '$$ (+F) = ' $(+F)
@echo ""
@echo "【5. 顺序依赖 (Order-only)】"
@echo '$$| = ' $|
@echo ""
@echo "【6. 比目标新的依赖】"
@echo '$$? = ' $?
@echo ""
@echo "【7. 匹配 stem (模式规则)】"
@echo '$$* = ' $*
@echo " (注:普通规则中为空,请看下方模式规则演示)"
@echo ""
@echo "============================================"
@touch demo
# ============================================
# 3. 模式规则演示 (演示 $* 的效果)
# 运行:make demo_dir/source1.o
# ============================================
demo_dir/%.o: demo_dir/%.c
@echo ""
@echo "【模式规则中的 $$* 演示】"
@echo ' 目标:$@'
@echo ' 依赖:$<'
@echo '$$* = ' $*
@echo '$$ (*D) = ' $( *D)
@echo '$$ (*F) = ' $( *F)
@touch $@
# ============================================
# 4. 单独演示 $?
# 运行:make check_new
# ============================================
check_new: demo_dir/source1.c
@echo ""
@echo "【$$? 专项演示】"
@echo '$$? = ' $?
@touch check_new
# ============================================
# 5. 清理
# ============================================
clean:
@echo "=== 清理演示文件 ==="
@rm -rf demo_dir
@rm -f demo check_new
@echo "=== 清理完成 ==="我们按如下顺序执行命令并观察输出信息
sumu@virtual-machine:~/hk/alpha$ make prepare
=== 创建演示文件 ===
=== 文件创建完成 ===
sumu@virtual-machine:~/hk/alpha$ make
============================================
7 个自动变量及其 D/F 参数演示
============================================
【1. 目标文件】
$@ = demo
$(@D) = .
$(@F) = demo
【2. 第一个依赖】
$< = demo_dir/source1.c
$(<D) = demo_dir
$(<F) = source1.c
【3. 所有依赖 (去重)】
$^ = demo_dir/source1.c demo_dir/source2.c demo_dir/header.h
$(^D) = demo_dir demo_dir demo_dir
$(^F) = source1.c source2.c header.h
【4. 所有依赖 (不去重)】
$+ = demo_dir/source1.c demo_dir/source2.c demo_dir/source2.c demo_dir/header.h
$ (+D) = demo_dir demo_dir demo_dir demo_dir
$ (+F) = source1.c source2.c source2.c header.h
【5. 顺序依赖 (Order-only)】
$| = demo_dir
【6. 比目标新的依赖】
$? = demo_dir/source1.c demo_dir/source2.c demo_dir/header.h
【7. 匹配 stem (模式规则)】
$* =
(注:普通规则中为空,请看下方模式规则演示)
============================================
sumu@virtual-machine:~/hk/alpha$ make demo_dir/source1.o
【模式规则中的 演示】
目标:demo_dir/source1.o
依赖:demo_dir/source1.c
$* = source1
$ (*D) =
$ (*F) =
sumu@virtual-machine:~/hk/alpha$ make check_new
【0 专项演示】
$? = demo_dir/source1.c六、变量高级用法
1. 变量值的替换
1.1 基本格式
我们可以在引用的时候直接替换变量的值,可以直接替换变量中的共有的部分,一般格式如下:
# 先定义一个变量,并简单赋值
var1 := value_list
# 开始替换变量值
var2 := $(var1:<string1>=<string2>)
# 或者
var2 := ${var1:<string1>=<string2>}上边的含义就是,将变量 var1 中的所有以 string1 字符串结尾的变量值替换成以 string2 字符串结尾。
【注意】
(1)这里是 结尾字符串的替换,其他位置的好像是不可以进行替换的。
(2)也可以使用模式规则进行替换,也就是通过 % 来匹配除需要替换的部分以外的字符串。
(3) var1:< string1 >=< string2 > 这一部分的 : 和 = 两端最好不要有空格,否则可能会出现问题。
1.2 使用实例
Makefile 文件内容如下:
var1 := main.o test.o
var2 := $(var1:.o=.c)
var3 := $(var1:%.o=%.c)
test:
@echo "var1=$(var1)"
@echo "var2=$(var2)"
@echo "var3=$(var3)"然后在终端运行 make test ,输出结果如下:
var1=main.o test.o
var2=main.c test.c
var3=main.c test.c可以发现所有的 .o 都被替换为 .c 。
2. 变量嵌套
2.1 语法格式
变量的嵌套引用的具体含义是,我们可以在一个变量的赋值中引用其他的变量,并且引用变量的数量和和次数是不限制的。也就是说可以把变量的值再当成变量。例如,
x = y
y = z
a := $($(x))
test:
@echo "x=$(x)"
@echo "y=$(y)"
@echo "a=$(a)"然后在终端执行 make test ,会看到如下输出:
x=y
y=z
a=z其实, $(x) 的值就是 y ,而外边还有一个 $ ,这样就会变成 $(y) 而 $(y)= z ,所以最后就是 a = $($(x))=$(y)= z 了。
【说明】遇到这种变量嵌套的情况吗,我们就从最里层的 $ 开始向外一层一层进行分析即可。
2.2 使用实例
2.2.1 更多的层次
# 这里a的值最终是 u
x = y
y = z
z = u
a := $($($(x)))2.2.2 在变量定义中使用变量
# 这里的 $($(x)) 被替换成了 $($(y)) ,因为 $(y) 值是“z”,所以,最终结果是:a:=$(z) ,也就是“Hello”。
x = $(y)
y = z
z = Hello
a := $($(x))2.2.3 再加上函数
# $($($(z))) 扩展为 $($(y)) ,而其再次被扩展为 $($(subst 1,2,$(x))) 。
# $(x) 的值是“variable1”,subst 函数把“variable1”中的所有“1”字串替换成“2”字串,于是,“variable1”变成“variable2”,再取其值,所以,最终,$(a) 的值就是 $(variable2) 的值——“Hello”。
x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))2.2.4 多个变量来组成一个变量
# $a_$b 组成了“first_second”,于是,$(all) 的值就是“Hello”。
first_second = Hello
a = first
b = second
all = $($a_$b)参考资料: