注意:这个问题最初是由现在删除的用户发布为rant的,但是在rant后面有一个有效的问题;这是我提供答案的尝试。

给定Makefile:

ifeq "$(MAKELEVEL)" "0"

# Override the command-line specification of "foo".
override foo=replaced
export foo

all::
    @echo outer: foo is "$(foo)"
    @$(MAKE)

else

# Variable 'foo' was "exported" from the top-level Makefile.
all::
    @echo inner: foo is "$(foo)"

endif

期望export foo将导致make导出override声明中定义的值。但事实并非如此:

$ make -s foo=original
outer: foo is replaced
inner: foo is original

最佳答案

期望可能是合理的,但事实证明,这不是Gnu的工作方式。很有可能可以改进make文档以澄清该过程,但是所有提示似乎都在那里。

变量如何获取其值

变量可以通过三种方式为set by the programmer:

  • 在命令行上使用var=value命令行参数
  • 明确显示在生成文件
  • 来自环境

  • 上面的列表是正常的优先顺序;在列表“wins”中找到的第一个定义。但是,您可以使用 override directive交换前两种方法的优先级。 (您还可以使用-e标志来交换后两种方法的优先级。Posix要求-e标志,但不鼓励使用它,并且它不会与override交互。)

    变量如何传递给子品牌
    make与shell非常相似,因为环境用于传递变量值。如果将变量标记为已导出,则将其值放入由make发起的任何过程(包括子make)的环境中。与 shell 程序一样,如果变量的定义来自环境,或者使用export指令将其明确标记为已导出,则将其标记为已导出。如果变量是在命令行上设置的,则也会导出。

    但是,还有另一种将命令行上的变量传递给子进程的机制: MAKEFLAGS exported variable.MAKEFLAGS包含(大多数)命令行选项以及所有命令行变量替代。如果make在环境中找到MAKEFLAGS,它将合并该变量中的设置与其在命令行中实际指定的设置。这意味着make中的命令行变量设置也将在子make中具有优先级。

    由于命令行变量设置是通过MAKEFLAGS变量传递的,因此它们不会在makefile中进行任何更改。 Makefile可以unexportoverride在命令行上设置的变量,但这只会影响环境中变量的值(或存在)。它不会删除或更改MAKEFLAGS中的值。

    解析度

    因此,如果要在make本身和子make环境中重写(或修改)命令行变量,则必须同时使用MAKEFLAGSMAKEFLAGS进行显式修改。 (如make手册中所述,MAKEOVERRIDES实际上是使用CFLAGS变量递归组成的,因此我们实际上是对该变量进行了修改。)

    ifeq "$(MAKELEVEL)" "0"
    
    # Override the command-line specification of "foo".
    override foo=replaced
    MAKEOVERRIDES += foo=replaced
    
    all::
        @echo outer: foo is "$(foo)"
        @$(MAKE) -s
    
    else
    
    # Variable 'foo' was "exported" from the top-level Makefile.
    all::
        @echo inner: foo is "$(foo)"
    
    endif
    

    现在我们得到了预期的结果:
    $ make -s foo=original
    outer: foo is replaced
    inner: foo is replaced
    

    现实应用:处理空白

    覆盖的主要目的是允许makefile将单词附加到命令行上可能提供的变量中。 gnu make手册中提供的示例坚持认为,即使在-g命令行上指定了make标志,它也始终包含CFLAGS标志:
    override CFLAGS += -g
    

    将附件传递到子模型时需要谨慎一些。特别是显而易见的是:
    MAKEOVERRIDES += CFLAGS=$(CFLAGS)  # Don't do this
    

    将不起作用,因为将MAKEFLAGS变量中的空格添加到MAKEFLAGS时不会逃脱;结果将是CFLAGS看起来像这样:
    -- CFLAGS=-O3 CFLAGS=-O3 -g
    

    而不是期望的
    -- CFLAGS=-O3 CFLAGS=-O3\ -g
    

    如果在命令行上分配给MAKEFLAGS的值包含空格,则在space中转义该空格。没有记录使用的特殊转义机制,Posix仅要求有某种机制。显然,Gnu make使用反斜杠。可以手动反斜杠转义空白,结果是这样的:
    # Don't do this either
    MAKEOVERRIDES += CFLAGS=$(subst $(space),\ ,$(CFLAGS))
    

    (MAKEOVERRIDES的定义和使用基于example in the gnu make manual。)

    但是,实际上,仅在CFLAGS中使用附加分配会更容易,该记录未记录但似乎可以使用。它也可以在命令行上使用。
    override         CFLAGS+=-g
    MAKEOVERRIDES += CFLAGS+=-g
    

    从make v4.1开始的重要说明:一点测试表明,仅当在命令行上实际设置了ojit_code(或其他变量)时,以上节才会起作用。我将此错误报告为Savannah issue 46013,并在错误报告中进行了非常简单的修复。同时,如果您确实要执行此操作,请使用以下解决方法:
    override         CFLAGS+=-g
    MAKEOVERRIDES += CFLAGS+=-g
    
    # This line is necessary in case there were no command-line overrides.
    # In effect, it produces a command-line override, although that value
    # will not be passed on to sub-makes.
    MAKEFLAGS     += dummy=dummy
    

    更新2019年5月19日:今天,我得知已经提交了针对上述错误的修复程序,因此应在下一个gmake版本中对其进行修复。

    09-08 01:00