Say we have a project where using make, we create .bar
files, then combine them into a foobar
file for the final result.
We make each .bar
file from a .foo
file, and in the foo
directory we also use a .foobar
file which we also create using make.
I came up with this Makefile to do this:
default: all
%.bar: %.foo
@echo generic build for $@
@cat $< > $@
foo/%.bar: foo/%.foo foo/%.foobar
@echo specific build for $@
@cat $< > $@
%.foobar:
@echo building $@
@echo foobar > $@
foobar: foo/foo.bar bar/foo.bar
@echo building $@
@cat foo/foo.bar bar/foo.bar > $@
all: foobar
clean:
rm -r foobar */*.bar
.PHONY: all clean
Then I set up an environment like this:
mkdir foo bar
touch foo/foo.foo bar/foo.foo
Now, running make
I expect foo/foo.bar
to be built using the second rule, and bar/foo.bar
using the first.
Instead, I see the following output:
generic build for foo/foo.bar
generic build for bar/foo.bar
building foobar
Indicating that for both files, the first rule was used.
Why does this happen and how do I fix it?
It seems to be related to having dependencies that don't exist yet compared to the other rule, since if I do a make foo/foo.foobar
first then run make
the second rule gets used for foo/foo.bar
.
I want everything in the foo directory to specifically use the second rule with these dependencies, never the first one.
Why does this happen and how do I fix it?
Section 10.5.4 of the GNU make
manual explains:
a[n implicit] rule which can be satisfied without chaining other implicit rules (for example, one which has no prerequisites or its prerequisites already exist or are mentioned) always takes priority over a rule with prerequisites that must be made by chaining other implicit rules.
Pattern rules are implicit rules, and that is the effect you are observing. To overcome it, you can either avoid using a pattern rule for building .foobar
files, at least in folder foo/
, or remove the .foobar files from the prerequisite list for .bar
targets in that folder. Among the possible alternatives are:
write a regular rule for building those .foobar
files. This will require designating all of them explicitly, but is not otherwise much different from what you're already doing. For example,
FOO_FOOBARS = foo/foo.foobar
$(FOO_FOOBARS):
@echo building $@
@echo foobar > $@
OR
Remove foo/%.foobar
from the prerequisite list of foo/%.bar
. Instead, use recursive make
in that recipe to build the .foobar
file when needed, something like this:
foo/%.bar: foo/%.foo
$(MAKE) $(<)bar
@echo specific build for $@
@cat $< > $@
Of those, I prefer the former.