Makefile 在make配方中,何时应该评估对*eval*的调用
我有一些软件项目是作为RPM分发的。它们的版本是使用我们附加了一个版本号的。使用常规约定,这是MAJOR.MINOR.PATCH-REL\u NUM。尽管超出了本文的范围,但发布号存储在git中。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
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