注意:这个问题最初是由一个现已删除的用户发布的,但是这个咆哮背后有一个有效的问题;这是我尝试提供答案。
鉴于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
答案 0 :(得分:8)
期望可能是合理的,但事实证明这不是Gnu的工作方式。很可能是make
文档可以改进以澄清过程,但暗示似乎都在那里。
变量可以通过三种方式set by the programmer:
var=value
命令行参数以上列表是普通优先顺序;列表中的第一个定义"胜利"。但是,您可以使用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可以unexport
或override
在命令行上设置变量,但这只会影响环境中变量的值(或存在);它不会从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可以unexport
或override
在命令行上设置变量,但这只会影响环境中变量的值(或存在);它不会从MAKEFLAGS
删除或更改值。
你应该得到:
outer: foo is replaced
inner: foo is original
换句话说,我们应该得到sub-make,即在命令行(original
)上定义的值!
因为,你自己说:
makefile 无法
unexport
或override
在命令行上设置变量。
所以,在这里,当我们通过makefile赋予环境权限时,这意味着makefile具有更少的权力"在总体方案中。正确?
当然,对于这种情况,你的断言会更强。