6 变量的使用
变量是makefile中为一串字符定义的名字,这个名字就是变量,这个字符串就是变量的值。变量在调用它的地方被显式替换。(有些版本的make把变量叫做宏。)
变量和函数在makefile被读取时展开,但recipe中的变量和函数会在将其传递给shell
时展开。用define
指令定义变量,=
右边的是变量值。
变量可以是文件名列表、传给编译器的选项、需要运行的程序、源文件路径、输出的写入目录,或者你可以想象的任何东西。
变量名可以由不包含:
、#
、=
或者空格的任意字符组成。然而当变量名包含字母数字下划线之外的字符时要格外小心,因为有些版本的make不能将这种变量传递给子make。以.
和大写字母开始的变量拥有特殊的含义,它们留给将来新版make使用。
变量名是区分大小写的,名字foo
, FOO
,和Foo
分别指向不同的变量。
通常变量名使用大写字母,但是我们推荐makefile内部使用的变量使用小写字母,控制隐含规则或者用户用于覆盖命令行选项的变量使用大写字母。
很少有变量只有一个字母或者很少几个字母。这些一般都是自动变量,它们都有特殊用途。
6.1 变量引用基础
要替换变量的值,可以在$
符号后面用括号或者花括号把变量名括起来:$(foo)
或者${foo}
都是对变量foo
的有效引用。要在文件名或recipe中使用$
字符,必须用两个$$
代替。
可以在很多地方使用变量引用,诸如targets,prerequisites,recipes等大多数指令和新变量值中都可以使用变量引用。下面是常见示例,变量包含了程序中所有的object文件名:
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
变量引用执行严格的文本替换。如下规则:
foo = c
prog.o : prog.$(foo)
$(foo)$(foo) -$(foo) prog.$(foo)
相当于:
prog.o : prog.c
cc -c prog.c
由于变量赋值时,变量值前的空格被省略,所以foo
的值就是c
。(千万不要把你的makefile写成这样。)
如果$
符号后面跟一个字符而不是$
,并且没有被括号括起来,那么这个字符被当作变量名。因此可以使用$x
引用变量x
。但是并不鼓励这么做,除非是自动变量。
6.2 变量的两个特点
在GNU make中,可以用两种方法给变量赋值,称之为变量的两个特性。这两个特性根据变量定义的方式以及变量展开的时机来区分。
第一个特性是递归展开变量(recursively expanded variable)。这种变量使用=
或者defile
指令定义。你指定的值会被逐字符展开,如果其中包含变量引用,这些引用在这个变量被替换时展开。这叫做递归展开。例如:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
会回显Huh?
。$(foo)
被展开成$(bar)
,而后者展开成$(ugh)
,最终输出Huh?
。
其他版本make只支持这一种特性。它有利也有弊。一个好处是:
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
当CFLAGS
在一个recipe中展开时,它会展开成-Ifoo -Ibar -O
。不好之处在于你不能给这个变量后面添加别的东西:
CFLAGS = $(CFLAGS) -O
这会导致变量展开时无限循环。(实际上make会检查到这个无限循环,并报错)
另一个不好的是,定义中包含的任何函数引用都会在变量每次展开时执行一次。这会拖慢make的运行速度,更糟的是,这会让通配符和shell函数给出错误的结果,因为你不能控制函数在何时被调用,以及被调用的次数。
另一个特性可以避免递归展开变量遇到的问题和不便,它就是简单展开变量(simply expanded variables)。
简单展开变量使用:=
或::=
定义。在GNU make中两者等效,然而只有::=
在POSIX标准中有声明(2012年才将::=
添加到POSIX标准中,因此更老的版本不支持)。
只有在简单展开变量在定义时,其中包含的变量和函数引用才会被展开一次。简单展开变量的值实际上是文本展开后的结果字符串。其中不包含任何其它变量的引用,它在定义时直接包含其它变量的值。因此:
x := foo
y := $(x) bar
x := later
等效于:
y := foo bar
x := later
当一个简单展开变量被引用时,按字面值展开。
下面是更复杂的示例,展现了:=
和shell
函数连接使用。其中使用了变量MAKELEVEL
,其值在逐层下传时不断增加。
ifeq (0,${MAKELEVEL})
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif
使用:=
的一个好处是,进入一个目录的recipe看起来像这样:
${subdirs}:
${MAKE} -C $@ all
简单展开变量通常使复杂的makefile变得更清晰,因为其工作方式类似于大多数编程语言中的变量。它允许你用变量本身的值来定义变量自己(或者其值可以被扩展函数以某种方式处理),同时可以极大的提高扩展函数的效率。
你也可以使用它们在变量中引入前导空格。在变量引用被替换和函数调用之前,前导空格在你的输入中被抛弃。这意味着你在变量值中包含前导空格,通过变量引用保护它们,像这样:
nullstring :=
space := $(nullstring) # end of the line
这里变量space的值是一个空格。注释# end of the line
只是为了让语句更清晰,这行最后的那个空格是有效的(但是并不易读)。如果你要在变量值结尾加入一个空格,在空格后加上注释会让程序更清晰。同样,如果你不想在结尾加上空格,那么最好不要在语句结束后加上若干空格,然后写上注释:
dir := /foo/bar # directory to put the frobs in
这儿变量dir
的值是/foo/bar
(包括四个空格),这可能并不是你想要的。(想象一下像$(dir)/file
的使用)
?=
是变量的另一个赋值操作符,称作条件变量赋值操作符,因为它只在变量没有被定义时生效。下面的声明:
FOO ?= bar
等效于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
注意,给变量设置一个空值也是变量定义,?=
不会重新设置这个变量。
6.3 变量引用的高级特性
这部分讲述变量的一些高级特性,以使你可以更灵活的使用变量引用。
6.3.1 引用替换
引用替换是指用你指定的值替换变量。形如$(var:a=b)
(或者${var:a=b}
),表示获取变量var
的值,在其中用b
替换单词结尾的每一个a
,并且用结果字符串替换原有字符串。
当我们说单词结尾的a
时,表示a
必须出现在空格的前面或者整个字符串值的最后,变量值其它地方出现的a
不会改变,例如:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
bar
的值为a.c b.c c.c
。
变量的引用替换实际上是扩展函数patsubst
的缩写。同时支持引用替换和patsubst
用于兼容其它实现版本的make。
另一种引用替换能够使用patsubst
函数的完整功能。与上面的形式$(var:a=b)
相同,但是a
必须包含一个%
字符,其等效于$(patsubst a,b,$(var))
。
例如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
bar
的值为a.c b.c c.c
。
6.3.2 计算变量名
计算变量的名字是一个复杂的概念,只在复杂的makefile中使用。大多数情况下不需要考虑它们,只需要知道变量名中包含$
可能产生奇怪的结果。然而,如果你是那种想弄明白一切的人,或许你会对这部分感兴趣,那就接着往下读吧。
变量可以在变量名中被引用。这称之为计算变量名或者叫嵌套变量引用。例如:
x = y
y = z
a := $($(x))
将a
定义为z
:内层的$(x)
展开成y
,而$(y)
展开成z
。这儿引用变量名并没有显式声明,它通过$(x)
计算。对$(x)
的引用嵌套于外层的变量引用中。
前面的例子展示了两层嵌套,但是更多层的前提是可行的。例如,三层嵌套:
x = y
y = z
z = u
a := $($($(x)))
在一个变量名中引用递归展开的变量叫做二次展开(re-expanded)。例如:
x = $(y)
y = z
z = Hello
a := $($(x))
定义a
为Hello
:$($(x))
变成$($(y))
,再变成$(z)
,最后变成Hello
。
嵌套变量引用也可以包含改变引用的函数调用。例如使用subst
函数:
x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))
最终定义a
为Hello
。很怀疑是否有人会像这样曲折的写一个嵌套引用,但是它是正确的:$($($(z)))
展开为$($(y))
,然后展开为$($(subst 1,2,$(x)))
,这会从x
中获取值variable1
,并且用variable2
代替,因此该句成为$(variable2)
,其只是Hello
的简单引用。
计算变量名并非只能包含一个变量引用,其可以包含若干变量引用和固定文本。例如:
a_dirs := dira dirb
1_dirs := dir1 dir2
a_files := filea fileb
1_files := file1 file2
ifeq "$(use_a)" "yes"
a1 := a
else
a1 := 1
endif
ifeq "$(use_dirs)" "yes"
df := dirs
else
df := files
endif
dirs := $($(a1)_$(df))
将会根据use_a
和use_dirs
的设置为dirs
提供不同的值:a_dirs, 1_dirs, a_files,
或1_files
。
计算变量名也可以用于引用替换:
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o
sources := $($(a1)_objects:.o=.c)
会根据a1
的值给sources
设置不同的值:a.c b.c c.c
或 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))
试图给变量foo
赋值为sort a d b g q c
或者strip a d b g q c
,而不是将a d b g q c
作为函数sort
或strip
的参数进行调用。如果有可能,这个限制会在未来去掉。
你也可以把计算变量名用于变量赋值的左边或者用于define
指令,就像:
dir = foo
$(dir)_sources := $(wildcard $(dir)/*.c)
define $(dir)_print =
lpr $($(dir)_sources)
endef
这个例子定义了变量dir
,foo_sources
,和foo_print
。
注意,嵌套引用变量不同于递归展开变量,尽管两者都用于复杂的makefile程序中。
6.4 变量如何获取它们的值
变量有多种方法获取值:
在运行make时用特定值覆盖变量原有值。
在makefile中使用赋值语句或者逐字定义语句。
环境变量成为make变量。
每个规则的自动变量获得属于自己的值。每个自动变量都有单一的常规用法。
有些变量有常量的初始值。
6.5 设置变量
在一个makefile中设置一个变量时,在一行中使用变量名开始,其后跟=
,:=
,或::=
。该行中剩下的部分都是变量的值。例如:
objects = main.o foo.o bar.o utils.o
定义了一个名为objects
的变量。变量名前后的空格和紧跟=
的空格被忽略。
以=
定义的变量成为递归展开(recursively expanded)变量。以:=
和::=
定义的变量称为简单展开(simply expanded)变量,其定义中可以包含变量引用,变量引用会在定义时就展开。
变量名中可能含有函数和变量引用,当该行被读取时,会去查找其中使用的变量名。
变量值的长度没有限制,只取决于计算机内存。可以将较长的变量分割成更易读取的多行。
如果你没设置的话,大多数变量都被认为其值只包含空字符串。有些变量有内建的初始值,并不为空,但是你也可以通过普通方式来设置它们。有些特殊的变量在每条规则中会被设置成新的值,这些叫做自动变量。
如果你希望一个变量在没有被设置时才能被设置,可以使用?=
代替=
。下面两个示例设置FOO的效果相同:
FOO ?= bar
与:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
shell的赋值运算符!=
可以用来执行shell脚本,并且为它的输出设置变量。这个操作符首先计算右边的部分,然后将结果传递给shell去执行。如果执行的结果以一个信号终止,这个新行被移除。其它所有的新行以空格代替。随后结果字符串被放入递归展开变量。例如:
hash != printf '\043'
file_list != find . -name '*.c'
如果执行的结果产生一个$
,而你又不想把跟在它后面的东西解释为变量,那你需要把每个$
替换成$$
当作执行的一部分。或者,你可以为shell函数调用的程序运行的结果设置一个简单展开变量。例如:
hash := $(shell printf '\043')
var := $(shell find . -name "*.c")
由于使用了shell函数,被调用的shell脚本的退出状态被存放在变量.SHELLSTATUS
中。
6.6 给变量追加更多的文本
通常给已经定义的变量追加更多的文本是很有用的。可以使用+=
:
objects += another.o
这会获取objects
的值,并追加文本another.o
(前面会加一个空格)。因此:
objects = main.o foo.o bar.o utils.o
objects += another.o
将objects
设置为main.o foo.o bar.o utils.o another.o
。
使用+=
类似于:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o
但是其间的差异对于更复杂的变量变得很重要。
当变量之前并未定义,+=
行为与=
相同,其定义一个递归展开变量。但是如果前面有定义,那么+=
会遵循变量原始定义的特征。
当你用+=
增加一个变量的值,make的行为本质上与你在原始定义中包含这些额外的文本是一样的。如果你最开始以:=
或::=
定义变量,使其成为简单展开变量。+=
追加到简单展开变量的定义时,在将文本追加到旧值之前,先将文本展开,就像:=
的行为一样。事实上:
variable := value
variable += more
完全等效于:
variable := value
variable := $(variable) more
另一方面,当你为一个最初使用=
定义的递归展开变量使用+=
时,make的做法又有一点不同。回想一下,当你定义一个递归展开变量时,make不会立马展开变量和函数引用的值。相反,make保存文本字面值,并且保存这些变量和函数的引用以便在之后当你指向新变量时展开。当为递归展开变量使用+=
时,make不会展开新增的文本:
variable = value
variable += more
基本上等效于:
temp = value
variable = $(temp) more
当然之前要没有定义过temp
才行。变量的旧值包含变量引用时这很重要,注意如下通用示例:
CFLAGS = $(inculdes) -O
...
CFLAGS += -pg # enable profiling
第一行用另一个变量includes
的引用定义变量CFLAGS
。使用=
定义CFLAGS
,使其成为递归展开变量,这意味着在make处理CFLAGS
的定义时$(includes) -O
不会展开。因此inculdes
不需要在之前被定义,其只需要在CFLAGS
的任何引用之前被定义即可。如果不使用+=
追加CFLAGS
的值,可能要这样写:
CFLAGS := $(CFLAGS) -pg # enable profiling
这很接近,但不是我们想要的。使用:=
重新定义CFLAGS
为简单展开变量,表示在设置这个变量之前make要展开文本$(CFLAGS) -pg
。如果includes
之前没有被定义,我们会得到-O -pg
,并且之后再定义includes
已经没有效果。相反,使用+=
设置CFLAGS
为未展开的$(includes) -O -pg
值。因此我们保留inculdes
的引用,不管在之后任何地方定义includes
,引用$(CFLAGS)
仍然可以使用它的值。
6.7 override指令
如果一个变量已经设置了一个命令参数,那么makefile中对该变量一般的赋值操作就会被忽略。如果你想在makefile中设置一个已经被设置命令参数的变量,可以使用override
指令:
override variable = value
或
override variable := value
给命令行中定义的变量追加文本,可以使用:
override variable += more text
除了其它override
以外,有override
标志的赋值语句比其它的赋值语句有更高的优先级。之后没有被override
标记的赋值或追加操作将被忽略。
voerride
指令不是用来扩大makefile和命令参数之间的战争。它是用来更改或增加用户用命令参数指定的值。
例如,运行C编译器时一般都会使用-g
选项,但也运行用户使用命令参数指定其它的选项。可以使用override
指令:
override CFLAGS += -g
也可以将override
用于define
指令。下面的代码可以如期工作:
override define foo =
bar
endef
请看下一部分关于define
的介绍。
6.8 定义多行变量
另一个设置变量值的方式是使用define
指令。define
允许换行符出现在定义的值中,这便于定义命令序列,也方便于makefile中的eval
语法。
define
指令语法格式是,在define
关键字之后跟变量名以及赋值操作符,变量的值出现在下面的行中,最后以新行endef
结束。define
定义的变量与其它变量的工作方式相同。变量名可以包含函数和变量引用,其中包含的函数和变量会在指令被读入时展开。
可以省略赋值操作符。如果省略,make假设其使用=
,创建递归展开变量。当使用+=
,定义的值被添加到之前的值中,新旧值用空格分开。
可以嵌套使用define
指令,make会检查嵌套指令,如果没有合适的关闭指令endef
,就会报错。注意以recipe前导符开始的一行被认为是recipe,因此如果define
或endef
前面有recipe前导符(默认tab
),则不会被当作make指令。
define two-lines =
echo foo
echo $(bar)
endef
一般的赋值操作不能包含换行符,但是define
中的换行符被当作变量值的一部分(除了endef
前的最后一个换行符)。
当用在recipe中时,前面的例子等效于:
two-lines = echo foo; echo $(bar)
由于两条命令以分号分开,所以其行为类似于两条分开的shell命令。而make会对两行分开的指令调用两个shell。
如果要使define
定义的变量优先于命令行定义的变量,可以使用override
:
override define two-lines =
foo
$(bar)
endef
6.9 未定义变量
如果想要清除一个变量,可以给它设置空值。展开这个变量会得到一个空字符串。然而,如果使用flavor
和origin
函数,那么没有定义的变量和定义为空的变量是不同的。这种情况下可以使用undefine
指令将变量取消。例如:
foo := foo
bar = bar
undefine foo
undefine bar
$(info $(origin foo))
$(info $(flavor bar))
两个变量都会打印undefined
。
如果想要取消一个命令行定义的变量,可以使用override
:
override undefine CFLAGS
6.10 环境中的变量
make中的变量可以来自其运行的环境。每个make检测到的环境变量,都会被转换成同名等值的make变量。然而,makefile中的显式赋值,或者拥有命令参数的变量会覆盖环境变量。(如果指定-e
标志,那么环境变量会覆盖makefile中的赋值操作。)
这样,通过在环境中设置CFLAGS
变量,你可以让大多数makefile使用你想要的C编译选项。给变量使用有特殊意义的名字是比较安全的,因为你可以确定不会有makefile将这些名字用于其它用途。(这并不总是可靠的,有些makefile显式设置CFLAGS
,这样环境中的变量值将无效。)
当make运行一条recipe,makefile中定义的变量会导入到每一个被调用的shell的环境中。这允许你为子make传递值。默认情况下,只有环境中的变量,或者命令行的变量可以递归传递。可以使用export
指令传递其它变量。
不推荐在其它地方使用环境变量。将makefile的功能依赖于外部定义的环境变量是不明智的,这会导致对于相同的makefile,不同的用户会得到不同的结果。这违背了大多数makefile目的。
这个问题与变量SHELL
有些类似,SHELL
通常在环境中提供,用于指定交互用的shell。最好不要让这个选择影响make的行为。因此make以一种特殊的方式处理环境变量SHELL
。
6.11 特定目标变量值
make中的变量值通常是全局的,也就是说不管它们在哪被赋值,其效果都相同(除非它们被复位)。自动变量是一个列外。
另一个例外就是特定目标变量值(target-specific variable values)。这个特性允许你基于make当前建立的目标(target),为同一个变量定义不同的值。与自动变量一样,这些值只在当前目标的recipe(和其它特定目标变量赋值)中可用。
设置一个特定目标变量值:
target ... : variable-assignment
特定目标变量赋值可以加任意特定关键字的前缀,export
,override
,或private
,这些只应用于当前变量。
多目标规则会为列表中的每个target分别创建一个特定目标值。
任何形式的赋值对variable-assignment
都是有效的,递归(=
),简单(:=
或::=
),追加(+=
),或者条件(?=
)。所有在variable-assignment
中出现的变量,只在当前目标环境中被赋值,因此任何先前定义的特定目标变量值都会受到影响。注意,这个变量与全局变量有区别:这两个变量不需要有相同的特征(递归或简单)。
特定目标变量与其它makefile变量有相同的优先级。命令行中提供的变量有更高的优先级(如果环境中指定-e
选项)。override
指令使特定目标变量值有更高的优先级。
特定目标变量有另外一个特性:当你定义一个特定目标变量时,其影响此目标的所有prerequisites,以及这些prerequisites的prerequisites(除非这些prerequisites用它们自己的特定目标变量值覆盖了这个变量)。因此,下面的声明:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
会在prog
的recipe中设置CFLAGS
为-g
,也会在创建prog.o
,foo.o
和bar.o
以及它们的prerequisites的recipe中设置CFLAGS
为-g
。
每次make调用时,一个给定的prerequisite最多只被创建一次。如果多个目标中有同一个文件作为prerequisite,并且每个目标对相同的特定目标变量有不同的值,那么第一个被创建的目标会导致这个prerequisite被创建,这个prerequisite从这个目标中继承这个特定目标值。make会忽略来自其它目标的特定目标值。
6.12 特定模板变量值
除了特定目标变量值以外,GNU make还支持特定模板变量值(pattern-specific variable values)。这种形式下,这个变量会定义在任何匹配特定模板的目标中。
设置一个特定模板变量值:
pattern ... : variable-assignment
这儿模板是%
模板,与特定目标变量值一样,多模板规则为每个模板单独创建一个特定模板变量值。variable-assignment
可以是任何有效的赋值操作符。任何命令行变量有更高的优先级,除非用override
指定。
例如:
%.o : CFLAGS = -O
会给所有匹配模板%.o
的目标设置值为-O
的CFLAGS
。
如果一个目标匹配多个模板,匹配词干最长的特定模板变量最先被使用。这表示特殊的模板比一般的模板更优先,例如:
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
lib/%.o: CFLAGS := -fPIC -g
%.o: CFLAGS := -g
all: foo.o lib/bar.o
这个示例中,第一个CFLAGS
变量定义用于更新lib/bar.o
,即使第二个模板也能匹配这个目标。如果匹配的词干长度相同,则按它们在makefile中定义的顺序考虑。
特定模板变量在目标显式定义的特定目标变量之后被查询,在父目标中定义的特定目标变量之前被查询。
6.13 抑制继承
按照前面的描述,make变量通过prerequisites被继承。这个功能允许你通过改变prerequisite的行为,来改变基于该prerequisite重建的target。例如,你可以给debug
目标设置特定目标变量,然后运行make debug
可以使这个变量被debug
的所有的prerequisites继承,而运行make all
则不会有这个变量赋值操作。
有时候你不想让一个变量被继承,make提供了private
语句。尽管这个语句可用于任何变量赋值,但是在特定目标和特定模板变量中最有用。任何标记为private
的变量仅对其target可见,但可以被这个目标的prerequisites继承。一个标记为private
的全局变量在全局范围内可见,但是不能被任何目标继承,因此在任何recipe中都不可见。
考虑下面的例子:
EXTRA_CFLAGS =
prog: private EXTRA_CFLAGS = -L/usr/local/lib
prog: a.o b.o
由于private
语句,a.o
和b.o
不会从目标prog
中继承EXTRA_CFLAGS
变量的赋值。
6.14 其它特殊变量
GNU make支持一些有特殊功能的变量。
MAKEFILE_LIST
包含make处理的每个makefile文件名。当前文件在make开始处理前就加入到
MAKEFILE_LIST
中,因此如果一个makefile文件的第一条语句是检查这个变量的最后一个单词的话,会得到当前文件名。一旦在当前文件使用了include
,那么被包含的文件会成为最后一个文件名。
如果一个名为Makefile
的makefile有如下内容:name1 := $(lastword $(MAKEFILE_LIST)) include inc.mk name2 := $(lastword $(MAKEFILE_LIST)) all: @echo name1 = $(name1) @echo name2 = $(name2)
会产生如下输出:
name1 = Makefile name2 = inc.mk
.DEFAULT_GOAL
如果命令行没有指定目标,则会使用设置的默认目标。变量
.DEFAULT_GOAL
查看当前的默认目标,或者通过清空该值重新开始默认目标的选择算法,或者显式设置默认目标。下面的示例说明了这些情况:# Query the default goal. ifeq ($(.DEFAULT_GOAL),) $(warning no default goal is set) endif .PHONY: foo foo: ; @echo $@ $(warning default goal is $(.DEFAULT_GOAL)) # Reset the default goal. .DEFAULT_GOAL := .PHONY: bar bar: ; @echo $@ $(warning default goal is $(.DEFAULT_GOAL)) # Set our own. .DEFAULT_GOAL := foo
这个makefile打印如下信息:
no default goal is set default goal is foo default goal is bar foo
注意,将超过一个的目标名赋值给
.DEFAULT_GOAL
是无效的,会产生一个错误。MAKE_RESTARTS
只有在这个make实例重新开始时,这个变量才会被设置。这个变量记录这个实例重新开始的次数。注意这不同于递归(变量
MAKELEVEL
记录)。不能设置,修改或导出这个变量。MAKE_TERMOUT
MAKE_TERMERR当make开始时,会检查是否有
stdout
和stderr
将其输出显示在终端。如果有,make会分别设置MAKE_TERMOUT
和MAKE_TERMERR
为终端设备名(或者当无法确定时设置为true
)。这些变量如果被设置,则会标记为export
。这些变量不能被make修改,如果已经被设置,则无法修改。
这些值被用于决定make是否将它的输出写到终端,例如,它们可以被用于测试是否让recipe命令产生有色输出。.RECIPEPREFIX
这个变量的第一个值的第一个字符用来作为recipe行的引导字符。如果这个变量为空(默认),那这个字符是标准
tab
字符。例如,下面是一个有效的makefile:.RECIPEPREFIX = > all: > @echo Hello, world
.RECIPEPREFIX
的值可以被多次改变,一旦被设定,在下次更改之前都有效。.VARIABLES
扩展为到目前为止定义的所有全局变量名列表。它包含空值变量,以及内建变量。但不包含任何定义于特定目标环境的变量。注意,你赋给它的所有值都会被忽略,它总是返回其特定值。
.FEATURES
扩展为当前版本make支持的所有特性列表。包含但不限于以下值:
‘archives’
支持使用特殊文件名语法的ar
文件。‘check-symlink’
支持-L
(--check-symlink-times
)标志。‘else-if’
支持非嵌套条件else if
。‘jobserver’
支持job server
并行建立。‘oneshell’
支持特殊目标.ONESHELL
。‘order-only’
支持order-only prerequisites
。‘second-expansion’
支持prerequisite列表二次展开。‘shortest-stem’
选择模板时,如果有多个可用选项,使用“最短词”干方法。‘target-specific’
支持特定目标和特定模板变量赋值。‘undefine’
支持undefine
指令。‘guile’
使用GNU Guile作为一个嵌入式扩展语言。‘load’
支持创建用户扩展时,动态加载objects
。.INCLUDE_DIRS
扩展为make查找的包含makefile目录列表。