bug-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Potential Bug: `.PHONY` targets and order-only prerequisites


From: Alejandro Colomar
Subject: Re: Potential Bug: `.PHONY` targets and order-only prerequisites
Date: Sat, 21 May 2022 19:06:09 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.9.0

Hi Paul and Jacob,

On 5/21/22 18:24, Paul Smith wrote:
On Wed, 2022-05-18 at 14:36 -0700, Jacob Kopczynski wrote:
The thing that the docs refer to as "impose order" is not a single
thing, but two. I would characterize a normal prerequisite as doing
three things rather than two:
- update-marking: cause a target to be marked out of date if the
prereq is marked out of date
- require-existence: require the prereq to be built successfully at
least once before the target is built
- imposed-order: require the prereq to be built before the target, if
both are being built

I guess I've just been using make for too long because I don't
understand the distinction you're trying to make between the last two.
There's no difference here, and the extra comment at the end "if both
are being built" is not meaningful (or anyway I don't understand what
it means).

I kind of get what he means.  Let me try to explain below.


Let's review how make works: it reads the makefile(s) and builds a
directed acyclic graph where every target is a node and every
prerequisite relationship is an edge between two nodes.

Then starting with each node representing a goal target (either the
first target in the makefile, or the target(s) given on the command
line) make performs a depth-first walk of the graph starting at that
node.  "Walking the graph" consists of processing each child node
recursively, in order, and once all children are complete make compares
the timestamp of each (not order-only) child node to the timestamp of
the current node.  If the current node is out of date, then it is
updated (by running its recipe), else we do nothing.  Then this node is
complete and we return to the processing of the parent node, or if
we're the goal target then we're done.

I think knowing so much how make(1) is implemented is hiding the intuitive idea of make(1) for someone not so familiar with it. By "once all children are complete" you are implying the "existence" of the children (which make(1) doesn't really check, but one can think of it as if it did).

Still, since make(1) doesn't really check for existence of files, I think the current term (order-only) is the correct one.

See some example that will probably show why existence is really never tested:


$ cat Makefile
a: b
        ls b

# Oh, I accidentally forgot to actually create b in its recipe
#
# To make(1), 'b' "exists", as it's recipe has succeeded.
#
# But if someone thinks of make as something that makes sure that
# 'b' exists before building 'a', this is for sure unintuitive.
# Can't blame them, as in the common case make(1) is used so that
# one can think of it as making sure the prerequisites exist.
b:
        touch a

$ make --silent
ls: cannot access 'b': No such file or directory
make: *** [Makefile:4: a] Error 2


Jacob, as you can see, make(1) doesn't fail here due to the non-existence of b. It simply assumes that it exists, but never really tests if it exists. It is ls(1) that is failing, as make(1) run it assuming that 'b' existed.

An implementation that checked that a non-.PHONY target really existed after running its recipe wouldn't seem insane to me; just a bit inefficient, and maybe in some corner case one would make good use of the behavior represented by my broken Makefile above, which wouldn't be possible if make(1) really checked for existence.

Cheers,

Alex



reply via email to

[Prev in Thread] Current Thread [Next in Thread]