能否给出一个清晰的解释,说明Makefile中变量赋值的工作原理。
冒号和双冒号有什么区别:
VARIABLE = value
VARIABLE ?= value
VARIABLE := value
VARIABLE += value
我已经阅读了GNU Make手册中的章节,但是对我来说仍然不太清楚。VARIABLE = value
这是一种常规的变量设置方式,但是如果在value
字段中提到了任何其他变量,则这些变量将递归地按照它们在使用该变量时的值展开,而不是它们在声明时的值。
VARIABLE := value
在简单展开变量值的情况下设置变量 - 在声明时展开其中的值。
VARIABLE ?= value
仅在变量没有值时设置变量。当访问VARIABLE
时,总是会计算value
的值。它相当于:
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)
使用 ?=
语法为变量赋值,仅当变量之前未被赋值时才会生效。如果变量之前已经被赋空值 (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
被扩展。
VARIABLE = literal
和VARIABLE := literal
始终是等效的。我理解得对吗? - aiao我建议你使用"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
/* ... */
块注释。 - yoonghm当你使用 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
被使用时(如示例1)。
VARIABLE += value
仅将value
附加到VARIABLE
上。实际的value
值是在最初使用=
或:=
时确定的。
*.c
这样的值并不包含任何展开。只有当此字符串被命令使用时,它才可能触发某些通配。类似地,像$(wildcard *.c)
或$(shell ls *.c)
这样的值并不包含任何展开,并且即使我们在变量定义中使用了:=
,它也会在定义时完全求值。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
。
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
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
追加:
当所涉及的变量之前未定义时,'+=' 的作用就像普通的 '=' 一样:它定义了一个递归展开的变量。然而,当存在先前的定义时,'+=' 的确切作用取决于您最初定义的变量类型。
因此,这将打印 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')
CFLAGS
/CPPFLAGS
/LDFLAGS
)覆盖时,?=
和=
是否等效? - shadowtalkermake
)会覆盖Makefile
中的每个赋值,除了覆盖。 - Victor Sergienko=
和:=
会覆盖环境变量,环境变量会覆盖=?
,命令行中的“override variables”会同时覆盖两者,而override
指令则会覆盖以上所有。环境变量和命令行覆盖的交互可能会在您已经非常详尽的答案中提供非常有用的澄清。 - shadowtalker