任何人都可以清楚地解释变量赋值在 Makefile 中是如何工作的。
和有什么区别:
VARIABLE = value
VARIABLE ?= value
VARIABLE := value
VARIABLE += value
我已经阅读了GNU Make 手册中的部分,但对我来说仍然没有意义。
VARIABLE = value
一个变量的正常设置,但在该value
字段中提到的任何其他变量都在使用该变量时使用它们的值递归扩展,而不是它在声明时的值
VARIABLE := value
通过内部值的简单扩展来设置变量 - 其中的值在声明时扩展。
VARIABLE ?= value
仅当变量没有值时才设置变量。value
总是在VARIABLE
被访问时评估。它相当于
ifeq ($(origin VARIABLE), undefined)
VARIABLE = value
endif
有关更多详细信息,请参阅文档。
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)
如果之前未分配变量,则使用?=
为变量分配一个值。如果变量之前被分配了一个空白值 ( ),我认为它仍然被认为是 set 。否则,功能与.VAR=
=
using+=
类似于 using =
,但不是替换值,而是将值附加到当前值,中间有一个空格。如果该变量以前是用 设置的,我认为:=
它会被扩展。结果值在使用时会扩展我认为。例如:
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)
make test
印刷:
x - later
y - foo bar
a - later
b - later bar
使用时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
只是附加value
到VARIABLE
. 的实际值value
是按照最初设置时的值确定的,使用=
或:=
。
在上述答案中,重要的是要了解“值在声明/使用时扩展”的含义。给出类似的值*.c
并不需要任何扩展。只有当命令使用此字符串时,它才会触发一些通配符。类似地,一个类似$(wildcard *.c)
or的值$(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)
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!"
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
catch : 如果FOO
在 makefile、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
或者
define VAR_NAME =
line
line
endef
赋值运算符可以省略,然后它创建一个递归扩展变量。
define VAR_NAME
line
line
endef
endef
删除之前的最后一个换行符。
HASH != printf '\043'
是相同的
HASH := $(shell printf '\043')
不要使用它。$(shell)
call 更具可读性,强烈建议不要在 makefile 中同时使用这两种方法。至少,$(shell)
遵循 Joel 的建议,让错误的代码看起来很明显是错误的。