GNU Makefile变量赋值 =, ? =, := 和+=有什么区别?

有人能清楚地解释变量赋值在Makefile中是如何工作的吗?

有什么区别:

 VARIABLE = valueVARIABLE ?= valueVARIABLE := valueVARIABLE += value

我已经阅读了GNU Make手册中的部分,但它对我来说仍然没有意义。

258882 次浏览

懒惰套装

VARIABLE = value

变量的正常设置,但value字段中提到的任何其他变量都将在使用变量时使用它们的值递归扩展,而不是声明时的值

即时集

VARIABLE := value

通过简单扩展内部值来设置变量-其中的值在声明时进行扩展。

懒惰集如果缺席

VARIABLE ?= value

仅当变量没有值时才设置变量。访问VARIABLE时始终计算value。它等效于

ifeq ($(origin VARIABLE), undefined)VARIABLE = valueendif

有关更多详细信息,请参阅留档

追加

VARIABLE += value

将提供的值附加到现有值(如果变量不存在,则设置为该值)

当你使用VARIABLE = value时,如果value实际上是对另一个变量的引用,那么只有在使用VARIABLE时才确定该值。这最好用一个例子来说明:

VAL = fooVARIABLE = $(VAL)VAL = bar
# VARIABLE and VAL will both evaluate to "bar"

当你使用VARIABLE := value时,你会得到value就像现在一样的值。例如:

VAL = fooVARIABLE := $(VAL)VAL = bar
# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"

使用VARIABLE ?= val意味着您只设置VARIABLE如果VARIABLE的值尚未设置。如果尚未设置,则将推迟值的设置,直到使用VARIABLE(如示例1)。

VARIABLE += value只是将value附加到VARIABLEvalue的实际值与最初设置时的值相同,使用=:=

使用=会导致变量被赋予一个值。如果变量已经有一个值,则将其替换。使用时此值将被扩展。例如:

HELLO = worldHELLO_WORLD = $(HELLO) world!
# This echoes "world world!"echo $(HELLO_WORLD)
HELLO = hello
# This echoes "hello world!"echo $(HELLO_WORLD)

使用:=类似于使用=。但是,它不是在使用时扩展值,而是在赋值过程中扩展值。例如:

HELLO = worldHELLO_WORLD := $(HELLO) world!
# This echoes "world world!"echo $(HELLO_WORLD)
HELLO = hello
# Still echoes "world world!"echo $(HELLO_WORLD)
HELLO_WORLD := $(HELLO) world!
# This echoes "hello world!"echo $(HELLO_WORLD)

使用?=为变量分配一个值iff,该变量之前没有分配。如果变量之前被分配了一个空白值(VAR=),它仍然被视为设置我认为。否则,功能与=完全相同。

使用+=就像使用=一样,但不是替换值,而是将值附加到当前值,中间有一个空格。如果变量先前设置为:=,则将其扩展为我认为。当使用我认为时,结果值将被扩展。例如:

HELLO_WORLD = helloHELLO_WORLD += world!
# This echoes "hello world!"echo $(HELLO_WORLD)

如果使用类似HELLO_WORLD = $(HELLO_WORLD) world!的东西,会导致递归,这很可能会结束Makefile的执行。如果使用A := $(A) $(B),结果将与使用+=不完全相同,因为B是用:=扩展的,而+=不会导致B被扩展。

我建议你使用“make”做一些实验。这是一个简单的演示,显示了=:=之间的区别。

/* Filename: Makefile*/x := fooy := $(x) barx := later
a = foob = $(a) bara = later
test:@echo x - $(x)@echo y - $(y)@echo a - $(a)@echo b - $(b)

make test打印:

x - latery - foo bara - laterb - later bar

点击这里查看更详细的解释

在上面的答案中,重要的是要理解是什么意思“值在声明/使用时被扩展”。给出一个像*.c这样的值不需要任何扩展。只有当这个字符串被命令使用时,它可能会触发一些全局化。类似地,像$(wildcard *.c)$(shell ls *.c)这样的值不需要任何扩展,即使我们在变量定义中使用了:=,也会在定义时完全求值。

在您有一些C文件的目录中尝试以下Makefile:

VAR1 = *.cVAR2 := *.cVAR3 = $(wildcard *.c)VAR4 := $(wildcard *.c)VAR5 = $(shell ls *.c)VAR6 := $(shell ls *.c)
all :touch foo.c@echo "now VAR1 = \"$(VAR1)\"" ; ls $(VAR1)@echo "now VAR2 = \"$(VAR2)\"" ; ls $(VAR2)@echo "now VAR3 = \"$(VAR3)\"" ; ls $(VAR3)@echo "now VAR4 = \"$(VAR4)\"" ; ls $(VAR4)@echo "now VAR5 = \"$(VAR5)\"" ; ls $(VAR5)@echo "now VAR6 = \"$(VAR6)\"" ; ls $(VAR6)rm -v foo.c

运行make将触发一个规则,该规则创建一个额外的(空)C文件,称为foo.c,但6个变量中没有一个值为foo.c

投票最多的答案可以改进。

让我参考GNU Make手册“设置变量”“味道”,并添加一些注释。

递归扩展变量

您指定的值是逐字安装的;如果它包含对其他变量的引用,则每当替换此变量时(在扩展其他字符串的过程中)都会扩展这些引用。发生这种情况时,它被称为递归展开

foo = $(bar)

这个陷阱foo将被扩展为$(bar)的值每次foo被评估,可能导致不同的值。当然你不能称之为“懒惰”!如果在午夜执行,这会让你大吃一惊:

# This variable is haunted!WHEN = $(shell date -I)
something:touch $(WHEN).flag
# If this is executed on 00:00:00:000, $(WHEN) will have a different value!something-else-later: somethingtest -f $(WHEN).flag || echo "Boo!"

简单扩展变量

VARIABLE := valueVARIABLE ::= value

用':='或'::='定义的变量只是扩展变量。

简单的扩展变量由使用':='或'::='[…]的行定义。这两种形式在GNU make中是等价的;但是只有'::='形式由POSIX标准[…] 2012描述。

简单扩展变量的值会被一劳永逸地扫描,当定义变量时,扩展对其他变量和函数的任何引用。

没什么要补充的。它会立即进行评估,包括递归扩展变量的递归扩展。

这个陷阱:如果VARIABLE指的是ANOTHER_VARIABLE

VARIABLE := $(ANOTHER_VARIABLE)-yohoho

并且ANOTHER_VARIABLE在此赋值之前未定义,ANOTHER_VARIABLE将扩展为空值。

如果未设置则分配

FOO ?= bar

相当于

ifeq ($(origin FOO), undefined)FOO = barendif

其中只有当变量根本没有设置时,$(origin FOO)才等于undefined

这个陷阱:如果FOO在makefile、shell环境或命令行覆盖中设置为空字符串,则没有将被分配bar

追加

VAR += bar

追加

当有问题的变量之前没有定义时, ‘+=’ 就像普通的“=”一样:它定义了一个递归扩展的变量。然而,当有之前的定义时,“+=”的确切作用取决于你最初定义的变量的味道。

所以,这将打印foo bar

VAR = foo# ... a mile of codeVAR += $(BAR)BAR = bar$(info $(VAR))

但这将打印foo

VAR := foo# ... a mile of codeVAR += $(BAR)BAR = bar$(info $(VAR))

这个陷阱+=的行为不同,这取决于VAR之前分配的变量类型。

多行值

将多行值分配给变量的语法是:

define VAR_NAME :=linelineendef

define VAR_NAME =linelineendef

赋值运算符可以省略,然后它创建一个递归扩展的变量。

define VAR_NAMElinelineendef

endef之前的最后一个换行符被删除。

奖励:shell赋值操作符'!='

 HASH != printf '\043'

HASH := $(shell printf '\043')

不要使用它。$(shell)调用更具可读性,并且非常不鼓励在makefile中使用两者。至少,$(shell)遵循了Joel的建议,让错误的代码看起来明显是错误的