关于gnu make:GNU Makefile变量赋值=,?=,:=和+ =之间有什么区别?

What is the difference between the GNU Makefile variable assignments =, ?=, := and +=?

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

有什么区别:

1
2
3
4
 VARIABLE = value
 VARIABLE ?= value
 VARIABLE := value
 VARIABLE += value

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


懒惰集

1
VARIABLE = value

变量的正常设置-使用变量时,变量中的值将递归展开,而不是声明变量时

立即集

1
VARIABLE := value

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

如果没有设置

1
VARIABLE ?= value

仅当变量没有值时才设置变量

追加

1
VARIABLE += value

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


使用=会使变量被赋值。如果变量已经有一个值,它将被替换。此值将在使用时展开。例如:

1
2
3
4
5
6
7
8
9
10
HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

使用:=与使用=相似。但是,它不是在使用时展开的值,而是在分配期间展开的。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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=),我认为它仍然被认为是集合。否则,函数与=完全相同。

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

1
2
3
4
5
HELLO_WORLD = hello
HELLO_WORLD += world!

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

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


我建议你用"make"做一些实验。下面是一个简单的演示,演示了=:=之间的区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 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印刷品:

1
2
3
4
x - later
y - foo bar
a - later
b - later bar

在这里查看更详细的解释


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

1
2
3
4
5
VAL = foo
VARIABLE = $(VAL)
VAL = bar

# VARIABLE and VAL will both evaluate to"bar"

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

1
2
3
4
5
VAL = foo
VARIABLE := $(VAL)
VAL = bar

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

使用VARIABLE ?= val表示如果VARIABLE尚未设置,则只设置VARIABLE的值。如果尚未设置该值,则该值的设置将延迟到使用VARIABLE(如例1所示)。

VARIABLE += value只是把value附加到VARIABLE上。使用=:=确定value的实际值,与最初设置时的值相同。


在上述答案中,理解"在声明/使用时扩展值"的含义很重要。给出类似于*.c的值并不需要任何扩展。只有当这个字符串被一个命令使用时,它才可能触发一些全局化。同样,像$(wildcard *.c)$(shell ls *.c)这样的值不需要任何扩展,即使我们在变量定义中使用:=也会在定义时进行完全评估。

请在包含一些C文件的目录中尝试以下生成文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的值。