evanb
evanb

Reputation: 180

Make should not rebuild deep dependencies

I have a build procedure roughly described by the following Makefile example:

a: b
    @echo "Build a, just using b.  Don't care about c."
    touch a

b: c
    @echo "Constructing b from c is cheap..."
    touch b
    @echo "Once accomplished, I no longer need c."

c:
    @echo "Constructing c is very expensive..."
    @echo "Work work work..."
    touch c

clean:
    $(RM) a b c

example: clean
    make a
    $(RM)   c
    make a

The point is: I need c to build b, but once I have b, I never again need c. When I do make example, make makes c, b, and a (as expected), deletes c, and then, in the last make a invocation, just remakes c (and does NOT re-make b and a, even though, I'd have thought they were stale now). But, since my goal is a and b hasn't changed, I don't want to remake c. Forget about it! Who cares! a should be considered up-to-date.

Another peculiar thing, is that when I

make a
rm c
make a

(rather than make example), in the second invocation make rebuilds everything (while in make example the second invocation just rebuilds c).

How do I prevent make from building c when its goal is a and all of a's immediate prerequisites exist and is fresher than they are (a isn't stale compared to b), even though the prerequisites of the prerequisites do not?

Edit: I think that what I may want is to treat every file as old (eg. with --old-file) unless that file doesn't exist.

Upvotes: 1

Views: 253

Answers (2)

Reinier Torenbeek
Reinier Torenbeek

Reputation: 17383

It looks like you want make to treat the file c as an intermediate file, a file that does not have any importance to you other than as an intermediate result when generating another file or other files. This concept is explained in section 10.4 Chains of Implicit Rules of the manual. Since your example does not use any implicit rules, you can manually mark your file c as .INTERMEDIATE.

This makefile shows c as an intermediate file.

a: b
        @echo "Build a, just using b.  Dont care about c."
        touch a

b: c
        @echo "Constructing b from c is cheap..."
        touch b
        @echo "Once accomplished, I no longer need c."

c: d
        @echo "Constructing c is very expensive..."
        @echo "Work work work..."
        touch c

.INTERMEDIATE: c
.PRECIOUS: c

I added a file d, based on your comment, although it is not needed for this example to work.

Before invoking make, the file d has to exist, it is the starting point of the chain. When invoking make, the following happens:

$ touch d
$ make
Constructing c is very expensive...
Work work work...
touch c
Constructing b from c is cheap...
touch b
Once accomplished, I no longer need c.
Build a, just using b.  Dont care about c.
touch a

Now deleting c will not have any impact on the build:

$ rm c
$ make
make: `a' is up to date.

Other than that, the update behavior based on dependencies is "the same as usual".

The .PRECIOUS target is optional. It is a built-in that instructs make not to delete the intermediate file named c. You can see for yourself what happens if you remove that line.

Upvotes: 3

John Marshall
John Marshall

Reputation: 7005

b might be built from c, but you don't want to tell Make that b depends on c — if b merely exists, then that's good enough. So you might write b's recipe as

b:
    $(MAKE) c
    @echo "Constructing b from c is cheap..."
    touch b
    @echo "Once accomplished, I no longer need c."

or if c is only used in making b, you could just fold the commands for making c into the recipe for b and not expose the existence of c to Make at all.

Maybe there are more elegant ways of expressing this, without invoking sub-makes. And if c has some prerequisites that would cause it to be rebuilt if they were updated, I guess they would need to be listed as reprequisites of b as well.

Upvotes: 0

Related Questions