1

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.

evanb
  • 137
  • 6
  • I can't reproduce the behaviour you describe for `make example`: after deleting `c`, make rebuilds `c`, `b` and `a`. What version of make do you use? – Renaud Pacalet Dec 19 '18 at 17:06
  • GNU Make 3.81 (OS X) – evanb Dec 19 '18 at 17:16
  • 1
    It does seem that using `GNU Make 4.2.1` differs from 3.81. When I use 4.2.1 I the second `make a` in `make example` rebuilds everything. – evanb Dec 19 '18 at 17:20
  • Very interesting, I didn't know about this difference between 3.81 and more recent versions. – Renaud Pacalet Dec 19 '18 at 17:21
  • Although I have no proof that you are seeing this, note that there HAVE been reports of problems with the MacOS/Xcode-supplied version of GNU make 3.81 which do not appear in the original version of GNU make 3.81, even compiled for MacOS. And IIRC the failures were very odd. – MadScientist Dec 19 '18 at 19:51
  • OK. I can settle on 4.2.1. But that still doesn't solve my problem of deleting c, still having b, and not needing to update a. – evanb Dec 19 '18 at 21:58
  • For me, a vanilla (Brew'ed) GNU Make 4.2.1 on macOS rebuilt only `c`, just like the Apple-supplied 3.81. What I think you're seeing here is that the (older) Apple filesystem only tracks the timestamps with 1-second resolution, so the existing `b` and `a` have the same timestamp as the rebuilt `c`. With `sleep 1` added after all the `touch` commands in the Makefile, both Make versions rebuilt `c`, `b`, `a` as expected on macOS. – John Marshall Dec 20 '18 at 10:56
  • @JohnMarshall, very interesting---and you're right! Adding a bunch of `sleep`s in there prevents that unusual behavior. It still doesn't help me not rebuild a when c is gone, though! – evanb Dec 20 '18 at 13:23

2 Answers2

3

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.

Reinier Torenbeek
  • 16,669
  • 7
  • 46
  • 69
  • Interesting, I didn't know about `.INTERMEDIATE`. Can you explain the difference between `.INTERMEDIATE` and `.SECONDARY`? – evanb Dec 21 '18 at 13:04
  • I never use `.SECONDARY` but it looks like it does the same thing as `.INTERMEDIATE` AND it does not throw away your intermediate file. So it looks you could use that as well in stead of both `.INTERMEDIATE` and `.PRECIOUS`. To me, the names of the latter two make it more clear what they are intended to do, but that is a matter of preference. – Reinier Torenbeek Dec 21 '18 at 13:20
  • Thinking about it a bit more: it is not typical to have to mark a file as `.INTERMEDIATE` explicitly. Usually files are intermediate because of the implicit chaining as explained in section 10.4. The `.PRECIOUS` indication is common though. That is why I ended up using those two explicitly and why I was less familiar with `.SECONDARY`. Of course, if you do not *want* to keep `c` around in the first place, then just using `.INTERMEDIATE` is sufficient and `make` will delete `c` by itself. – Reinier Torenbeek Dec 21 '18 at 13:59
  • OK. So, if I don't want c to be automatically deleted I can mark it (`.INTERMEDIATE` and `.PRECIOUS`) or `.SECONDARY`, as these are equivalent? If they're equivalent, why have a `.SECONDARY` target? – evanb Dec 21 '18 at 16:35
  • The documentation of `.PRECIOUS` does indicate that there is an overlap with `.SECONDARY`. The difference seems to be the resulting behavior when make gets a fatal signal while a shell is executing, see [5.6 Interrupting or Killing make](https://www.gnu.org/software/make/manual/make.html#Interrupts). – Reinier Torenbeek Dec 21 '18 at 16:54
  • Also, you can not list the target pattern of an implicit rule (such as `%.o`) as a prerequisite file of `.SECONDARY`. See [Why .SECONDARY does not work with patterns (%) while .PRECIOUS does?](https://stackoverflow.com/questions/27090032/why-secondary-does-not-work-with-patterns-while-precious-does). Apparently getting rid of that difference has been a to-do for many years. – Reinier Torenbeek Dec 21 '18 at 16:57
0

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.

John Marshall
  • 6,815
  • 1
  • 28
  • 38
  • Unfortunately, in my real example I have multiple things that might depend on c, not just b. Once those things are there, c can be tossed. c also does have prerequisites which, if they change, c changes (and what depends on c changes). But if c is deleted and you've already got the things that depend on it, no problem. – evanb Dec 20 '18 at 13:21