Makefile 在make配方中,何时应该评估对*eval*的调用

Makefile 在make配方中,何时应该评估对*eval*的调用,makefile,gnu-make,Makefile,Gnu Make,我有一些软件项目是作为RPM分发的。它们的版本是使用我们附加了一个版本号的。使用常规约定,这是MAJOR.MINOR.PATCH-REL\u NUM。尽管超出了本文的范围,但发布号存储在git中。makefile中的发布目标如下所示: release: make clean $(BLD_ROOT)/tools/incr_rel_num # Although the third step, this was re-ordered to step 1 $(eval RELEA

我有一些软件项目是作为RPM分发的。它们的版本是使用我们附加了一个版本号的。使用常规约定,这是MAJOR.MINOR.PATCH-REL\u NUM。尽管超出了本文的范围,但发布号存储在git中。
makefile
中的发布目标如下所示:

release:
    make clean
    $(BLD_ROOT)/tools/incr_rel_num
# Although the third step, this was re-ordered to step 1
    $(eval RELEASE_NUMBER=$(shell cat $(BLD_ROOT)/path/to/rel_num.txt))
    make rpm RPM_RELEASE_NUM=$(RELEASE_NUMBER)
在调试过程中,我最终发现,尽管调用eval是配方中的第三步,但实际上它是在第一步进行评估的!这就是为什么RPM总是有一个版本号比我看到的版本号少的原因


我在谷歌上搜索了很多,但我没有找到任何可以解释在食谱中使用eval时评估顺序的点击。也许它甚至不是关于eval的,而是一般的函数。此外,我也没有在make的GNU手册中找到关于这方面的措辞(如果有,请指出哪一章)。我已经解决了这个问题,所以不麻烦,我只是想知道,这是预期的吗?如果是,为什么?

你是对的,有多个评估级别。在实际调用函数之前,第一次评估
eval
中的内容。如果希望在调用eval时对
eval
的内容进行评估,则必须将
$
符号放两次以转义,如下所示:

$(eval RELEASE_NUMBER=$$(shell $(BLD_ROOT)/path/to/rel_num.txt))
要在调用时查看
eval
中的实际内容,可以使用与
info
相同的语法,而不是
eval

$(info RELEASE_NUMBER=$$(shell $(BLD_ROOT)/path/to/rel_num.txt))
现在,我不确定评估得太早的零件,因此我加倍的
$
符号可能不是好的,但使用
info
函数将帮助您找到正确的命令

生成make sytax的片段,该片段成为已解析makefile的一部分。 makefile在执行任何配方之前以及在执行配方时被完全解析 执行所有make语句、make表达式和make变量 他离开了

<> P> >将代码< > $(EVAL)作为一个调用是没有意义的。 菜谱的每一行。它可能会生成make扩展中使用的值 但如果是这样,则在运行配方之前解析makefile时会发生这种情况

因此,在您的示例中,行:

$(eval RELEASE_NUMBER=$(shell $(BLD_ROOT)/path/to/rel_num.txt))
我认为应该是:

$(eval RELEASE_NUMBER=$(shell cat $(BLD_ROOT)/path/to/rel_num.txt))
在解析makefile时对其求值,假设它会导致 使变量
RELEASE\u NUMBER
获取值
1.0
,因为 解析makefile时,文件
$(BLD\u ROOT)/path/to/rel\u num.txt)
包含
1.0
。在这种情况下,您的食谱:

release:
    make clean
    $(BLD_ROOT)/tools/incr_rel_num
    $(eval RELEASE_NUMBER=$(shell cat $(BLD_ROOT)/path/to/rel_num.txt))
    make rpm RPM_RELEASE_NUM=$(RELEASE_NUMBER)
将决议如下:

release:
    make clean
    some_build_dir/tools/incr_rel_num
    make rpm RPM_RELEASE_NUM=1.0
make
运行配方时,您会发现它不会打印出 是“
$(eval RELEASE\u NUMBER=$(shell cat$(BLD\u ROOT)/path/to/rel\u num.txt))的“扩展”
, 因为食谱里没有这样的东西。这并不重要:

some_build_dir/tools/incr_rel_num
大概是一个命令,它在文件
some\u build\u dir/path/to/rel\u num.txt
中写入,例如
1.1
2.0
。 这一行动对菜谱没有任何影响。配方中没有执行任何操作 你可以改变食谱

$(eval…
与您的食谱无关。您想要实现的只是:

release:
    make clean
    $(BLD_ROOT)/tools/incr_rel_num
    RELEASE_NUMBER=$$(cat $(BLD_ROOT)/path/to/rel_num.txt) && \
    make rpm RPM_RELEASE_NUM=$$RELEASE_NUMBER
其中,
$
是您在生成文件中转义
$
的操作,在本例中, 当配方执行时,将其留给shell

此配方扩展为按顺序执行的3个shell命令:

$ make clean
$ some_build_dir/tools/incr_rel_num
$ RELEASE_NUMBER=$(cat some_build_dir/path/to/rel_num.txt) && \
make rpm RPM_RELEASE_NUM=$RELEASE_NUMBER
也可以进一步简化为:

release:
    make clean
    $(BLD_ROOT)/tools/incr_rel_num
    make rpm RPM_RELEASE_NUM=$$(cat $(BLD_ROOT)/path/to/rel_num.txt)

缺少的一点,上面没有人得到,很简单:当make要运行一个配方时,它首先扩展配方的所有行,然后再开始第一行。因此:

release:
        make clean
        $(BLD_ROOT)/tools/incr_rel_num
# Although the third step, this was re-ordered to step 1
        $(eval RELEASE_NUMBER=$(shell $(BLD_ROOT)/path/to/rel_num.txt))
        make rpm RPM_RELEASE_NUM=$(RELEASE_NUMBER)
当make决定运行
release
目标时,它首先展开配方中的所有行,这意味着展开
eval
,然后运行生成的行。这就是为什么你会有这样的行为

我真的不明白你为什么需要在这里使用
eval
;为什么不直接使用:

release:
        $(MAKE) clean
        $(BLD_ROOT)/tools/incr_rel_num
        $(MAKE) rpm RPM_RELEASE_NUM='$$(cat $(BLD_ROOT)/path/to/rel_num.txt))'

(顺便说一句,你不应该在makefile中使用bare
make
;你应该始终使用
$(make)
(或者
${make}
,同样的道理)。

如果你想在evalOne其他注释中计算变量,eval通常是危险的,因为它会打开安全漏洞。你的例子(如果您的
$(shell…
命令)中有
cat
,则会打开一个安全漏洞,黑客可以通过修改rel_num.txt来访问您的系统。我正要按照同样的思路回复,但我刚刚尝试过,如果您执行
$(eval X=Y)
在配方中,然后执行配方后对
$X
的所有引用扩展到
Y
(即使它们位于单独的目标内)。为了启动,它似乎设置了makefile变量
X
,而不是shell变量。但是,我很难找到对此行为的任何引用…我想我将就此提出一个新问题。感谢您的详细解释。我使用的是
$(eval…)
因为我在一篇stackoverflow文章中找到了一个答案,用于定义配方中的变量是
$(eval…)
.LOL,我不明白发生了什么。您的解决方案当然更简单、更简洁。您的陈述在配方运行之前解析makefile时发生这种情况是不正确的。
eval
与任何其他make函数或变量类似,并且在将其扩展为任何其他变量或函数时遵循相同的规则:I如果它出现在配方上下文中,当且仅当该配方被调用时,它才会被扩展。@AndrewFalanga Madscitect's