有人能清楚地解释一下变量赋值在Makefiles中的工作原理吗?
有什么区别:
VARIABLE = value
VARIABLE ?= value
VARIABLE := value
VARIABLE += value
我已经阅读了GNU Make手册中的章节,但我仍然没有理解。
有人能清楚地解释一下变量赋值在Makefiles中的工作原理吗?
有什么区别:
VARIABLE = value
VARIABLE ?= value
VARIABLE := value
VARIABLE += value
我已经阅读了GNU Make手册中的章节,但我仍然没有理解。
当前回答
在上面的回答中,理解“值在声明/使用时展开”是什么意思是很重要的。给出*.c这样的值并不需要进行任何展开。只有当命令使用这个字符串时,它才可能触发一些通配符。类似地,像$(通配符*.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。
其他回答
点赞最多的答案可以改进。
让我参考GNU Make手册“设置变量”和“口味”,并添加一些注释。
递归展开变量
您指定的值将逐字安装;如果它包含对其他变量的引用,那么每当这个变量被替换时,这些引用就会展开(在展开其他字符串的过程中)。当这种情况发生时,称为递归展开。
foo = $(bar)
问题在于:每次foo被求值时,foo的值都会被扩展为$(bar),这可能会导致不同的值。当然你不能称之为“懒惰”!如果在午夜执行,会让你大吃一惊:
# 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年)。
简单展开变量的值将被一次性扫描,在定义变量时展开对其他变量和函数的任何引用。
没有太多要加的,它是立即求值的,包括递归展开,递归展开变量。
捕获:如果变量引用ANOTHER_VARIABLE:
VARIABLE := $(ANOTHER_VARIABLE)-yohoho
并且在此赋值之前没有定义ANOTHER_VARIABLE,则ANOTHER_VARIABLE将展开为空值。
如果未设置则赋值
FOO ?= bar
等于
ifeq ($(origin FOO), undefined)
FOO = bar
endif
其中$(origin FOO)只有在变量根本没有设置时才等于undefined。
问题是:如果FOO被设置为空字符串,无论是在makefiles、shell环境中,还是在命令行重写中,它都不会被分配bar。
附加
VAR += bar
附加:
当讨论的变量之前没有定义时,' += '就像普通的' = '一样:它定义了一个递归扩展的变量。然而,当存在先前的定义时,' += '的确切作用取决于您最初定义的变量类型。
因此,这将打印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的类型。
多行值
为变量分配多行值的语法是:
define VAR_NAME :=
line
line
endef
or
define VAR_NAME =
line
line
endef
赋值运算符可以省略,然后它创建一个递归展开的变量。
define VAR_NAME
line
line
endef
删除edef之前的最后换行符。
额外的好处:shell赋值操作符` != '
HASH != printf '\043'
和
HASH := $(shell printf '\043')
不要用它。$(shell)调用更具可读性,在makefiles中使用这两者是非常不鼓励的。至少,$(shell)遵循了Joel的建议,使错误的代码看起来明显是错误的。
懒惰的设置
VARIABLE = value
一个变量的正常设置,但任何其他变量的值字段提到递归扩展其值在变量被使用的点,而不是它在声明时
立即设置
VARIABLE := value
通过简单地展开变量中的值来设置变量——变量中的值在声明时展开。
如果没有Lazy Set
VARIABLE ?= value
只有在变量没有值时才设置它。value总是在VARIABLE被访问时被计算。它等价于
ifeq ($(origin VARIABLE), undefined)
VARIABLE = value
endif
有关详细信息,请参阅文档。
附加
VARIABLE += value
将提供的值附加到现有值(如果变量不存在,则设置为该值)
当使用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的实际值被确定为最初设置时的值,使用=或:=。
使用=会给变量赋一个值。如果变量已经有值,则替换它。该值将在使用时展开。例如:
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=),我认为它仍然被认为是集合。否则,函数完全像=。
使用+=类似于使用=,但不是替换值,而是将值附加到当前值,中间有一个空格。如果变量之前设置为:=,我认为它被扩展了。我认为,当它被使用时,产生的值会被扩展。例如:
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做一些实验。下面是一个简单的演示,展示了=和:=之间的区别。
/* 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)
做测试打印:
x - later
y - foo bar
a - later
b - later bar
点击这里查看更详细的解释