目录
我们继续Makefile编写的内容学习~第三节可参考:↓传送门↓
如何编写Makefile(3)-CSDN博客
https://blog.csdn.net/L_peanut/article/details/144242622?spm=1001.2014.3001.5501 本节内容主要介绍Makefile的变量使用。
Makefile中定义的变量类似于C/C++中的宏,其代表一个文本字符串,在Makefile执行的时候会自动在使用的位置进行展开。与C/C++不相同的是,我们可以在Makefile中改变其值。Makefile中,变量可以使用在“目标”,“依赖目标”,“命令”或者是Makefile的其它部分中。变量的命令可以包含字符、数字、下划线(可以是数字开头),但不可以包含 :、#、=或者是空字符(空格、回车等)。变量是大小写敏感的,“test”、“Test”和“TEST”是三个不同的变量名。传统Makefile变量名是全大写的方式,平常使用时可以使用类似于C++变量驼峰式命名的方式,如“MakeFlags”,这样可以避免与系统变量冲突。
一、基础
变量在声明时需要赋初值,在使用时需要在变量名前面加上$符号,但最好用小括号()或大括号{}把变量包含起来。如果需要使用真实的$字符,可以使用$$来表示。变量可以在许多地方使用,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
objects=a.o b.o c.o
program:$(objects)
cc -o program $(objects)
$(objects):header.h
变量会在使用它的地方精确展开,就像C/C++中的宏一样。如:
value=c
a.o:a.$(value)
$(value)$(value) -$(value) a.$(value)
展开后可以得到:
a.o:a.c
cc -c a.c
当然,如果你在Makefile中这样写是非常不科学的。这里只是举例来说明Makefile中的变量在使用处展开的真实模样,原理是“替代”。
二、变量中的变量
在定义变量的值时,可以使用其它变量来构造变量的值,在Makefile中有两种方式来使用变量定义变量的值。
第一种方式。使用“=”号,=的左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定是已经定义好的值,也可以是使用后面定义的值。如:
value=$(a)
a=$(b)
b=c
all:
echo $(value)
如果我们执行“make all”,将会打印出变量$(value)的值是c,($(value))的值是$(a),$(a)的值是$(b),$(b)的值是c。这个功能有益有弊,好的地方是我们可以把变量的真实值推导后面来定义,如:
CFLAGS=$(include_dirs) -O
include_dirs=-Iuser1 -Iuser2
当CFLAGS在命令中被展开时,会是-Iuser1 -Iuser2 -O,但是这种形式也有不好的地方,那就是递归定义。譬如说:
CFLAGS=$(CFLAGS) -O
或者
A=$(B)
B=$(A)
这样会让make陷入无限的变量展开中,不过make是有能力检测这样的定义的,并且会报错。同时,如果在变量中使用函数,那么这种方式将会让我们运行make时非常慢,更糟糕的是使用的两个参数“wildcard”和“shell”可能会发生不可预知的错误。因为不知道这两个函数会调用多少次。
为了避免上面的情况,可以使用make中的另一种变量来定义变量的方法,这种方法使用:=符号。如:
x:=a
y:=$(x) b
x:=c
等价于
y:=a b
x:=c
这种方法前面的变量不能使用后面的变量,只能使用前面已经定义好了的变量,如果是这样:
y:=$(x) b
x:=a
那么,y的值是“b”,而不是“a b”。
下面这个例子相对复杂,包括了make的函数、条件表达式和系统变量:
ifeq (0,${MAKELEVEL})
cur-dir:=$(shell pwd)
whoami:=$(shell whoami)
host-type:=$(shell arch)
MAKE:=${MAKE} host-type=${host-type} whoami=${whoami}
endif
其中,MAKELEVEL是系统变量,意思是如果我们的make有一个嵌套执行的动作,那么这个变量会记录我们的当前Makefile调用层数。下面这个例子,如果我们要定义一个变量,其值是一个空格,那么我们可以这样来:
nullstring:=
space:=$(nullstring) #end of the line
nullstring是一个Empty变量,其中什么也没有,而space的值是一个空格。因为在操作符的右边很难描述一个空格,这里先用一个Empty变量来标明变量的值开始了,后面采用“#”注释符来表示变量定义的终止,这样我们可以定义出其值是一个空格的变量。如果我们这样定义了一个变量:
dir:=/home/user #directory to put the frobs in
dir这个变量的值是“/home/user”,后面还跟了4个空格,如果我们这样使用变量来指定其它目录即“$(dir)/file”就不对了。
还有一个比较有用的操作符是?=,如:
A?=b
含义是如果A没有被定义过,那么变量A的值就是“b”,如果A先前被定义过,那么这条语句什么也不做,等价于:
ifeq ($(origin A), undefined)
A=b
endif
三、变量的高级用法
我们介绍两种变量的高级用法,第一种是变量值的替换。
我们可以替换变量中共有的部分,格式为$(var:a=b)或者是${var:a=b},意思是把变量“var”中所有以“a”字串“结尾”的“a”替换为“b”字串。这里的“结尾”意思是“空格”或者是“结束符”。如下:
value1:=a.o b.o c.o
value2:=$(value1:.o=.c)
这个示例中先定义了一个$(value)变量,第二行的意思是把$(value)中所有以.o字串“结尾”的全部替换成.c,所以$(value2)的值就是"a.c b.c c.c"。
另一种变量替换的方法是以“静态模式”定义的,如:
value1:=a.o b.o c.o
value2:=$(value1:%.o=%.c)
这依赖于被替换字串中有相同的模式,模式必须包含一个%字符,其结果也是$(value2)的值为“a.c b.c c.c”。
第二种高级用法是“把变量的值再当成变量”,如:
x=y
y=z
a:=$($x))
例子中$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”。我们还可以使用更多的层次:
x=y
y=z
z=u
a:=$($($(x)))
这里a的值是“u”。
我们再看一个复杂一点的例子:
x=$(y)
y=z
z=Hello
a:=$($(x))
这里的$($x))被替换成$($(y)),因为y的值是“z”,所以最终结果是a:=$(z),也就是Hello。
如果再复杂一点,加上函数:
x=value1
value2:=Hello
y=$(subst 1,2,$(x))
z=y
a:=$($($(z)))
例子中$($($(z)))扩展为$($(y)),再次被扩展为$($(subst 1,2,$(x)))。$(x)的值是“value1”,subst函数把“value1”中的所有“1”字串替换成“2”字串,于是“value1”变成“value2”,再取其值。所以最终$(a)的值就是$(value2)的值——“Hello”。
这种方式中可以使用多个变量来组成一个变量的名字,然后再取其值:
first_second=Hello
a=first
b=second
all=$($a_$b)
这里的$a_$b组成了一个“first_second”,于是$(all)的值就是“Hello”。再来看看结合第一种技术的例子:
a_objects:=a.o b.o c.o
1_objects:=1.o 2.o 3.o
sources:=$($(a1)_objects:.o=.c)
这个例子中如果$(a1)的值是“a”的话,$(sources)的值就是“a.c b.c c.c”;如果$(a1)的值是“1”,那么$(sources)的值是“1.c 2.c 3.c”。
再看这种技术和“函数”与“条件语句”一起使用的例子:
ifdef do_sort
func:=sort
else
func:=strip
endif
bar:=a d b g q c
foo:=$($(func) $(bar))
示例中如果定义了“do_sort”,foo:=$(sort a d b g q c),于是$(foo)的值就是“a b c d g q”,如果没有定义“do_sort”,foo:=$(strip a d b g q c),调用的就是strip函数。“把变量的值再当成变量”这种技术同样可以用在操作符的左边:
dir=foo
$(dir)_sources:=$(wildcard $(dir)/*.c)
define $(dir)_print
lpr $($(dir)_sources)
endef
这个例子中定义了三个变量:“dir”、“foo_sources”和“foo_print”。
四、追加变量值
我们可以使用+=操作符给变量追加值,如:
objects=main.o a.o b.o c.o
objects+=other.o
其中的$(objects)就变成“main.o a.o b.o c.o other.o”,使用+=操作符,可以模拟为下面的例子:
objects=main.o a.o b.o c.o
objects:=$(objects) other.o
不同的是,使用+=更加简洁。如果变量之前没有定义过,+=会自动变为=,如果有变量定义,那么+=会继承前次操作的赋值符。如果前一次是:=,那么+=会以:=作为其赋值符。如:
value:=a
value+=more
等价于
value:=a
value:=$(value) more
但如果是这种情况:
value=a
value+=more
由于前次的赋值符是=,所以+=也会以=来作为赋值, 那么就不会发生变量的递归定义,这样是不好的,make会自动为我们解决这个问题。
五、override指示符
如果有变量是通过make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如果想在Makefile中设置这类参数的值,可以使用“override”指示符,语法为:
override <variable>;=<value>;
override <variable>;:=<value>;
也可以追加:
override <variable>; +=<more text>;
对于多行的变量定义,我们使用define指示符,在define之前,也同样可以使用override指示符,如:
override define a
bar
endef
六、多行变量
还有一种设置变量值的方法是使用“define”关键字,使用define关键字设置变量的值可以有换行,这有利于一些列的命令。
define指示符后面跟的是变量的名字,另起一行定义变量的值。定义是以endef关键字结束,其工作方式和“=”操作符一样。变量的值可以包括函数、命令、文字或者是其它变量。因为命令需要以Tab键开头,所以如果用define定义的命令变量中没有以Tab键开头,那么make就不会把其认为是命令。
下面展示define的用法:
define two-lines
echo foo
echo $(bar)
endef
七、环境变量
make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中已定义了这个变量,或者这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make指定了-e参数,系统环境变量将覆盖Makefile中定义的变量)
如果我们在环境变量中设置了CFLAGS环境变量,那么我们就可以在所有的Makefile中使用这个变量了。如果Makefile中定义了CFLAGS,那么则会使用Makefile中的这个变量,如果没有定义则使用系统环境变量的值,类似于“全局变量”和“局部变量”的特性。
当make嵌套调用时,上层Makefile中定义的变量会以系统环境变量的方式传递给下层的Makefile。默认情况下,只有通过命令行设置的变量会被传递。定义在文件中的变量,如果要向下层Makefile传递,则需要使用export关键字来声明。
八、目标变量
前面我们所说的在Makefile中定义的变量都是“全局变量”,在整个文件中,我们都可以访问这些变量。“自动化变量”除外,如$<等这种类型的自动化变量就属于“规则型变量”,这种变量的值依赖于规则的目标和依赖目标的定义。
同样也可以为某个目标设置局部变量,这种变量称为“Target-specific Variable”,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效,不会影响规则链之外的全局变量的值。
语法为:
<target ...>:<variable-assignment>;
<target ...>:override <variable-assignment>;
<variable-assignment>;可以是前面提到的各种赋值表达式,如=、:=、+=、?=。第二个语法是针对于make命令行带入的变量,或者是系统环境变量。
这个特性很有用,当我们设置了一个这样的变量,这个变量会作用到由这个目标所引发的所有规则中,如:
prog:CFLAGS = -g
prog:prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o:prog.c
$(CC) $(CFLAGS) prog.c
foo.o:foo.c
$(CC) $(CFLAGS) foo.c
bar.o:bar.c
$(CC) $(CFLAGS) bar.c
示例中,无论全局的$(CFLAGS)的值是什么,在prog目标以及其所引发的所有规则中,$(CFLAGS)的值都是-g。
九、模式变量
在GNU的make中,支持模式变量(Pattern-specific Variable),通过上面的目标变量中,我们指定变量可以定义在某个目标上。模式变量的好处是我们可以给定一种“模式”,可以把变量定义在符合这种模式的所有目标上。
make的“模式”一般至少含有一个%,所以我们可以采用如下方式给所有以.o结尾的目标定义目标变量:
%.o:CFLAGS = -O
同样,模式变量的语法和“目标变量”一样:
<pattern ...>;:<variable-assignment>;
<pattern ...>;:override <variable-assignment>;
override同样是针对于系统环境传入的变量或者是make命令行指定的变量。
-变量使用&spm=1001.2101.3001.5002&articleId=144265633&d=1&t=3&u=8acce69af94c436d8d50b7cefa3b4c04)
7713

被折叠的 条评论
为什么被折叠?



