Why does this Makefile fail to build make the final target, but all intermediate targets work fine? - makefile

Say we have the following Makefile:
.PHONY: build
build:
cd .. && make
%.hl.a.json: %.c
touch $#
%.hl.b.json: %.hl.a.json
touch $#
%.x.p.json: %.hl.b.json
touch $#
%.x.hl.a.json: %.x.p.json
touch $#
%.x.hl.b.json: %.x.hl.a.json
touch $#
.PHONY: clean
clean:
$(RM) *.json
Let us assume we have file called small.c, and that I want to build small.hl.b.json. However, running make small.hl.b.json (with GNU Make 3.81) gives me:
$ make small.x.hl.b.json
make: *** No rule to make target `small.x.hl.b.json'. Stop.
However, the Makefile works for building small.x.hl.a.json which produces
$ make small.x.hl.a.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
rm small.x.p.json small.hl.a.json small.hl.b.json
Thus, if I run make small.x.hl.a.json first, and then make small.x.hl.b.json, then everything works.
And now it gets interesting. If I do the following:
$ make small.x.hl.a.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
rm small.x.p.json small.hl.a.json small.hl.b.json
$ make clean
rm -f *.json
$ make small.x.hl.b.json
make: *** No rule to make target `small.x.hl.b.json'. Stop.
it does not work. But it does work if I do this:
$ make small.x.hl.a.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
rm small.x.p.json small.hl.a.json small.hl.b.json
$ make clean small.x.hl.b.json
rm -f *.json
touch small.hl.a.json
touch small.hl.b.json
touch small.x.p.json
touch small.x.hl.a.json
touch small.x.hl.b.json
rm small.x.p.json small.hl.a.json small.hl.b.json
Can someone tell me what's going on? And why can't I build small.x.hl.b.json directly but instead must first build small.x.hl.a.json and then small.x.hl.b.json?

I may be wrong, and I'll delete this answer if anyone can provide a better one, but this looks to me like a bug in GNUMake 3.81:
Make first considers the target small.x.hl.b.json, and finds two pattern rules that match it:
%.hl.b.json: %.hl.a.json # call this rule 2
...
%.x.hl.b.json: %.x.hl.a.json # call this rule 5
...
Note that both of these rules have the same prerequisite, namely small.x.hl.a.json.
It looks as if Make then makes a mistake. (Here's where I go out on a limb.) It goes searching for a way to build small.x.hl.a.json, remembering that it has already used rules 2 and 5, but failing to remember that they are parallel alternatives, not sequential steps. Later on, when Make needs small.hl.b.json, the only rule that will serve is rule 2, which Make rejects on the grounds that it must not use the same pattern rule twice in a chain. Never mind that this invocation would use a shorter stem, never mind that the chain in question is actually two chains, one of which has not yet used Rule 2, Make considers this a dead end.
This fits the results of the other experiments as well. Make can build small.x.hl.b.json without this self-collision, and once that target is built, Make can then build small.x.hl.b.json in a single step without a troublesome chain.

I found a tutorial saying that:
The rule with the shorted stem will be tried first, and if there is a tie the rule that appears first will be selected.
So I tried swapping the order of the rules so that .x. targets appear first, and then everything works!

Related

Make target with optional wildcard dependency doesn't update when dependency does

I'm using a makefile to build some documents from markdown. Since I have various possible outputs, I'm using a pattern rule. And since the input documents may or may not have associated tables, they have a wildcard dependency for csv files with the same stem filename:
tables = $(wildcard $*_*.csv)
%.docx: %.md $(tables)
#echo building $#
#touch $#
This works fine the first time:
$ touch thing.md
$ touch thing_table.csv
$ make thing.docx
building thing.docx
$ ls thing.docx
thing.docx
However, if the table file is updated, make still thinks everything's up to date:
$ touch thing_table.csv
$ make thing.docx
make: `thing.docx' is up to date.
I think this must have something to do with the order in which make evaluates things, but I don't understand it well enough to figure out how to make this work.
I can do sort of what I want to do using a more "literal" pattern rule:
%.docx: %.md %_*.csv
#echo building $#
#touch $#
But of course this fails in the absence of appropriately-named csv files:
$ rm thing.docx thing_table.csv
$ make thing.docx
make: *** No rule to make target `thing.docx'. Stop.
Is there a way to specify a target dependency such that if a target is foo.docx the dependencies would be foo_*.csv, but have those dependencies be optional (i.e. the target still works if the files don't exist) AND have it appropriately update if those optional dependencies change?
Ok, it looks like secondary expansion will work here:
.SECONDEXPANSION:
%.docx: %.md $$(wildcard $$*_*.csv)
#echo building $#
#touch $#

How to determine if Make target is a PHONY?

I have a make target that depends on a variable, which contains both PHONY and real targets.
This target needs to depend only on the real targets in the variable.
How can I test a variable to determine if it is a PHONY or not, so I can filter them out?
(I can test for a file's existence inside the recipe, but I don't want my target to be triggered by execution of any of the PHONY targets.)
Thanks!
There is a way to do it, but I would strongly recommend against it. First of, phony targets can be also file targets. And there is no way to tell a phony file target from a non-phony file target.
It looks like the question implies that the phony targets the author wants to ignore are all non-file targets. In this case see the example below.
.PHONY: phony_target .FORCE
.FORCE:
ALL_TARGETS = phony_target file_target undetermined_target
-include detect_phony.inc
all: final_target
# All done
final_target: $(REAL_TARGETS)
# create $# triggered by $?
#touch $#
ifeq (,$(MAKE_RESTARTS))
# Generate the list of real file targets in make include file
detect_phony.inc: .FORCE
#echo 'REAL_TARGETS = ' `ls $(ALL_TARGETS) 2>/dev/null` > $# |:
endif
file_target:
touch $#
undetermined_target phony_target:
# process $#
clean:
rm -f file_target final_target
Here are the test results:
$make clean
rm -f file_target final_target
$ make
# create final_target triggered by
# All done
$ touch file_target
$ make
# create final_target triggered by file_target
# All done
$ make
# All done
As you can see it only triggers the final target when the file target is updated.
Before you criticize - Here are the flaws of this implementation:
make is always called twice, updating the generated detect_phony.inc include file at every run
if detect_phony.inc gets corrupted somehow, make execution will be locked by syntax errors, until you manually delete it.
it can't handle phony file targets as I mentioned before
if another generated include is added in this makefile that requires another restart before detect_phony.inc this functionality will break.
So it this method is hacky and has several gotchas. I would not use it in production environment. I would insist on changing the top level Makefile first.

How do I get Make to do Second Expansion using two rules with the same target containing %?

I have a directory structure that looks like this:
$ tree
.
|-- dir
| `-- subdir
| `-- data
`-- makefile
where data is a file. My makefile looks like this:
all: dir/analysis
.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
When I run make, I would expect the result to look like this:
TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis
Instead, it just reports
make: *** No rule to make target `dir/analysis', needed by `all'. Stop.
If I change the first rule to dir/analysis: dir/subdir then it works as I expected. So I suspect that make ignores the first rule and skips straight to the second when the first rule is %/analysis: %/subdir. It also works as expected when both rules have dir/analysis as their target instead of just the first rule. MadScientists's answer to a different question here seemed to apply to my problem. I tried adding rules like
dummy_target: dir/subdir
mkdir -p dir/subdir
and
dir/subdir:
mkdir -p dir/subdir
to the end of makefile to try to make the dependency on the first rule an explicit target but this didn't change anything. I'm pretty new to make, so I'm probably missing something pretty stupid, but I can't for the life of me figure it out. How do I get the first and second rules to execute in the order they're written? I'm using Make version 3.81 in case that matters.
--EDIT--
If I actually add a command after the first rule, like #echo RULE 1 then that command executes and not the second one.
I think that you understand the makefile:
# Original
all: dir/analysis
.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
like this:
By default we make dir/analysis
We request secondary expansion
We say that whatever/analysis depends on whatever/subdir. So
that makes dir/subdir a prerequisite of dir/analysis
We say also that whatever/analysis depends on the thing we get by
suffixing /data to the prior prerequisites of whatever/analysis. (Secondary
expansion of $$^ gives us the prior prerequisites of the target). And
since the prior prerequisites of dir/analysis are dir/subdir, that now
makes dir/subdir dir/subdir/data the new prerequisites of dir/analysis.
Hence the output of the recipe ought to be:
TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis
This understanding is badly wrong. You're confusing and conflating the operation
of pattern rules with the operation or ordinary rules.
If a makefile says, e.g:
# A
xx: xa
xx: xb
xx: xc
touch $#
or even:
# B
xx: xa
xx: xb
%x: %c
touch $#
then the prerequisites of all the empty rules for the target xx are combined
with those of the (one) non-empty rule when xx is considered as a target. So
# A is equivalent to:
xx: xa xb xc
touch $#
and # B is equivalent to:
xx: xa xb
%x: %c
touch $#
and in both cases the prerequisites of xx are xa xb xc.
However:
%x: %a
%x: %b
touch $#
is not equivalent to:
%x: %a %b
touch $#
If you ever write an empty pattern rule, its effect is simply to delete
any prior pattern rule with the same target and prerequisite patterns. And if you
ever write a non-empty pattern rule, you simply replace
any prior pattern rule with the same target and prerequisite patterns. See
10.5.6 Canceling Implicit Rules
So:
.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
is equivalent to:
.SECONDEXPANSION:
%/analysis: $(addsuffix /data, $$^)
#echo TARGET: $# DEPS: $^
touch $#
Then when this pattern rule is considered with respect to dir/analysis,
there are no prior prerequisites: $^ will expand to the empty string, and
in secondary expansion, so will $$^. So finally the recipe is equivalent to:
# Residual
%/analysis: /data
#echo TARGET: $# DEPS: $^
touch $#
You can satisfy yourself of this by running both of the # Original and
# Residual makefiles in debug mode (-d) and comparing the outputs.
This explains why the outputs in both cases conclude:
make: *** No rule to make target 'dir/analysis', needed by 'all'. Stop.
A pattern rule will only be instantiated to make a target if doing so offers a way for the target to be made. Why else select it? Since the prerequisite /data does not exist, the pattern rule
is not viable and is discarded. Just as if you tried to make foo.o with the makefile:
%.o: %c
touch $#
when there is no foo.c in the directory.
If you really want to see the output you were expecting, then you can get it from:
.PHONY: all clean
all: dir/analysis
%/analysis: %/subdir %/subdir/data
#echo TARGET: $# DEPS: $^
touch $#
clean:
rm -f dir/analysis
But it is hard to believe that is what you really want to see. This makefile
makes the analysis dependent on the data and also on the directory where
the data resides. What can be the point of that?
%/analysis: %/subdir/data
presumably expresses the desired relationship between the data and the analysis:-
If the data doesn't exist then the analysis is out of date and cannot be made (till you get some data).
If the data exists and is older than the analysis then the analysis does not
need to be made.
If the data exists and is newer than the analysis then the analysis can and will be
made.
By making dir/subdir an independent prerequisite all that you achieve is to introduce
a requirement to remake the analysis if it becomes older than the directory in
which the data resides - regardless of the data. So if, e.g. I run touch dir/subdir,
that will require make to re-run the analysis, even though the data hasn't changed.
I can make sense of your motivation only by supposing that in
reality you also want make to make the data, and the directory where it resides, if
they happen not exist when an analysis is required. If that is the situation then
you want a makefile on the lines of:
.PHONY: all clean
all: dir/analysis
.SECONDARY:
%/subdir:
mkdir -p $#
%/subdir/data: | %/subdir
touch $#
%/analysis: %/subdir/data
#echo TARGET: $# DEPS: $^
touch $#
clean:
rm -f dir/analysis dir/subdir/data
Here, the pattern rule:
%/subdir/data: | %/subdir
makes %/subdir an order-only prerequisite
of %/subdir/data. It means that /dir/subdir is not considered when determining whether /dir/subdir/data needs
to be made, but if it is determined that /dir/subdir/data is to be made, then /dir/subdir will be made first. This
way, /dir/subdir will be created, if it does not exist, when you need it to put the data there, but will have no influence
on whether you need to make the data or the analysis.
The special target .SECONDARY is a technicality. It directs make not to automatically delete any subsequent targets
that it deduces to be intermediate artefacts that emerge from chained pattern rules. Without it, in this example make
would deduce that dir/subdir/data and dir/subdir/ are just waste-products of making dir/analysis and auto-delete them
at the end.
If you run this makefile initially with no dir/subdir/data and no dir/subdir/ you get:
$ make
mkdir -p dir/subdir
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis
And subsequently:
$ make clean
rm -f dir/analysis dir/subdir/data
$ make
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis
And then:
$ make
make: Nothing to be done for 'all'.
$ touch dir/subdir
$ make
make: Nothing to be done for 'all'.

How can I make archive contents true targets of an archive extraction rule with GNU make?

I wish to write several rules that extract the contents of tar archives to produce a number of files that are then used as input dependencies for other rules. I wish this to work even with parallel builds. I'm not using recursive make.
First up, sorry for the marathon question, but I don't think I can explain it well in a shorter form.
Think of untarring a collection of source files and then compiling them with rules stored outside of the archive to produce various build artefacts that are then, in turn, used further. I am not seeking other arrangements that lead to the omission of this problem. Just take it for granted that I have good reason to do this. :)
I'll demonstrate my issue with a contrived example. Of course, I started with something basic:
TAR := test.tar.bz2
CONTENTS := $(addprefix out/,$(filter-out %/,$(shell tar -tf $(TAR))))
out: $(TAR)
rm -rf out
mkdir out
tar -xvf $< -C out --touch || (rm -rf out; exit 1)
$(CONTENTS): out
sums: $(CONTENTS)
md5sum $^ > $#
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
rm -rf out sums
The thinking here is that since $(CONTENTS) are all of the files in the archive, and they all depend on out, then to run the sums target we need to end up extracting the archive.
Unfortunately, this doesn't (always) work if you use a parallel invocation after a previous build when only test.tar.bz2 is updated, because make may decide to check the timestamp of $(CONTENTS) before running the out rule, which means it thinks that each of the sources is older than sums, so there is nothing to do:
$ make clean
rm -rf out sums
$ make -j6
rm -rf out
mkdir out
tar -xvf test.tar.bz2 -C out --touch || (rm -rf out; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
md5sum out/data.txt out/file out/weird.file.name out/dir/another.c out/dir/more > sums
$ touch test.tar.bz2
$ make -j6
rm -rf out
mkdir out
tar -xvf test.tar.bz2 -C out --touch || (rm -rf out; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
Oops! The sums rule didn't run!
So, the next attempt was to tell make that the one untar rule actually does make all the $(CONTENTS) directly. This seems better since we're telling make what's really going on, so it knows when to forget any cached timestamps for targets when they are remade through their rule.
First, let's look at what seems to work, and then I'll get to my problem:
TAR := test.tar.bz2
CONTENTS := $(addprefix out/,$(filter-out %/,$(shell tar -tf $(TAR))))
# Here's the change.
$(addprefix %/,$(patsubst out/%,%,$(CONTENTS))): $(TAR)
rm -rf out
mkdir out
tar -xvf $< -C out --touch || (rm -rf out; exit 1)
sums: $(CONTENTS)
md5sum $^ > $#
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
rm -rf out sums
In this case, we've effectively got a rule that says:
%/data.txt %/file %/weird.file.name %/dir/another.c %/dir/more: test.tar.bz2
rm -rf out
mkdir out
tar -xvf $< -C out --touch || (rm -rf out; exit 1)
Now you can see one of the reasons I forced the output into an out directory: to give me a place to use the % so I could use a pattern rule. I am forced to use a pattern rule even though there isn't a strong pattern here because it is the only way make can be told that one rule creates multiple output files from a single invocation. (Isn't it?)
This works if any of the files are touched (not important for my use case) or if the test.tar.bz2 file is touched, even in parallel builds, because make has the information it needs: running this recipe makes all these files and will change all their timestamps.
For example, after a previous successful build:
$ touch test.tar.bz2
$ make -j6
rm -rf out
mkdir out
tar -xvf test.tar.bz2 -C out --touch || (rm -rf out; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
md5sum out/data.txt out/file out/weird.file.name out/dir/another.c out/dir/more > sums
So, if I have a working solution, what's my problem?
Well, I have many of these archives to extract, each with their own set of $(CONTENTS). I can manage that, but the trouble comes in writing a nice pattern rule. Since each archive needs its own rule defined, the patterns for each rule must not overlap even if the archives have similar (or identical) content. That means the output paths for the extracted files must be made unique for each archive, as in:
TAR := test.tar.bz2
CONTENTS := $(addprefix out.$(TAR)/,$(filter-out %/,$(shell tar -tf $(TAR))))
$(patsubst out.$(TAR)/%,out.\%/%,$(CONTENTS)): $(TAR)
rm -rf out.$(TAR)
mkdir out.$(TAR)
tar -xvf $< -C out.$(TAR) --touch || (rm -rf out.$(TAR); exit 1)
sums: $(CONTENTS)
md5sum $^ > $#
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
rm -rf out.$(TAR) sums
So, this can be made to work with the right target-specific variables, but it now means that the extraction points are all "ugly" in a way that is very specifically tied to how the makefile is constructed:
$ make -j6
rm -rf out.test.tar.bz2
mkdir out.test.tar.bz2
tar -xvf test.tar.bz2 -C out.test.tar.bz2 --touch || (rm -rf out.test.tar.bz2; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
md5sum out.test.tar.bz2/data.txt out.test.tar.bz2/file out.test.tar.bz2/weird.file.name out.test.tar.bz2/dir/another.c out.test.tar.bz2/dir/more > sums
The next natural step I took was to try to combine static pattern rules with the multiple-targets-via-pattern-rule approach. This would let me keep the patterns very general, but limit their application to a specific set of targets:
TAR := test.tar.bz2
CONTENTS := $(addprefix out/,$(filter-out %/,$(shell tar -tf $(TAR))))
# Same as second attempt, except "$(CONTENTS):" static pattern prefix
$(CONTENTS): $(addprefix %/,$(patsubst out/%,%,$(CONTENTS))): $(TAR)
rm -rf out
mkdir out
tar -xvf $< -C out --touch || (rm -rf out; exit 1)
sums: $(CONTENTS)
md5sum $^ > $#
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
rm -rf out sums
Great! Except it doesn't work:
$ make
Makefile:5: *** multiple target patterns. Stop.
$ make --version
GNU Make 4.0
So, is there a way to use multiple target patterns with a static pattern rule? If not, is there another way to achieve what I have in the last working example above, but without the constraint on the output paths to make unique patterns? I basically need to tell make "when you unpack this archive, all of the files in this directory (which I am willing to enumerate if necessary) have new timestamps". A solution where I can force make to restart if and only if it unpacks an archive would also be acceptable, but less ideal.
The problem with your original makefile is that you have a collision in names. You have a target (non-phony) named out and a directory named out. make thinks those are the same thing and gets very confused.
(Note: I added .SUFFIXES: to your first makefile to cut down on some noise but it doesn't change anything. The -r and -R flags disable make built-in rules and variables also for noise reduction.)
$ make clean
....
$ make -j6
....
$ touch test.tar.bz2
$ make -rRd -j6
....
Considering target file 'all'.
File 'all' does not exist.
Considering target file 'sums'.
Considering target file 'out/data.txt'.
Looking for an implicit rule for 'out/data.txt'.
No implicit rule found for 'out/data.txt'.
Considering target file 'out'.
Considering target file 'test.tar.bz2'.
Looking for an implicit rule for 'test.tar.bz2'.
No implicit rule found for 'test.tar.bz2'.
Finished prerequisites of target file 'test.tar.bz2'.
No need to remake target 'test.tar.bz2'.
Finished prerequisites of target file 'out'.
Prerequisite 'test.tar.bz2' is older than target 'out'.
No need to remake target 'out'.
Finished prerequisites of target file 'out/data.txt'.
Prerequisite 'out' is older than target 'out/data.txt'.
No recipe for 'out/data.txt' and no prerequisites actually changed.
No need to remake target 'out/data.txt'.
.... # This following set of lines repeats for all the other files in the tarball.
Considering target file 'out/file'.
Looking for an implicit rule for 'out/file'.
No implicit rule found for 'out/file'.
Pruning file 'out'.
Finished prerequisites of target file 'out/file'.
Prerequisite 'out' is older than target 'out/file'.
No recipe for 'out/file' and no prerequisites actually changed.
No need to remake target 'out/file'.
....
Finished prerequisites of target file 'sums'.
Prerequisite 'out/data.txt' is older than target 'sums'.
Prerequisite 'out/file' is older than target 'sums'.
Prerequisite 'out/weird.file.name' is older than target 'sums'.
Prerequisite 'out/dir/more' is older than target 'sums'.
Prerequisite 'out/dir/another.c' is older than target 'sums'.
No need to remake target 'sums'.
Finished prerequisites of target file 'all'.
Must remake target 'all'.
Successfully remade target file 'all'.
make: Nothing to be done for 'all'.
The main details here are these two lines:
Considering target file 'out'.
Prerequisite 'out' is older than target 'out/data.txt'
The out directory doesn't matter here. We don't care about it (and make doesn't deal with directory prerequisites too well anyway because modification timestamps on directories don't mean the same thing as they do on files). Even more to the point you don't want out/data.txt not being created because the build artifact directory target already existed (and seemed older).
You can "fix" this by marking the out target as .PHONY but that is just going to get make to extract the tarball every time you run make (you already run tar -tf every time you run make so it would probably be better to just combine those two steps if you were going to do this).
That said I wouldn't do that. I think the simplest solution to this problem is the "atomic rules" idea from John Graham-Cunning built-up and explained here.
sp :=
sp +=
sentinel = .sentinel.$(subst $(sp),_,$(subst /,_,$1))
atomic = $(eval $1: $(call sentinel,$1) ; #:)$(call sentinel,$1): $2 ; touch $$# $(foreach t,$1,$(if $(wildcard $t),,$(shell rm -f $(call sentinel,$1))))
.PHONY: all
all: a b
$(call atomic,a b,c d)
touch a b
You could probably also do this with an extraction stamp file (prereq on the tarball), extracting the tarball to a "shadow" directory and copy/link to the "final" location (build/$file: shadow/$file target) if you wanted to but that's going to be a bit more complicated I think.

Makefile pattern rule either ignores phony rule or spontaneously deletes output file

I'm trying to write a makefile to produce several output files for each of several sources, using pattern 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 do not represent real output files, I tried marking them as .PHONY. However, running make then doesn't 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 be pretending to run the %.all rules, but skipping the bodies.
But with the .PHONY line commented out, Make runs 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
A minimal example giving anomalous behavior:
%.all: %.out
#echo Made $*
%.out:
touch $#
I expect running make somefile.all to cause it to create the file somefile.out, but it gets deleted:
$ make somefile.all
touch somefile.out
Made somefile
rm somefile.out
Keeping make from deleting intermediary files
I recommend against using .PRECIOUS (see below as to why). Using .SECONDARY would preserve the .out files:
TARGETS=foo bar
all: $(TARGETS:=.all)
%.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
$(TARGETS:=.all) just appends .all to all names in TARGETS. $(TARGETS:=.out) appends .out. We apparently cannot use %.out as a target of .SECONDARY. These just save having to relist all targets individually.
I prefer to not use .PRECIOUS for this because the documentation says
if make is killed or interrupted during the execution of their recipes, the target is not deleted.
This can leave corrupted files in the file system. Here's an example.
all: foo.all bar.all
%.all: %.out
#echo Made $*
%.out:
sh -e -c 'echo "{1, 2, 3" > $#; FAIL!; echo "}" >> $#'
.PRECIOUS: %.out
The FAIL! command simulates a tool that crashes in the middle of its work. Here's a 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 incomplete. Let's try making 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 none the wiser about files left around by earlier runs so when you run make again, it will take the corrupted files at face value. foo.out was not remade (despite the "Made foo" message) because it already exists and the Makefile went straight to trying to make bar.
.SECONDARY makes it so that:
The targets which .SECONDARY depends on are treated as intermediate files, except that they are never automatically deleted.
This means they are never automatically deleted just because they are intermediate files. The default make behavior of deleting targets that were being rebuilt if the tool rebuilding them crashed is not affected.
Using .PHONY with pattern rules
It seems though that .PHONY works only for targets that are explicit, not inferred. I've not found documentation confirming 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): gives the list of targets to which the pattern can be applied. It makes foo.all and bar.all explicit targets. Without this, they would be inferred targets.
You can test that it works by creating file called foo.all in your directory and run make over and over. The foo.all file has no effect on make.
Your somefile.out files are considered intermediate by GNU make, which is why they are automatically deleted in your example. You can instruct GNU make to preserve these files by use the of .PRECIOUS special target, like this:
%.all: %.out
#echo Made $*
%.out:
touch $#
.PRECIOUS: %.out

Resources