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


763

任何人都可以清楚地解释变量赋值在Makefiles中是如何工作的。

之间有什么区别:

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

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

Answers:


1027

懒集

VARIABLE = value

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

立即设置

VARIABLE := value

使用内部值的简单扩展设置变量-声明时扩展其中的值。

缺席的懒惰集

VARIABLE ?= value

仅在没有值的情况下设置变量。value总是在VARIABLE访问时评估。相当于

ifeq ($(origin FOO), undefined)
  FOO = bar
endif

有关更多详细信息,请参见文档

附加

VARIABLE += value

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


25
A + = B会展开B吗?就是说,如果我先做A + = B,然后做B + = C,A会求值$ {B}和$ {C}的串联吗?
Anton Daneyko

15
如手册的链接部分所述。+ =根据原始分配具有的任何简单或递归语义进行操作。因此,是的,它将扩展RHS,但是它是立即执行还是以延迟方式执行,取决于LHS变量的类型。
Etan Reisner 2013年

6
当您说变量值扩展时,您是什么意思?
Sashko Lykhenko,2015年

2
@СашкоЛихенко在这里看看扩展的含义 gnu.org/software/make/manual/make.html#Flavors
Umair R

7
是“懒惰”还是“立即设置”?我可以“如果不存在则懒惰设置”和“如果不存在则立即设置”吗?
Woodrow Barlow

268

使用=将为变量分配一个值。如果变量已经具有值,则将其替换。使用时将扩展该值。例如:

HELLO = world
HELLO_WORLD = $(HELLO) world!

# This echoes "world world!"
echo $(HELLO_WORLD)

HELLO = hello

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

使用:=与使用类似=。但是,与其在使用时扩展值,不如在分配期间扩展它。例如:

HELLO = world
HELLO_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)

使用?=会为变量分配一个值,前提是该变量先前未分配。如果该变量先前分配了一个空白值(VAR=),我认为仍将其视为set 。否则,功能与完全相同=

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

HELLO_WORLD = hello
HELLO_WORLD += world!

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

如果使用了类似的东西HELLO_WORLD = $(HELLO_WORLD) world!,则将导致递归,这很可能会终止Makefile的执行。如果A := $(A) $(B)使用了,其结果不会是完全一样的使用+=,因为B与扩大:=,而+=不会造成B被扩大。


3
因此,其结果是VARIABLE = literal并且VARIABLE := literal始终是等效的。我说对了吗?
aiao

1
@aiao,是的,因为字面量不改变其用法
塞巴斯蒂安

一个细微的区别是:-?:可以提高称为makefile的递归性能。例如,如果$?= $(shell some_command_that_runs_long_time)。在递归调用中,它将仅被评估一次。导致构建性能提高。:=将变慢,因为该命令不必要地运行了多次
KeshV

61

我建议您使用“ make”进行一些实验。这是一个简单的演示,展示了=和之间的区别:=

/* Filename: Makefile*/
x := foo
y := $(x) bar
x := later

a = foo
b = $(a) bar
a = later

test:
    @echo x - $(x)
    @echo y - $(y)
    @echo a - $(a)
    @echo b - $(b)

make test 印刷品:

x - later
y - foo bar
a - later
b - later bar

在这里查看更详细的解释


5
最好@在每个配方前都使用a ,以免造成结果重复。
亚历山德拉·德·奥利维拉

2
Make不支持/* ... */阻止评论
yoonghm '19

31

当使用时VARIABLE = value,如果value实际上是对另一个变量的引用,则仅在VARIABLE使用时确定该值。最好用一个例子说明:

VAL = foo
VARIABLE = $(VAL)
VAL = bar

# VARIABLE and VAL will both evaluate to "bar"

使用时VARIABLE := value,您会获得value 现在的价值。例如:

VAL = foo
VARIABLE := $(VAL)
VAL = bar

# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"

使用VARIABLE ?= val意味着您仅设置了VARIABLE if 的值VARIABLE(尚未设置)。如果尚未设置,则将值的设置推迟到VARIABLE使用为止(如示例1中所示)。

VARIABLE += value只是附加valueVARIABLEvalue使用=或来确定初始设置时的实际值:=


实际上,在您的第一个示例中,VARIABLE是$(VAL)而VAL是bar。使用时会扩展VARIABLE。
斯特拉格

1
是的,评论正在解释使用它们时会发生什么。
mipadi

啊; 我猜您已纠正它,或者我将“评估”误读为“是”。
斯特拉格

7

在以上答案中,重要的是要了解 “在声明/使用时扩展值”的含义。给一个像*.c并不需要任何扩展。仅当命令使用此字符串时,它才可能触发一些glob。类似地,即使我们在变量定义中使用过,类似$(wildcard *.c)或的值$(shell ls *.c)也不需要任何扩展,并且在定义时会完全求:=值。

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

VAR1 = *.c
VAR2 := *.c
VAR3 = $(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其值。


这是一个很好的呼吁,并在声明时提供了许多扩展示例,使用示例扩展一些答案并在使用时扩展一些单词将很有用
Robert Monfera
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.