Wrong expansion for the variable “$?” in makefile

旧巷老猫 提交于 2019-12-11 11:53:12

问题


From the docs:

$?

The names of all the prerequisites that are newer than the target, with spaces between them.



Now, given a makefile:

# Create target 'all', that is created later than 'old', which was created after 'older'.
$(shell touch older)
$(shell sleep 1)
$(shell touch old)
$(shell sleep 1)
$(shell touch all)

all : old phony;
    @echo '"$$?" is: "$?"'

# A command to turn the file 'old' even "older", by acquiring the modification-time of 'older'.
old ::
    cp -p 'older' 'old'


.PHONY: phony



Running, I get:

$ make
cp -p 'older' 'old'
"$?" is: "old phony"

# Trying again, but this time, with a *parallel-execution*.
$ make -j
cp -p 'older' 'old'
"$?" is: "phony"



*Can you see, that Make expand the value of $? differently for a parallel execution (-j), than it does for a non-parallel-execution?*

Let's first analyze the first case, i.e. for a non-parallel execution.

The modification-time of all and old are:

  • Before the build, old was "older" than all. This is self-evident. Right?
  • After Make finished building the prerequisite, i.e. after Make "builds" old, old got "older", so to speak, by "acquiring" the modification time of a file named: older. This is basically a net-result of the shell command: cp -p older old. Now, it is not hard to see, that older was way "older" than all.

Now, Make ended up building the target all, because of the other prerequisite, namely: phony.

But, according the documentation, $? should expands only to "the names of all the prerequisites that are newer than the target". This is an exact quote.

Could we agree, that old was never newer than all. And, if fact, had it not been for phony, Make would not even re-build all.

So, how wrong is it then, for Make, to expand $? to old phony.

phony I get. But old? Really?!



But, let's now turn our attention to parallel-execution, where Make expands $? as phony.

Here:

  1. Certainly, one can say, that this expansion is right (compared to the expansion in a non-parallel execution). The reason is, that only phony is the file that triggered the re-build of all. Without it, Make would not bother to re-build all, at-all.

  2. Still, one wonders, how can make expand the variable differently for a these two modes of execution. I can not even think, how this could be any relevant to the expansion of $?.

    In summary, it looks like that a "random" change (i.e. a modification in the mode of execution, from a parallel to a non-parallel) influences a seemingly unrelated entity, that is the expansion of automatic-variables. Why?


回答1:


Any file built or updated by make has been updated, so it will implicitly assume it to be newer, even if you hack in a command that makes it older.

Theoretically, Make should work on the contents of a file, not the date. If the contents are different, then you should run dependent builds. Given that we don't have filesystems providing easy content-derivates (hashes or such) it uses the last-modified-date to do this. As that's also not 100% reliable - see for example systems on which those dates don't have sufficient accuracy or may be just in the future - it'll also use this as an information source. If it just built it, it must be modified, therefore it's correct to consider it "newer" or "rebuilt".




回答2:


Having built old using the rules, Make now considers that to be the newest file in the dependency graph. It does not inspect old after making it.

This is obviously the right thing to do for phony targets, and it usually is The Right Thing for file targets, too.



来源:https://stackoverflow.com/questions/32114743/wrong-expansion-for-the-variable-in-makefile

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!