为什么没有gnu make" s"覆盖"通过分制?

时间:2015-09-17 18:10:23

标签: makefile gnu-make

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

鉴于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

2 个答案:

答案 0 :(得分:8)

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

变量如何获得其值

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

  • 在命令行中使用var=value命令行参数
  • 明确在make文件中
  • 来自环境

以上列表是普通优先顺序;列表中的第一个定义"胜利"。但是,您可以使用override directive来交换前两种方法的优先级。 (您也可以使用-e标志来交换最后两个方法的优先级.Posix需要-e标志,但不建议使用它,它不会与{{override交互。 1}}。)

如何将变量传递给子

make与shell非常相似,因为环境用于传递变量值。如果变量被标记为导出,则其值将被放入环境中,用于由make启动的任何进程,包括sub-make。与shell一样,如果变量的定义来自环境,或者使用export指令明确标记为导出,则将变量标记为已导出。如果在命令行中设置了变量,也会导出变量。

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

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

分辨率

因此,如果意图是在make本身和子make的环境中覆盖(或修改)命令行变量,则必须同时使用覆盖和{{1}的显式修改}。 (正如make手册中所解释的,MAKEFLAGS实际上是使用MAKEFLAGS变量递归组合的,所以我们实际上修改了该变量。)

MAKEOVERRIDES

现在我们得到了预期的结果:

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

现实应用:处理空白

覆盖的主要目的是允许makefile将单词附加到命令行上可能提供的变量。 gnu make手册中提供的示例坚持$ make -s foo=original outer: foo is replaced inner: foo is replaced 始终包含CFLAGS标志,即使它已在-g命令行中指定:

make

将附加内容传递给子作品需要一点点谨慎;特别是,显而易见的:

override CFLAGS += -g

无法正常工作,因为MAKEOVERRIDES += CFLAGS=$(CFLAGS) # Don't do this 变量中的空格在添加到CFLAGS时不会被转义;结果是MAKEFLAGS看起来像这样:

MAKEFLAGS

而不是所需的

-- CFLAGS=-O3 CFLAGS=-O3 -g

如果在命令行上分配给-- CFLAGS=-O3 CFLAGS=-O3\ -g 的值包含空格,则空格将在CFLAGS中转义。使用的特定转义机制没有记录,Posix只要求有一些机制;显然,Gnu make使用反斜杠。可以手动反斜杠转义空格,从而得到如下内容:

MAKEFLAGS

# Don't do this either MAKEOVERRIDES += CFLAGS=$(subst $(space),\ ,$(CFLAGS)) 的定义和使用基于example in the gnu make manual。)

但实际上更容易在space中使用附加作业,该作业没有文档但似乎有效。它也适用于命令行。

MAKEOVERRIDES

关于make v4.1的重要说明:一些测试显示,只有在命令上实际设置了override CFLAGS+=-g MAKEOVERRIDES += CFLAGS+=-g (或其他一些变量)时,上述节才会起作用 - 线。我将此错误报告为Savannah issue 46013,并在错误报告中进行了非常简单的修复。与此同时,如果您确实想要这样做,请使用以下解决方法:

CFLAGS

更新2019年5月19日:今天我被告知上面提到的错误的修复已经提交,所以应该在下一个gmake版本中修复它。

答案 1 :(得分:0)

首先,我想指出,您添加到MAKEOVERRIDES的建议是 危险!

并且 不应该完成!!

您只需将递归变量转换为简单变量,如果递归扩展完成, 总是 会得到错误的结果。

我无法相信你是因为这个明显的错误而被投了票#34;建议"。

请注意:

  • 您甚至无法使用引用的作业进行修复,例如MAKEOVERRIDES += foo=$$(bar) !!!

但是,让我回到你职位的要点。

而且,我不能不同意这一点。

一个简单的例子是,如果你运行完全相同的makefile,你有:

这一个是从您的帖子中逐字复制的:

ifeq "$(MAKELEVEL)" "0"

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

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


在任何现代版本4.0及更高版本中运行:

# Sub-make does NOT get the value from the root-Make's command-line.
# Instead, it "inherits" the value from the root-Make's definition in the Makefile.
$ make -s foo=original -e
outer: foo is replaced
inner: foo is replaced

现在,鉴于你的断言:

  

但是,还有另一种机制可以将命令行上的变量传递给子进程:[MAKEFLAGS导出变量。] [3]。 MAKEFLAGS包含(大多数)命令行选项以及所有命令行变量覆盖。如果make在环境中找到MAKEFLAGS,它会将该变量中的设置与其命令行中实际指定的设置合并。这意味着make中的命令行变量设置也将在子make中占优先权。

     

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

你应该得到:

outer: foo is replaced
inner: foo is original

换句话说,我们应该得到sub-make,即在命令行(original)上定义的值!

因为,你自己说:

  

makefile 无法 unexportoverride在命令行上设置变量。

所以,在这里,当我们通过makefile赋予环境权限时,这意味着makefile具有更少的权力"在总体方案中。正确?

当然,对于这种情况,你的断言会更强。