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

mmo*_*ris 740 makefile gnu-make

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

有什么区别:

 VARIABLE = value
 VARIABLE ?= value
 VARIABLE := value
 VARIABLE += value
Run Code Online (Sandbox Code Playgroud)

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

Aln*_*tak 978

懒集

VARIABLE = value
Run Code Online (Sandbox Code Playgroud)

变量的正常设置 - 当使用变量时,不会在声明变量时递归地扩展变量中的值

立即设定

VARIABLE := value
Run Code Online (Sandbox Code Playgroud)

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

设置如果缺席

VARIABLE ?= value
Run Code Online (Sandbox Code Playgroud)

仅在变量没有值时设置变量

附加

VARIABLE += value
Run Code Online (Sandbox Code Playgroud)

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

  • A + = B是否扩展B?那就是如果我做A + = B,然后B + = C,A会评估$ {B}和$ {C}的连接吗? (23认同)
  • 正如手册的链接部分所说.+ =根据原始赋值所具有的简单或递归语义进行操作.所以是的,它将扩展RHS,但是它是立即还是以延迟方式实现,取决于LHS上变量的类型. (13认同)
  • 懒惰或立即"设置如果缺席"?我可以"懒惰设置,如果缺席"和"立即设置,如果abset"? (7认同)
  • 当你说变量值被扩展时,你是什么意思? (4认同)
  • @СашкоЛихенко 在这里查看扩展的含义 http://www.gnu.org/software/make/manual/make.html#Flavors (3认同)
  • 那么...用更简单的术语来引用`=`,而`:=`是按值的实际副本吗? (2认同)
  • @VictorSergienko我在解释中确实提到了“递归扩展”,而“惰性求值”是一个众所周知的术语。 (2认同)
  • 是的,“惰性求值”并不是 `make` 在这里所做的事情。当第一次访问某个值时,惰性求值会起作用一次:https://en.wikipedia.org/wiki/Lazy_evaluation。`make` 每次都会重新评估递归扩展的变量。 (2认同)
  • 这里值得一提 override 指令吗?如果用户使用命令行参数(或先前的覆盖)设置 VARIABLE,则这些分配都不应更改 VARIABLE,除非前面带有覆盖指令。示例:覆盖 VARIABLE += 值 (2认同)

str*_*ger 253

使用=会导致为变量赋值.如果变量已有值,则替换它.使用时,该值将被扩展.例如:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

# This echoes "hello world!"
echo $(HELLO_WORLD)
Run Code Online (Sandbox Code Playgroud)

使用:=类似于使用=.但是,在使用它时,不是在扩展值时,而是在赋值期间扩展它.例如:

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)
Run Code Online (Sandbox Code Playgroud)

使用?=分配一个值的变量当且仅当变量没有被先前分配.如果先前为变量分配了一个空值(VAR=),我认为它仍然被认为是集合.否则,功能完全一样=.

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

HELLO_WORLD = hello
HELLO_WORLD += world!

# This echoes "hello world!"
echo $(HELLO_WORLD)
Run Code Online (Sandbox Code Playgroud)

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

  • 因此,`VARIABLE = literal`和`VARIABLE:= literal`总是等价的.我做对了吗? (3认同)
  • @aiao,是的,因为文字对其用途是不变的 (2认同)

小智 57

我建议你用"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)
Run Code Online (Sandbox Code Playgroud)

make test 打印:

x - later
y - foo bar
a - later
b - later bar
Run Code Online (Sandbox Code Playgroud)

在这里查看更详细的解释

  • 最好在每个食谱前面使用一个"@"来避免这种令人困惑的重复结果. (4认同)
  • Make 不支持 `/* ... */` 块注释 (4认同)

mip*_*adi 31

当您使用时VARIABLE = value,if value实际上是对另一个变量的引用,那么该值仅在VARIABLE使用时确定.最好用一个例子来说明:

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

# VARIABLE and VAL will both evaluate to "bar"
Run Code Online (Sandbox Code Playgroud)

使用时VARIABLE := value,您可以获得value 现在的值.例如:

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

# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"
Run Code Online (Sandbox Code Playgroud)

使用VARIABLE ?= val意味着您只设置了VARIABLE if 的值VARIABLE.如果它尚未设置,则将使用该值的设置直到VARIABLE使用(如示例1中所示).

VARIABLE += value只是追加valueVARIABLE.实际值value是根据最初设置时的确定,使用=或者:=.


phs*_*phs 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
Run Code Online (Sandbox Code Playgroud)

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


Vic*_*nko 6

最高票的答案可以改进。

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

递归扩展变量

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

foo = $(bar)
Run Code Online (Sandbox Code Playgroud)

catch :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!"
Run Code Online (Sandbox Code Playgroud)

简单扩展变量

VARIABLE := value
VARIABLE ::= value
Run Code Online (Sandbox Code Playgroud)

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

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

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

要补充的不多。它立即被评估,包括递归扩展,以及递归扩展的变量。

捕获:如果VARIABLE指的是ANOTHER_VARIABLE

VARIABLE := $(ANOTHER_VARIABLE)-yohoho
Run Code Online (Sandbox Code Playgroud)

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

如果未设置则赋值

FOO ?= bar
Run Code Online (Sandbox Code Playgroud)

相当于

ifeq ($(origin FOO), undefined)
FOO = bar
endif
Run Code Online (Sandbox Code Playgroud)

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

catch:如果FOO被设置为空字符串,无论是在 makefiles、shell 环境还是命令行覆盖中,都不会被分配bar

追加

VAR += bar
Run Code Online (Sandbox Code Playgroud)

附加

当所讨论的变量之前没有定义过时,'+=' 的作用就像普通的 '=':它定义了一个递归扩展的变量。但是,当有先前的定义时, '+=' 的确切作用取决于您最初定义的变量的风格。

所以,这将打印foo bar

VAR = foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))
Run Code Online (Sandbox Code Playgroud)

但这会打印foo

VAR := foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))
Run Code Online (Sandbox Code Playgroud)

问题在于,+=根据VAR之前分配的变量类型,其行为会有所不同。

多行值

为变量分配多行值语法是:

define VAR_NAME :=
line
line
endef
Run Code Online (Sandbox Code Playgroud)

或者

define VAR_NAME =
line
line
endef
Run Code Online (Sandbox Code Playgroud)

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

define VAR_NAME
line
line
endef
Run Code Online (Sandbox Code Playgroud)

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

奖励:shell 赋值运算符 '!='

 HASH != printf '\043'
Run Code Online (Sandbox Code Playgroud)

是相同的

HASH := $(shell printf '\043')
Run Code Online (Sandbox Code Playgroud)

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