help-make
[Top][All Lists]
Advanced

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

Re: simple explanation for order-only prerequisite?


From: Kaz Kylheku (gmake)
Subject: Re: simple explanation for order-only prerequisite?
Date: Mon, 04 Nov 2019 19:51:36 -0800
User-agent: Roundcube Webmail/0.9.2

On 2019-11-03 06:50, Robert P. J. Day wrote:
was asked on friday by a make newbie to explain order-only
prerequisite (OOP), as he was reading the explanation in the manual
and was having trouble getting a fix on what it really meant.

When we specify that P is an order-only prerequisite for T,

   T : | P

most of the regular target-prerequisite relationship between
them continues to hold, except that an update/creation of
P doesn't require T to be updated.

This is true even if the update to P is triggered by the fact
that T itself needs P.

Basically it means "If rule T is in the dependency tree
then add P as a dependency of T, ensuring that it's
up-to-date, before T, without any side effect of invalidating an
up-to-date T."

It's not entirely explained with time stamps, because T or P
can be phony targets.

Consider this Makefile with P as a phony, and two real file
targets T0 and T1

.PHONY: P

all: T0 T1

T0 : P
        touch T0

T1 : P
        touch T1

Each time we run, both recipes are executed: both touches take
place.  P is a phony target, and everything that requires it
requires unconditional update.

Now suppose we make it this:

.PHONY: P

all: T0 T1

T0 : | P
        touch T0

T1 : P
        touch T1

If we run this when T0 and T1 do not exist, both recipes run.
Then every subsequent time the T1 recipe is run, but not T0.

The P target is always updated because "all" depends on it
through T0 and T1, and it is phony.

T0 doesn't run if that file exists, because the update of P
doesn't require it to due to order-only.

T1 runs because the update of P causes its own update.

Then if we make it:

all: T0 T1

T0 : | P
        touch T0

T1 : | P
        touch T1

so that both are order-only, we get a useful-looking behavior:

$ rm T0 T1
$ make
touch T0
touch T1
$ make
make: Nothing to be done for 'all'.
$ rm T0
$ make
touch T0
$ make
make: Nothing to be done for 'all'.
$ rm T1
$ make
touch T1
$ make
make: Nothing to be done for 'all'.

Our Makefile now behaves exactly like this one:

all: T0 T1

T0:
        touch T0

T1:
        touch T1

Except that both T0 and T1 depends on the phony target P also.

We can hang some useful recipe on that target which is is
always executed, and which is executed before T0 or T1 is
updated:

Without order-only prerequisites, we couldn't do that; if we
introduce the phony target P we get the behavior that T0 and
T1 are always updated:

.PHONY: P

all: T0 T1

T0 : | P
        touch T0

T1 : | P
        touch T1

P :
        echo P first



$ rm T0 T1
$ make
echo P first
P first
touch T0
touch T1
$ rm T0
$ make
echo P first
P first
touch T0
$ make
echo P first
P first

Because P is phony and in the dependency tree for "all",
the "echo P first" recipe always executes. If either of T0
or T1 rules, or both, also execute, they necessarily
execute afterward.

Semi-useful example: T0 and T1 need some output directory:

all: dir/T0 dir/T1

dir/T0 : | dir
        touch dir/T0

dir/T1 : | dir
        touch dir/T1

dir :
        mkdir -p dir

Why would we make a directory order-only? Because it's not a
real input to the build.

We don't want to update either dir/T0 or dir/T1 just because
the directory is newer than they are; the directory is irrelevant!
It just has to exist before either one is made/updated.

A directory's modification time stamp is bumped each time
an entry in that directory is created or deleted, so if
we make it a real prerequisite, it could cause spurious
rebuilds of the intermediates.

The above trick saves us from having to repeat a "mkdir -p"
into every recipe. However, the value is fairly low because
macros can be used to do that sort of thing.

dir/T1 : | dir
        $(call TOUCH, $@) # TOUCH includes mkdir -p action

Another example is if you're debugging parallel build (make
-j) race conditions, it can be useful as a band-aid for
imposing processing order.

Suppose that rules T0 and T0 have some hidden effect on shared
state, creating a seldom reproduced build-breaking race condition.
But, suppose that they are otherwise independent.

T0 : P01 P02 P03 ...
        # e.g. suppose a temp file is used here

T1 : P11 P12 P13 ...
        # ... and the same named temp file is used here! oops!

We can pull out the order-only hammer and serialize things:

T0 : P01 P02 P03 ... | T1

T1 : P11 P12 P13 ...

We thus have T0 done before T1, in a way that updating T1
doesn't cause an unnecessary rebuild of T0.

T0 now requires T1, so they can't be independently built
targets; if we build T0 we get T1 whether we like it or not.
That situation is often acceptable; internal intermediate
targets are often not independently built.




reply via email to

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