Makefile template rule ignores fake rule or spontaneously deletes output file

I am trying to write a makefile to create multiple output files for each of several sources using template rules.

I have the following Makefile (GNU Make 3.8.1):

 all : foo.all bar.all %.all : %.pdf %.svg @echo Made $* %.pdf : touch $@ %.svg : touch $@ .PHONY: foo.all bar.all 

Since *.all does not represent real output files, I tried marking them as .PHONY . However, running make does not work:

 $ ls Makefile $ make make: Nothing to be done for `all'. 

According to make -d :

  No implicit rule found for `all'. Considering target file `foo.all'. File `foo.all' does not exist. Finished prerequisites of target file `foo.all'. Must remake target `foo.all'. Successfully remade target file `foo.all'. Considering target file `bar.all'. File `bar.all' does not exist. Finished prerequisites of target file `bar.all'. Must remake target `bar.all'. Successfully remade target file `bar.all'. Finished prerequisites of target file `all'. Must remake target `all'. Successfully remade target file `all'. make: Nothing to be done for `all'. 

which seems to pretend to follow the %.all rules, but skips bodies.

But if the .PHONY line .PHONY commented out, Make launches the targets, but then spontaneously decides to delete the output files:

 $ make touch foo.pdf touch foo.svg Made foo touch bar.pdf touch bar.svg Made bar rm foo.pdf foo.svg bar.pdf bar.svg 

According to make -d , it says:

 Removing intermediate files... 

Minimal example

Minimal example giving abnormal behavior:

 %.all: %.out @echo Made $* %.out: touch $@ 

I expect to run make somefile.all to force it to create somefile.out file, but it somefile.out deleted:

 $ make somefile.all touch somefile.out Made somefile rm somefile.out 
+4
wildcard makefile gnu-make
source share
2 answers

Saving make from deleting intermediary files

I recommend not using .PRECIOUS (see below why). Using .SECONDARY save the .out files:

 TARGETS=foo bar all: $(TARGETS:=.all) %.all: %.out @echo Made $* %.out: touch $@ .SECONDARY: $(TARGETS:=.out) 

$(TARGETS:=.all) simply adds .all to all names in TARGETS . $(TARGETS:=.out) adds .out . Apparently, we cannot use %.out as the target .SECONDARY . They simply retain the need to replicate all goals individually.

I prefer not to use .PRECIOUS for this, because the documentation says

if make is killed or interrupted while executing its recipes, the target is not deleted.

This may leave corrupted files in the file system. Here is an example.

 all: foo.all bar.all %.all: %.out @echo Made $* %.out: sh -e -c 'echo "{1, 2, 3" > $@; FAIL!; echo "}" >> $@' .PRECIOUS: %.out 

FAULT! A team simulates a tool that falls in the middle of its work. Here is the shell session working with the Makefile above:

 $ ls Makefile $ make sh -e -c 'echo "{1, 2, 3" > foo.out; FAIL!; echo "}" >> foo.out' sh: 1: FAIL!: not found make: *** [foo.out] Error 127 $ cat foo.out {1, 2, 3 

Yikes ... my foo.out file is empty. Try again:

 $ make Made foo sh -e -c 'echo "{1, 2, 3" > bar.out; FAIL!; echo "}" >> bar.out' sh: 1: FAIL!: not found make: *** [bar.out] Error 127 $ cat *.out {1, 2, 3 {1, 2, 3 

Make is not wiser about files left over from earlier runs, so when you run make again, it will damage the files at face value. foo.out not redone (despite the message "Made foo"), because it already exists, and the Makefile went directly to trying to make a bar.

.SECONDARY makes it so that:

The targets on which .SECONDARY depends are considered intermediate files, except that they are never deleted automatically.

This means that they are never deleted automatically just because they are intermediate files. By default, the behavior is performed to delete targets that were rebuilt if the damaged recovery tool was not damaged.

Using .PHONY with template rules

It seems that .PHONY only works for explicit, not inferred purposes. I did not find any documentation supporting this. However, this works:

 TARGETS:=foo bar TARGETS_all:=$(TARGETS:=.all) .PHONY: all all: $(TARGETS_all) .PHONY: $(TARGETS_all) $(TARGETS_all): %.all: %.out @echo Made $* %.out: touch $@ .SECONDARY: $(TARGETS:=.out) 

In this rule, $(TARGETS_all): %.all: %.out $(TARGETS_all): provides a list of targets to which the template can apply. It makes explicit targets foo.all and bar.all . Without this, they would be the intended goals.

You can verify that it works by creating a file called foo.all in your directory and run it again and again. The foo.all file foo.all not affect make.

+3
source share

Your somefile.out files are considered intermediate with GNU make, so they are automatically deleted in your example. You can instruct GNU make to save these files with a special .PRECIOUS target, for example:

 %.all: %.out @echo Made $* %.out: touch $@ .PRECIOUS: %.out 
+2
source share

All Articles