30

I have the following rules

define compile_c
$(ECHO) "CC $<"
$(Q)$(CC) $(CFLAGS) -c -MD -o $@ $<
@# The following fixes the dependency file.
@# See http://make.paulandlesley.org/autodep.html for details.
@# Regex adjusted from the above to play better with Windows paths, etc.
@$(CP) $(@:.o=.d) $(@:.o=.P); \
  $(SED) -e 's/#.*//' -e 's/^.*:  *//' -e 's/ *\\$$//' \
      -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
  $(RM) -f $(@:.o=.d)
endef

vpath %.c . $(TOP)
$(BUILD)/%.o: %.c $(BUILD)/%.pp
    $(call compile_c)

vpath %.c . $(TOP)

$(BUILD)/%.pp: %.c
    $(ECHO) "PreProcess $<"
    $(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<

When the build finishes, GNU make says
Removing intermediate files... and deletes all the .pp files which I do NOT want.

Why is it doing this?
How do I stop it?

Bob
  • 4,576
  • 7
  • 39
  • 107
  • 2
    The URL you're using for the dependency generation is outdated and may go away at some point. You should use the correct URL: http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ – MadScientist Nov 23 '17 at 05:13

4 Answers4

21

Since you're using GNU Make, you can make the following adjustment to your Makefile:

.PRECIOUS: $(BUILD)/%.pp  # ADD THIS LINE
$(BUILD)/%.pp: %.c
    $(ECHO) "PreProcess $<"
    $(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<

The documentation has this to say about .PRECIOUS directives:

The targets which .PRECIOUS depends on are given the following special treatment: if make is killed or interrupted during the execution of their recipes, the target is not deleted.

[...]

Also, if the target is an intermediate file, it will not be deleted after it is no longer needed, as is normally done.

[...]

You can also list the target pattern of an implicit rule (such as ‘%.o’) as a prerequisite file of the special target .PRECIOUS to preserve intermediate files created by rules whose target patterns match that file's name.

This has the benefit of not creating an unwanted additional rule. It's also clearer what you're trying to do: keep the precious intermediate files that might be expensive to recreate.

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • 9
    Personally I would avoid `.PRECIOUS` unless it's _really_ what you want. It should certainly not be used as a casual replacement for declaring a target as in the answer above. Consider very carefully what the docs say: if make is killed or interrupted, the target is not deleted. What if you're compiling a large file and use `^C`? This interrupts make _and also interrupts your compiler_. Some compilers will clean up a half-created target, but others will not. Since the half-created target has a newer timestamp make won't recreate it. This can be a hard problem to find. – MadScientist May 14 '20 at 21:21
  • .SECONDARY is better than .PRECIOUS! – Nick Huang Dec 01 '21 at 13:40
  • 3
    I'm leaving this up for posterity, but in the intervening years I've come to agree that `.SECONDARY` should be reached for sooner than `.PRECIOUS`. `.PRECIOUS` still has its place when, for instance, you are actively debugging something and _would_ like to keep a partial output on failure. – Alex Reinking Sep 14 '22 at 16:30
17

I think the best solution is to use the .SECONDARY special target. Just add this line:

.SECONDARY:

Quoting the manual:

.SECONDARY with no prerequisites causes all targets to be treated as secondary (i.e., no target is removed because it is considered intermediate).

Why is this better than making the targets prerequisites of a throw-away target? That's more clutter, and has to be done explicitly for every set of files that might be generated with pattern rules.

Why is this better than .PRECIOUS? That causes files to be retained even if their recipe fails when using .DELETE_ON_ERROR. The latter is important to avoid failing recipes leaving behind bad outputs that are then treated as current by subsequent make invocations. IMO, you always want .DELETE_ON_ERROR, but .PRECIOUS breaks it.

Scott McPeak
  • 8,803
  • 2
  • 40
  • 79
10

If you search for "gnu make intermediate files" you'll immediately find the answer as to why it's happening, in the GNU make manual section Chains of Implicit Rules.

It also tells you how to avoid it: a file cannot be intermediate if it is mentioned in the makefile as a target or prerequisite.

So, just list your .pp files as a prerequisite of some rule, somewhere. It doesn't have to be a rule that's ever invoked. You don't give enough of your makefile here for us to provide a complete answer, but it would be something like:

all_pps: $(ALL_OBJECTS:.o=.pp)

assuming you had a variable ALL_OBJECTS containing all your .o files.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • 8
    If I search for "gnu make intermediate files". I find this post. The post itself guarantees its own success on Google! – Drew Dec 08 '20 at 22:27
  • all_pps: $(ALL_OBJECTS:.o=.pp) doesn't work because only the last .pp file is considered! – Nick Huang Dec 01 '21 at 13:39
  • Maybe I don't understand that comment @NickHuang, but as far as I do understand it it's not correct. – MadScientist Dec 01 '21 at 14:52
  • What about using `-save-temps`? Wouldn’t that cut in half the number of times the compiler is invoked? – Bob Jun 09 '23 at 19:59
  • I'm not sure what `-save-temps` (I presume this is a compiler flag? It's definitely not a make flags) has to do with either the question or this answer...? – MadScientist Jun 09 '23 at 21:08
2

Here is a detail that finally got PRECIOUS working for me. The pattern that you give to PRECIOUS has to be exactly the pattern that is being used in the rule that creates the intermediate file. I want to save all files prefixed by moc_. At first I used .PRECIOUS: moc_% to no avail. Then I tried .PRECIOUS: moc_%.cpp and this did the trick.