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

发布于 2024-07-11 18:25:52 字数 305 浏览 7 评论 0原文

任何人都可以清楚地解释变量分配在 Makefile 中的实际工作原理吗?

之间有什么区别:

 VARIABLE = value
 VARIABLE ?= value
 VARIABLE := value
 VARIABLE += value

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

Can anybody give a clear explanation of how variable assignment really works in Makefiles.

What is the difference between :

 VARIABLE = value
 VARIABLE ?= value
 VARIABLE := value
 VARIABLE += value

I have read the section in GNU Make's manual, but it still doesn't make sense to me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

落花随流水 2024-07-18 18:25:53

当您使用 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 附加到 VARIABLEvalue 的实际值是使用 =:= 最初设置时确定的。

When you use VARIABLE = value, if value is actually a reference to another variable, then the value is only determined when VARIABLE is used. This is best illustrated with an example:

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

# VARIABLE and VAL will both evaluate to "bar"

When you use VARIABLE := value, you get the value of value as it is now. For example:

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

# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"

Using VARIABLE ?= val means that you only set the value of VARIABLE if VARIABLE is not set already. If it's not set already, the setting of the value is deferred until VARIABLE is used (as in example 1).

VARIABLE += value just appends value to VARIABLE. The actual value of value is determined as it was when it was initially set, using either = or :=.

这样的小城市 2024-07-18 18:25:53

在上面的答案中,理解“值在声明/使用时扩展”的含义非常重要。 提供像 *.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。

In the above answers, it is important to understand what is meant by "values are expanded at declaration/use time". Giving a value like *.c does not entail any expansion. It is only when this string is used by a command that it will maybe trigger some globbing. Similarly, a value like $(wildcard *.c) or $(shell ls *.c) does not entail any expansion and is completely evaluated at definition time even if we used := in the variable definition.

Try the following Makefile in directory where you have some C files:

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

Running make will trigger a rule that creates an extra (empty) C file, called foo.c but none of the 6 variables has foo.c in its value.

何时共饮酒 2024-07-18 18:25:53

得票最多的答案可以改进。

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

递归扩展变量

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

foo = $(bar)

问题foo每次都会扩展为$(bar)的值 foo< /code> 被评估,可能会产生不同的值。 当然不能称之为“懒”! 如果在午夜执行,这可能会让您感到惊讶:

# 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 将扩展为空值。

Assign if not set

FOO ?= bar

相当于

ifeq ($(origin FOO), undefined)
FOO = bar
endif

仅当变量根本未设置时,

$(origin FOO) 等于 undefined问题:如果 FOO 设置为空字符串,无论是在 makefile、shell 环境还是命令行覆盖中,它将不会被赋值

追加

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 之前的最后一个换行符被删除。

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

 HASH != printf '\043'

相同

HASH := $(shell printf '\043')

与Don't use it 。 $(shell) 调用更具可读性,并且强烈建议不要在 makefile 中同时使用这两者。 至少,$(shell) 遵循了 Joel 的建议和 使错误的代码看起来明显错误

The most upvoted answer can be improved.

Let me refer to GNU Make manual "Setting variables" and "Flavors", and add some comments.

Recursively expanded variables

The value you specify is installed verbatim; if it contains references to other variables, these references are expanded whenever this variable is substituted (in the course of expanding some other string). When this happens, it is called recursive expansion.

foo = $(bar)

The catch: foo will be expanded to the value of $(bar) each time foo is evaluated, possibly resulting in different values. Surely you cannot call it "lazy"! This can surprise you if executed on midnight:

# 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!"

Simply expanded variable

VARIABLE := value
VARIABLE ::= value

Variables defined with ‘:=’ or ‘::=’ are simply expanded variables.

Simply expanded variables are defined by lines using ‘:=’ or ‘::=’ [...]. Both forms are equivalent in GNU make; however only the ‘::=’ form is described by the POSIX standard [...] 2012.

The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined.

Not much to add. It's evaluated immediately, including recursive expansion of, well, recursively expanded variables.

The catch: If VARIABLE refers to ANOTHER_VARIABLE:

VARIABLE := $(ANOTHER_VARIABLE)-yohoho

and ANOTHER_VARIABLE is not defined before this assignment, ANOTHER_VARIABLE will expand to an empty value.

Assign if not set

FOO ?= bar

is equivalent to

ifeq ($(origin FOO), undefined)
FOO = bar
endif

where $(origin FOO) equals to undefined only if the variable was not set at all.

The catch: if FOO was set to an empty string, either in makefiles, shell environment, or command line overrides, it will not be assigned bar.

Appending

VAR += bar

Appending:

When the variable in question has not been defined before, ‘+=’ acts just like normal ‘=’: it defines a recursively-expanded variable. However, when there is a previous definition, exactly what ‘+=’ does depends on what flavor of variable you defined originally.

So, this will print foo bar:

VAR = foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))

but this will print foo:

VAR := foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))

The catch is that += behaves differently depending on what type of variable VAR was assigned before.

Multiline values

The syntax to assign multiline value to a variable is:

define VAR_NAME :=
line
line
endef

or

define VAR_NAME =
line
line
endef

Assignment operator can be omitted, then it creates a recursively-expanded variable.

define VAR_NAME
line
line
endef

The last newline before endef is removed.

Bonus: the shell assignment operator ‘!=’

 HASH != printf '\043'

is the same as

HASH := $(shell printf '\043')

Don't use it. $(shell) call is more readable, and the usage of both in a makefiles is highly discouraged. At least, $(shell) follows Joel's advice and makes wrong code look obviously wrong.

眼眸里的那抹悲凉 2024-07-18 18:25:52

惰性设置

VARIABLE = value

变量的正常设置,但 value 字段中提到的任何其他变量都会使用该变量使用时的值进行递归扩展,而不是声明该变量时的值

。 Set

VARIABLE := value

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

如果不存在则延迟设置

VARIABLE ?= value

仅当变量没有值时才设置该变量。 当访问 VARIABLE 时,value 始终会被求值。 它相当于

ifeq ($(origin VARIABLE), undefined)
  VARIABLE = value
endif

有关更多详细信息,请参阅文档

Append

VARIABLE += value

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

Lazy Set

VARIABLE = value

Normal setting of a variable, but any other variables mentioned with the value field are recursively expanded with their value at the point at which the variable is used, not the one it had when it was declared

Immediate Set

VARIABLE := value

Setting of a variable with simple expansion of the values inside - values within it are expanded at declaration time.

Lazy Set If Absent

VARIABLE ?= value

Setting of a variable only if it doesn't have a value. value is always evaluated when VARIABLE is accessed. It is equivalent to

ifeq ($(origin VARIABLE), undefined)
  VARIABLE = value
endif

See the documentation for more details.

Append

VARIABLE += value

Appending the supplied value to the existing value (or setting to that value if the variable didn't exist)

笨笨の傻瓜 2024-07-18 18:25:52

使用 = 会为变量分配一个值。 如果变量已经有值,则将其替换。 该值在使用时会被扩展。 例如:

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=),则它仍然被视为已设置我认为。 否则,功能与 = 完全相同。

使用 += 与使用 = 类似,但不是替换值,而是将值附加到当前值,中间有一个空格。 如果变量之前使用 := 设置,那么它会被扩展我认为我认为使用时结果值会扩展。 例如:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

如果使用类似 HELLO_WORLD = $(HELLO_WORLD) world! 的内容,则会导致递归,这很可能会结束 Makefile 的执行。 如果使用 A := $(A) $(B),结果将与使用 += 不完全相同,因为 B使用 := 展开,而 += 不会导致 B 展开。

Using = causes the variable to be assigned a value. If the variable already had a value, it is replaced. This value will be expanded when it is used. For example:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

Using := is similar to using =. However, instead of the value being expanded when it is used, it is expanded during the assignment. For example:

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)

Using ?= assigns the variable a value iff the variable was not previously assigned. If the variable was previously assigned a blank value (VAR=), it is still considered set I think. Otherwise, functions exactly like =.

Using += is like using =, but instead of replacing the value, the value is appended to the current one, with a space in between. If the variable was previously set with :=, it is expanded I think. The resulting value is expanded when it is used I think. For example:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

If something like HELLO_WORLD = $(HELLO_WORLD) world! were used, recursion would result, which would most likely end the execution of your Makefile. If A := $(A) $(B) were used, the result would not be the exact same as using += because B is expanded with := whereas += would not cause B to be expanded.

长发绾君心 2024-07-18 18:25:52

我建议你使用“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

在此处查看更详细的解释

I suggest you do some experiments using "make". Here is a simple demo, showing the difference between = and :=.

/* 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 prints:

x - later
y - foo bar
a - later
b - later bar

Check more elaborate explanation here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文