懒人集
VARIABLE = value
一个变量的正常设置,但在 value
字段中提到的任何其他变量都使用它们在变量使用点的值递归扩展,而不是它在声明时的值
立即设置
VARIABLE := value
通过内部值的简单扩展来设置变量 - 其中的值在声明时扩展。
不存在时的惰性设置
VARIABLE ?= value
仅当变量没有值时才设置变量。访问 VARIABLE
时始终评估 value
。它相当于
ifeq ($(origin VARIABLE), undefined)
VARIABLE = value
endif
有关详细信息,请参阅 documentation。
附加
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)
使用 ?=
为变量分配一个值 iff 该变量以前未分配。如果变量之前被分配了一个空白值 (VAR=
),它仍然被认为是 set我认为。否则,函数与 =
完全相同。
使用 +=
与使用 =
类似,但不是替换值,而是将值附加到当前值,中间有一个空格。如果变量之前使用 :=
设置,它会被扩展我认为。结果值在使用时会被扩展我认为。例如:
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
的结果总是等价的。我做对了吗?
我建议你用“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
@
以避免这种令人困惑的重复结果。
/* ... */
块评论
使用 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
的实际值与最初设置时一样。
在上述答案中,了解“值在声明/使用时扩展”的含义很重要。给出像 *.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
将触发创建额外(空)C 文件的规则,称为 foo.c
,但 6 个变量的值中没有一个具有 foo.c
。
最赞成的答案可以改进。
让我参考 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
。
问题:如果在 makefile、shell 环境或命令行覆盖中将 FOO
设置为空字符串,则将不会分配 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
的类型。
多行值
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)
调用更具可读性,强烈建议不要在生成文件中同时使用这两种方法。至少,$(shell)
遵循了 Joel 的建议和makes wrong code look obviously wrong。
CFLAGS
/CPPFLAGS
/LDFLAGS
)时,?=
和 =
是否等效?
=
会覆盖任何现有值。另请注意,命令行 override 变量(在命令行上 在 make
之后)会覆盖 Makefile
中的每个单独分配,但 overrides 除外。
=
和 :=
覆盖环境变量,环境变量覆盖 =?
,命令行“覆盖变量”覆盖两者,override
指令覆盖以上所有内容。这种与环境变量和命令行覆盖的交互可能是在您已经非常彻底的答案中非常有用的澄清。