【问题标题】:What is the difference between the GNU Makefile variable assignments =, ?=, := and +=?GNU Makefile 变量赋值 =、?=、:= 和 += 有什么区别?
【发布时间】:2010-10-01 17:24:09
【问题描述】:

任何人都可以清楚地解释变量赋值在 Makefile 中是如何工作的。

有什么区别:

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

我已经阅读了 GNU Make 手册中的section,但它对我来说仍然没有意义。

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    惰性集

    VARIABLE = value
    

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

    立即设置

    VARIABLE := value
    

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

    如果不存在则延迟设置

    VARIABLE ?= value
    

    仅当变量没有值时才设置它。 value 总是在访问 VARIABLE 时被评估。相当于

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

    有关详细信息,请参阅documentation

    追加

    VARIABLE += value
    

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

    【讨论】:

    • A += B 扩展 B 吗?也就是说,如果我执行 A += B,然后执行 B += C,A 会评估为 ${B} 和 ${C} 的串联吗?
    • 如手册链接部分所述。 += 根据原始分配所具有的任何简单或递归语义进行操作。所以是的,它会扩展 RHS,但它是立即执行还是以延迟方式执行取决于 LHS 上变量的类型。
    • 变量值被扩展是什么意思?
    • @СашкоЛихенко 看看这里以了解扩展的含义gnu.org/software/make/manual/make.html#Flavors
    • 是“如果不存在则设置”是懒惰的还是立即的?我可以“如果不存在则惰性设置”和“如果不存在则立即设置”?
    【解决方案2】:

    在使用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。使用=:= 来确定@​​987654335@ 的实际值。

    【讨论】:

    • 实际上,在您的第一个示例中,VARIABLE 是 $(VAL),VAL 是 bar。 VARIABLE 在使用时展开。
    • 是的,cmets 正在解释使用它们时会发生什么。
    • 啊;我猜你改正了,或者我把“评估”误读为“是”。
    【解决方案3】:

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

    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 = literalVARIABLE := literal 总是等价的。我说对了吗?
    • @aiao,是的,因为文字对其用途是不变的
    • 一个细微的区别是:- ?: 可以提高递归调用makefile 的性能。例如,如果 $? = $(shell some_command_that_runs_long_time)。在递归调用中,这只会被评估一次。导致构建性能的提高。 := 会更慢,因为该命令会不必要地运行多次
    【解决方案4】:

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

    【讨论】:

    • 最好在每个配方前使用@ 以避免这种令人困惑的重复结果。
    • Make 不支持/* ... */ 块评论
    【解决方案5】:

    在上述答案中,了解“值在声明/使用时扩展”的含义很重要。给出像*.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 的值。

    【讨论】:

    • 这是一个很棒的电话,有很多在声明时扩展的例子,用一个例子和一些词来扩展答案会很有用,以便在使用时扩展
    【解决方案6】:

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

    让我参考GNU Make手册"Setting variables""Flavors",并添加一些cmets。

    递归扩展变量

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

    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 仅在根本没有设置变量的情况下。

    问题:如果 FOO 在 makefile、shell 环境或命令行覆盖中设置为空字符串,则将分配@987654344 @。

    追加

    VAR += bar
    

    Appending:

    如果有问题的变量之前没有定义过,‘+=’就像普通的‘=’一样:它定义了一个递归扩展的变量。但是,如果有先前的定义,“+=”的确切作用取决于您最初定义的变量的风格。

    所以,这将打印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) 调用更具可读性,强烈建议不要在 makefile 中同时使用这两种方法。至少,$(shell) 遵循 Joel 的建议和makes wrong code look obviously wrong

    【讨论】:

    • 在定义打算被环境变量覆盖的宏时,?== 是否等效,例如 CFLAGS/CPPFLAGS/LDFLAGS?
    • @shadowtalker,没有。使变量默认为环境变量,但= 会覆盖任何现有值。另请注意,命令行 override 变量(在命令行上 after make)会覆盖 Makefile 中的每个分配,overrides 除外。跨度>
    • 这听起来正确吗? =:= 覆盖环境变量,环境变量覆盖 =?,命令行“覆盖变量”覆盖两者,override 指令覆盖以上所有内容。这种与环境变量和命令行覆盖的交互可能是您已经非常彻底的答案中非常有用的澄清。
    • 这是准确的。谢谢你。我认为这有点超出了问题的范围。另一方面,毫无疑问“Makefile 变量如何获取它们的值?”。也许值得提出这个问题。
    • 我采纳了你的建议:stackoverflow.com/a/68825174/2954547
    猜你喜欢
    • 1970-01-01
    • 2017-06-12
    • 2015-07-22
    • 2015-06-27
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多