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

时间:2009-01-15 23:19:02

标签: makefile gnu-make

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

有什么区别:

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

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

7 个答案:

答案 0 :(得分:933)

懒人套装

VARIABLE = value

变量的正常设置 - 当使用变量时,递归扩展其中的值,而不是在声明它时

立即设置

VARIABLE := value

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

设置如果缺席

VARIABLE ?= value

仅当变量没有值

时才设置变量

追加

VARIABLE += value

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

答案 1 :(得分:238)

使用 = 会为变量分配值。如果变量已有值,则替换它。使用时,该值将被扩展。例如:

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)

使用 ?= 为变量指定值 iff 以前未分配变量。如果先前为变量指定了空值(VAR=),则仍将其视为我认为。否则,函数与=完全相同。

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

HELLO_WORLD = hello
HELLO_WORLD += world!

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

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

答案 2 :(得分:52)

我建议你使用“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

Check more elaborate explanation here

答案 3 :(得分: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,则只设置VARIABLE 的值。如果尚未设置,则推迟使用值设置,直到使用VARIABLE为止(如示例1所示)。

VARIABLE += value只需将value追加到VARIABLE即可。 value的实际值是根据最初设置的时间确定的,使用=:=

答案 4 :(得分:6)

在上面的答案中,理解的含义非常重要"值在声明/使用时间扩展"。给出像*.c这样的值并不需要任何扩展。只有当命令使用此字符串时,它才会触发某些通配。同样,$(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将触发一条规则,该规则会创建一个名为foo.c的额外(空)C文件,但6个变量中没有一个变量的值为foo.c

答案 5 :(得分:1)

最受好评的答案可以得到改善。

让我参考GNU Make手册"Setting variables""Flavors",并添加一些评论。

递归扩展变量

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

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: something
    test -f $(WHEN).flag || echo "Boo!"

简单扩展的变量

VARIABLE := value
VARIABLE ::= value

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

使用':='或':: ='[...]由行定义简单扩展的变量。两种形式在GNU make中是等效的;但是,POSIX标准[...] 2012仅描述“ :: =”形式。

定义变量后,将一次扫描简单扩展的变量的值,从而扩展对其他变量和函数的引用。

要添加的内容不多。立即对其进行评估,包括递归扩展变量以及递归扩展变量。

收获:如果VARIABLE指向ANOTHER_VARIABLE

VARIABLE := $(ANOTHER_VARIABLE)-yohoho

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

如果未设置则分配

FOO ?= bar

等同于

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

其中$(origin FOO)等于undefined,只有在根本没有设置变量的情况下。

捕获:如果FOO设置为空字符串,无论是在makefile,shell环境还是命令行替代中,都将分配bar

附加

VAR += bar

Appending

如果之前未定义相关变量,则'+ ='的作用与普通的'='相同:它定义了一个递归扩展的变量。但是,当有先前的定义时,“ + =”的作用取决于您最初定义的变量的样式。

因此,这将打印foo bar

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

但这会打印foo

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

要抓住的地方是,+=的行为取决于之前分配的变量VAR的类型。

多行值

syntax to assign multiline value to a variable是:

define VAR_NAME :=
line
line
endef

define VAR_NAME =
line
line
endef

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

define VAR_NAME
line
line
endef

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

奖金:shell赋值运算符‘!=’

 HASH != printf '\043'

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

请勿使用。 $(shell)调用更具可读性,强烈建议不要在makefile中使用二者。至少$(shell)遵循Joel的建议和makes wrong code look obviously wrong

答案 6 :(得分:0)

这是我的问题:

ULB=/usr/local/bin/
PGM1=myprogram
Install:
    sudo /usr/bin/cp ${PGM1} ${ULB} #works just fine, 
 # I need to do the following  which does not work (extra spaces and 
  #     catenation of ${ULB}{PGM1}   does not work.
    sudo chown root:root  ${ULB}${PGM1}   
  #     I need as a conclusion
  sudo chown root:root /usr/local/bin/myprogram

I tried make's join, strip, 

make 在不需要的地方添加空格字符,并且 make 可能不会连接程序和目录字段。