为什么gnu make的"覆盖"不会传递给子制作?

ric*_*ici 2 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
Run Code Online (Sandbox Code Playgroud)

期望是export foo会导致make导出override声明中定义的值.但它没有:

$ make -s foo=original
outer: foo is replaced
inner: foo is original
Run Code Online (Sandbox Code Playgroud)

ric*_*ici 8

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

变量如何获得它们的价值

程序员可以通过三种方式设置变量:

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

以上列表是普通优先顺序; 在列表"wins"中找到的第一个定义.但是,您可以使用该override指令交换前两种方法的优先级.(您也可以使用该-e标志来交换最后两个方法的优先级-e.Posix需要该标志,但不建议使用它,并且它不会与之交互override.)

如何将变量传递给子品牌

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

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

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

解析度

因此,如果意图是在make本身和子make的环境中覆盖(或修改)命令行变量,则必须同时使用覆盖和显式修改MAKEFLAGS.(正如make手册中所解释的那样,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
Run Code Online (Sandbox Code Playgroud)

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

$ make -s foo=original
outer: foo is replaced
inner: foo is replaced
Run Code Online (Sandbox Code Playgroud)

现实应用:处理空白

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

override CFLAGS += -g
Run Code Online (Sandbox Code Playgroud)

通过附加到子制作需要一点点谨慎; 特别是,显而易见的:

MAKEOVERRIDES += CFLAGS=$(CFLAGS)  # Don't do this
Run Code Online (Sandbox Code Playgroud)

将无法正常工作,因为CFLAGS变量中的空格在添加时不会被转义MAKEFLAGS; 结果将是MAKEFLAGS这样的:

-- CFLAGS=-O3 CFLAGS=-O3 -g
Run Code Online (Sandbox Code Playgroud)

而不是期望的

-- CFLAGS=-O3 CFLAGS=-O3\ -g
Run Code Online (Sandbox Code Playgroud)

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

# Don't do this either
MAKEOVERRIDES += CFLAGS=$(subst $(space),\ ,$(CFLAGS))
Run Code Online (Sandbox Code Playgroud)

(定义和使用space基于gnu make手册中示例.)

但实际上更容易使用附加的赋值MAKEOVERRIDES,它没有文档但似乎有效.它也适用于命令行.

override         CFLAGS+=-g
MAKEOVERRIDES += CFLAGS+=-g
Run Code Online (Sandbox Code Playgroud)

重要说明make v4.1:一些测试显示,只有CFLAGS在命令行上实际设置(或某些其他变量)时,上述节才会起作用.我把这个bug报告为Savannah问题46013,在bug报告中有一个非常简单的修复.与此同时,如果您确实想要这样做,请使用以下解决方法:

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
Run Code Online (Sandbox Code Playgroud)