Reputation: 5438
I have a make file with some pattern rules¹ that end up as prerequisites for another rule. A minimum example that shows the symptoms I'm puzzled by is this:
.PHONY: clean default one two
default: clean one two
clean:
@rm -f {one,two}.{a,b}
one two: %: %.a %.b
@echo TARGET $@ PREREQUISITES $^
%.a %.b:
@echo Prereq $@
@touch $@
The output I expect when running this would be:
Prereq one.a
Prereq one.b
TARGET one PREREQUISITES one.a one.b
Prereq two.a
Prereq two.b
TARGET two PREREQUISITES two.a two.b
Instead, only the first prerequisite gets built, make
gives me this:
Prereq one.a
TARGET one PREREQUISITES one.a one.b
Prereq two.a
TARGET two PREREQUISITES two.a two.b
As you can see, the recipe itself parses these correctly and knows that it should have been built after both prerequisites, but the prerequisite rule hasn't actually been run.
Incidentally, I'm using order-only prerequisites for the second item here but it doesn't really matter: the same problem is exhibited either way.
If I run the same target a second time, then it builds the second prerequisite. In other words two passes finally gets me the result I need:
$ make clean
$ make one
Prereq one.a
TARGET one PREREQUISITES one.a one.b
$ make one
Prereq one.b
TARGET one PREREQUISITES one.a one.b
The first time it runs, only the first prerequisite exists and I'm left with a broken one
. On the second pass one.a
exists, so it decides to build one.b
. Now that both prerequisites exist my one
builds properly.
If I spell out the prerequisite target variations as two separate recipes (repeat the same block for both %a:
and %.b
patterns separately) then both prerequisites get built for each target. However this would make my file quite a bit more complex and I'd like to understand why this breaks.
¹ This make file has some other problems but I've isolated this issue to an reproducible case so I don't think its other idiosyncrasies are the issue here.
Upvotes: 4
Views: 1560
Reputation: 3548
GNU Make has what we may consider a little "quirk" in the sense that multiple-target pattern rules don't work exactly like several rules. In this situation, the rule is triggered only once by a single target, and Make expects the rule to make all targets at once. From the GNU Make documentation:
Pattern rules may have more than one target. Unlike normal rules, this does not act as many different rules with the same prerequisites and recipe. If a pattern rule has multiple targets, make knows that the rule’s recipe is responsible for making all of the targets. The recipe is executed only once to make all the targets. [...]
So what is happening here is:
one
: finds one.a
and one.b
from the pattern %: %.a %.b
;one.a
and one.b
;$@
) set to one.a
(the one that triggered the rule);one.a
and one.b
as updated, even though the recipe only created one.a
;one
have been satisfied and go on to do the same with two
.The reason why Make touches different files when you call it again is because now one.a
and two.a
are up-to-date. So the targets that trigger the rule become one.b
and two.b
. If you remove only one.a
before calling Make a second time, it will create one.a
and two.b
.
So you have at least three possible solutions. One is to spell out the targets that behave like you'd expects—which you said is not a good solution, but it's worth mentioning here anyway. Another is to break that %.a %.b
into two rules, which achieves the same but might be easier considering your needs. And the third is to do what Make expects and create both targets in the same recipe—e.g., you can use the stem in the recipe:
%.a %.b:
@echo Processing target $@ from stem $*
touch $*.a $*.b
Another thing that I noticed while looking into this problem is that your MCVE's clean
is broken. By default, Make will use /bin/sh
, which will not expand things like {a,b}
. You can set SHELL=/bin/bash
to change that.
Upvotes: 5