Makefile"include"的时间为自动生成文件的语句 [英] Timing of Makefile "include" statements for auto-generated files
问题描述
在此Makefile中...
In this Makefile...
all: piped.mk
ifeq ($(PIPED),1)
@echo Output of make is piped
else
@echo Output of make is NOT piped
endif
piped.mk:
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=$${PIPED}" > piped.mk
.PHONY: piped.mk all
include piped.mk
...我期望以下内容:
...I would expect the following:
- 第一个规则
all
说,它取决于文件piped.mk
. - 文件
piped.mk
是通过询问外壳程序终端的标准输出是否为TTY生成的 - 文件
piped.mk
在结尾处是include
d;因此,从理论上讲,这应该触发make
的重制"逻辑(请参见make
手册中的3.5节) - 为了强制创建
piped.mk
(,因为它不应该依赖文件,而是调用make
的方式),所以它也被标记为.PHONY目标-无论如何,都应该重制它.
- The first rule,
all
, says it depends on the filepiped.mk
. - The file
piped.mk
is generated by asking the shell whether the terminal's stdout is a TTY or not - The file
piped.mk
isinclude
d at the end; in theory therefore this should triggermake
s "remaking" logic (see section 3.5 in themake
manual) - To force the creation of
piped.mk
(since it is not supposed to depend on files, but on the waymake
was invoked), it is also marked as a .PHONY target - one that should be remade no matter what.
如果这些假设是正确的,我不明白这个结果:
If these assumptions are correct, I don't understand this result:
$ make
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is NOT piped
$ cat piped.mk
PIPED=0
$ make | more
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is NOT piped
$ cat piped.mk
PIPED=1
似乎文件piped.mk
总是按预期进行了重新制作-并且确实包含了预期的信息(是否通过标准输出输出了).但是,Makefile似乎忽略了PIPED的值-它总是报告"NOT Piped";好像piped.mk
在重新生成后未重新包含...
It appears that file piped.mk
is always remade, as intended - and it indeed contains the intended information (whether the standard output is piped or not). The Makefile however, seems to ignore the value of PIPED - it always reports "NOT piped"; as if piped.mk
is not re-included after its regeneration...
更新
传统上,自动生成的Makefile
部分的典型示例是C文件依赖项的生成-在这种情况下,我看到的所有示例最终都使用Makefile底部的include
.但是,也许这种方法仅适用于生成依赖项,而不适用于Makefile变量的分配...
Traditionally, the prototypical example of automatically generated Makefile
parts is the generation of the C file dependencies - in which case all examples I've seen end up using include
at the bottom of the Makefile. However, maybe this approach only works for the generation of dependencies, and does not work for the assignment of Makefile variables...
要检查是否是原因,我尝试将include
向上移动到顶部...
To check if this was the reason, I attempted to move the include
up to the top...
$ cat Makefile
include piped.mk
all: ...
...导致令人讨厌的延迟"效果:
...which caused a nasty "delay" effect:
$ rm -f piped.mk
$ make | more
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is NOT piped
$ make | more
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is piped
基本上,会发生包含 -但它是在piped.mk
生成之前发生的.因此,它使用PIPED定义的旧"值,而不是执行时仅放置在piped.mk
内部的新值.也许只允许重新包含piped.mk
才能更新依赖项规则,而不能更新变量定义吗?
Basically, the inclusion does happen - but it happens BEFORE the piped.mk
generation. It therefore uses the "old" value of the definition of PIPED, not the new one that was just placed inside piped.mk
upon execution. Maybe the re-inclusion of piped.mk
is only allowed to update dependency rules and not variable definitions?
所以,我的问题:
是否可以自动生成包含变量定义的Makefile的一部分,并在同一Makefile调用中将其包括在内?
Is it possible to auto-generate a part of a Makefile that contains variable definitions, and include it within the same Makefile invocation?
也就是说,没有单独的$(MAKE)
调用-这是我到目前为止找到的唯一使之起作用的方法:
That is, without a separate $(MAKE)
invocation - which is the only way I have found so far to make it work:
all:
@[ -t 1 ] && PIPED=0 || PIPED=1 ; \
$(MAKE) binary "PIPED=$${PIPED}"
binary:
ifeq ($(PIPED),1)
@echo Output of make is piped
else
@echo Output of make is NOT piped
endif
.PHONY: all binary
谢谢您的建议.
推荐答案
首先,非常感谢@Beta的反馈.
First of all, many thanks to @Beta for his feedback.
@Beta建议使用$(shell ...)
构造执行[-t 1]
检查;但不幸的是,在这种情况下,这无法正常工作.目的是检测Makefile
是否在管道执行中得到处理...
@Beta recommended using the $(shell ...)
construct to execute the [-t 1]
check; but unfortunately this can't work in this case. The intent is to detect whether the Makefile
is processed in a piped execution...
$ make | less # page/search interactively in the build log
$ make | grep ... # Bulk search for stuff in the build log
$ make > log.txt # Or use the log.txt from within e.g. a LaTEX doc...
...还是不.想想避免在输出中打印颜色代码(这会弄乱在LaTEX文档中按原样重新使用输出,或者使您的寻呼机,grep等混乱).这个构造...
...or not. Think e.g. of avoiding the printing of color codes in the output (which would mess up re-using the output as-is from within a LaTEX document, or confuse your pager, grep, etc). This construct...
PIPED:=$(shell [ -t 1 ] ... )
...当嵌入在Makefile中时不起作用;因为stdout已经在make
内部重定向,以捕获输出并将其分配给变量.
...when embedded in the Makefile doesn't work; because stdout is already redirected inside make
, to capture the output and assign it to a variable.
@Beta另外提出了一个建议,这是解决此问题的关键-这与我对.PHONY
的使用有关:
@Beta additionally gave another suggestion, which was key to solving this - it was about my use of .PHONY
:
...如果该文件为PHONY,则重建包含的文件不会触发重新调用.
...rebuilding an included file doesn't trigger reinvocation if that file is PHONY.
的确,这解释了为什么我会看到重新执行创建piped.mk
的规则,但是不会重新包含该文件的原因.因此PIPED
变量保持不变.
Indeed, this explained why I would see the re-execution of the rule that created piped.mk
, but the file would not be re-included; and therefore the PIPED
variable stayed the same.
为避免这种情况,我完全删除了.PHONY规范,并在all
规则的末尾添加了一个操作,以删除piped.mk
(因此强制下次重新创建make
被调用):
To avoid that, I removed the .PHONY spec altogether, and added an action at the end of the all
rule, to remove the piped.mk
(thus forcing it to be re-created next time make
is invoked):
all: piped.mk
ifeq ($(PIPED),1)
@echo Output of make is piped because PIPED is ${PIPED}
else
@echo Output of make is NOT piped because PIPED is ${PIPED}
endif
@rm -f piped.mk
piped.mk:
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=$${PIPED}" > piped.mk
-include piped.mk
使用此方法,我看到了以下结果:
Using this, I saw the following result:
$ make
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is NOT piped because PIPED is 0
好.对于管道...
$ make | more
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is NOT piped because PIPED is 1
糟糕!显然,重新包含了piped.mk
,并且重新评估了PIPED
(因为该操作报告了because PIPED is 1
)-但令人惊奇的是,执行的规则是ifeq
中的错误规则!
Ooops! Clearly piped.mk
was re-included, and PIPED
was re-evaluated (since the action reported because PIPED is 1
) - but the rule that was executed, amazingly, was the wrong one in the ifeq
!
ifeq ($(PIPED),1)
@echo Output of make is piped because PIPED is ${PIPED}
else
@echo Output of make is NOT piped because PIPED is ${PIPED}
endif
注意:我严重怀疑那里的任何人都期望包含这5行的Makefile能够打印Output of make is NOT piped because PIPED is 1
.实际上,我认为这是make
中的错误.
Note: I seriously doubt anyone out there would expect that a Makefile with these 5 lines could ever print Output of make is NOT piped because PIPED is 1
. In fact, I'd consider this to be a bug in make
.
我的最终想法是,也许make
不会重新评估在重新包含之前的检查条件-我将include
移到了顶部:
My final idea was that maybe make
DOES NOT re-evaluate the check conditions that are BEFORE the point where the re-inclusion took place - and I moved the include
on top:
-include piped.mk
all: piped.mk
ifeq ($(PIPED),1)
@echo Output of make is piped because PIPED is ${PIPED}
else
@echo Output of make is NOT piped because PIPED is ${PIPED}
endif
@rm -f piped.mk
piped.mk:
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=$${PIPED}" > piped.mk
这终于奏效了:
$ make
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is NOT piped because PIPED is 0
$ make | more
[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=${PIPED}" > piped.mk
Output of make is piped because PIPED is 1
因此,总结一下:
- 切勿将包含的文件放在
.PHONY
规则中-如果您希望每次都对它们进行重新评估,只需确保在处理结束时将其删除即可. - 如果生成的文件包含依赖项(如
make
手册中描述的".d"文件),则可以在末尾将它们包括在内.但是,如果它们包含变量分配,请确保在使用/检查所述变量之前将其包括在内.
- Never place included files in
.PHONY
rules - if you want them re-evaluated every time, just make sure to remove them in the end of your processing. - If your generated files contain dependencies - like the '.d' files described in the
make
manual - it is OK to include them at the end. If, however, they contain variable assignments, make sure to include them BEFORE you use/check said variables.
再次感谢@Beta向我指出正确的方向.
Again, many thanks to @Beta for pointing me in the right direction.
更新:
几个月后,我意识到,由于重新评估了该变量(将其从.PHONY中删除后),我还可以避免使用ifeq
,而是在shell本身中进行决定:
And a few months later, I realized that since the variable was re-evaluated (after removing it from the .PHONY), I could also avoid using ifeq
and instead decide in the shell itself:
all:
@[ ${PIPED} -eq 1 ] && echo Piped. || echo Not piped.
@rm -f piped.mk
piped.mk:
@[ -t 1 ] && PIPED=0 || PIPED=1 ; echo "PIPED=$${PIPED}" > piped.mk
include piped.mk
这篇关于Makefile"include"的时间为自动生成文件的语句的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!