Multiple stems (%) in a Makefile prerequisite - makefile

I'm trying to use the following Makefile to create the target foo.a with foo.foo.b as a prerequisite, using the stem character (%) as shown.
%.a: %.%.b
touch $#
However running touch foo.foo.b; make foo.a doesn't work with No rule to make target. The debug output below shows that the stem % is only expanded once. How to get the desired behavior of replacing all % in the prerequisite?
Considering target file 'sdf.a'.
File 'sdf.a' does not exist.
Looking for an implicit rule for 'sdf.a'.
Trying implicit prerequisite 'sdf.%.b'.
Looking for a rule with intermediate file 'sdf.%.b'.
Avoiding implicit rule recursion.
Trying pattern rule with stem 'sdf.%.b'.
Trying implicit prerequisite 'sdf.%.b,v'.
(..)
No implicit rule found for 'sdf.a'.
Finished prerequisites of target file 'sdf.a'.
Must remake target 'sdf.a'.
make: *** No rule to make target 'sdf.a'. Stop.
Side note: this seems like a trivial question but for some reason I can't find the answer, maybe I'm using the wrong search terms or overlooking something simple, sorry if that is the case.

The stem is only replaced once. If you wish to have stem contents inserted more than once, you would need to use secondary expansion, i.e.:
$ cat Makefile
.SECONDEXPANSION:
%.a: $$*.$$*.b
echo Making $# from $<
Output:
$ touch foo.foo.b
$ make -s foo.a
Making foo.a from foo.foo.b

Related

GNU make - implicit rule is not used, but static pattern rule is

Consider the following makefile (and any hi.c):
.PHONY: analyze-%
hi: hi.c
gcc -o $# $<
%.json: %
touch $# # actually created by analysis-tool
analyze-%: %.json # why does this not work?
As my comment in the makefile points out, the implicit rule does not work:
$ make analyze-hi
make: *** No rule to make target 'analyze-hi'. Stop.
It only works after transforming it into a static pattern rule:
...
analyze-hi: analyze-%: %.json
Why is this the case? Shouldn't make be able to figure this out on its own, without me having to explicitly write the full target name? There is no ambiguity or anything (as far as I'm aware).
Pattern rules must have recipes. If they don't have a recipe then they're not creating a pattern rule, they're canceling one.
See https://www.gnu.org/software/make/manual/html_node/Canceling-Rules.html
A static pattern rule, contrary to what's implied by its name, is not actually creating an implicit rule (pattern or suffix rule). It's creating explicit rules, just based on a pattern. Explicit rules don't have to have recipes.

Make not using Match Anything rule for prerequisite

So, I have a Makefile with 2 pattern rules, one of them is a terminal Match-Anything; the target is about.html (for example):
vpath %.md $(SOURCE_DIR)
HEADER := assets/navbar.part
%.html : %.md $(HEADER)
pandoc [...] $< -o $#
%::
cp $(SOURCE_DIR)/$# $#
If I execute make assets/navbar.part, and then make about.html (for example), everything works fine (navbar.part uses the match anything, and about.html uses the first rule). But if I try to make about.html without having navbar.part present, make does:
Find the prerequisite about.md
?? Gives up on fulfilling $(HEADER)
Tries to use the match anything rule to generate about.html
I was expecting that make would:
Find the prerequisite about.md
Make the prerequisite $(HEADER) using the match-anything rule
With all the prerequisites fulfilled, finally execute the first rule
From reading the debug trace, it seems like Make never really tries to fulfill the $(HEADER) prerequisite:
Considering target file 'about.html'.
File 'about.html' does not exist.
Looking for an implicit rule for 'about.html'.
Trying pattern rule with stem 'about'.
Trying implicit prerequisite 'about.md'.
Found prerequisite 'about.md' as VPATH '../website/about.md'
Trying rule prerequisite 'assets/navbar.part'.
Trying pattern rule with stem 'about.html'.
Found an implicit rule for 'about.html'.
Considering target file 'about.md'.
Finished prerequisites of target file 'about.md'.
No need to remake target 'about.md'; using VPATH name '../website/about.md'.
Finished prerequisites of target file 'about.html'.
Must remake target 'about.html'.
cp -f ../website/about.html about.html
Any ideas?
After digging the manual, I found section 10.8 that states:
For each pattern rule in the list:
Find the stem s, which is the nonempty part of t or n matched by the ‘%’ in the target pattern.
Compute the prerequisite names by substituting s for ‘%’; if the target pattern does not contain a slash, append d to the front of each prerequisite name.
Test whether all the prerequisites exist or ought to exist. (If a file name is mentioned in the makefile as a target or as an explicit prerequisite, then we say it ought to exist.)
Because the $(HEADER) does not meet the definition of ought to exist:
No file exists when the rule is tried
No rule with that explicit target exists
it fails; however, it suceeds if we try to make specifically $(HEADER), and then try to make about.html it suceeds, because by then $(HEADER) is an existing file.

Makefile Pattern Rule is found but recipe does not run

I have a makefile that has the following pattern rule to create an implicit rule:
%.cpp:
$(warning foo)
When I check the output of make -d I see that this implicit rule is being found and matching a file. I verify that when I remove the pattern rule, the output of make -d no longer shows a match. However, even when the implicit rule is found, the warning doesn't show up. The recipe isn't running, even though the implicit rule matches.
What gives?
That recipe tells make how to create a %.cpp file (and gives it no prerequisites).
When are you expecting it to be run?
It will not run for any existing *.cpp files as the lack of prerequisites means that make considers the files up to date.
Try running make totally-doesnt-exist.cpp and you should see the warning.

Why does make ignore this rule, and trigger an implicit one instead?

I have the following rule in my makefile:
$(TESTDIR)/%.test.out:$(TESTDIR)/%.test $(TESTDIR)/%.in
$< < $(patsubst %.out, %.in, $#) 2>&1 > $#
I expect that, when I invoke make
make testing/Candidate.test.out
(where TESTDIR=testing in the makefile), make should respond with
testing/Candidate.test < Candidate.test.in 2>&1 >Candidate.test.out
Instead, make responds with
cp testing/Candidate.test testing/Candidate.test.out
and make -d yields:
Considering target file 'testing/Candidate.test.out'.
File 'testing/Candidate.test.out' does not exist.
Looking for an implicit rule for 'testing/Candidate.test.out'.
Trying pattern rule with stem 'Candidate'.
Trying implicit prerequisite 'testing/Candidate.test'.
Trying implicit prerequisite 'testing/Candidate.in'.
Trying pattern rule with stem 'Candidate.test'.
Trying implicit prerequisite 'testing/Candidate.test'.
Found an implicit rule for 'testing/Candidate.test.out'.
Building with make -r:
make -r testing/Candidate.test.out
make: *** No rule to make target 'testing/Candidate.test.out'. Stop.
indicates that make is decidedly not recognizing my rule, but I can't see why not. Clearly make believes $(TESTDIR)=testing, based on the cp output. Clearly it also recognizes testing/Candidate.test.out as a valid target, because it attempts to build it (whereas it fails for testing/blah.test.out).
Is there something obvious I'm missing here?
One of the targets in the rule (%.in) is incorrect (should be %.test.in).

Ensuring a prerequisite exists for a pattern rule

I understand that an "explicit" pattern rule will take precedence on its implicit counterpart when its prerequisites can be made.
all: src/foo.o
src/%.o: makefile my_haeder.h src/%.c
echo Do something with those source files
If there is a typo for "my_header.h", the implicit rule for %.o will take precedence. Not only my recipe will not be executed, but touching the prerequisites will not trigger the rule. Actually it is the second point which is of interest for me.
The make documentation offers a verification using static pattern rules:
SET_OF_FILES=src/foo.o
all: src/foo.o
$(SET_OF_FILES): src/%.o: makefile my_haeder.h src/%.c
echo Do something with those source files
This results in:
gmake: *** No rule to make target `src/my_haeder.h', needed by `src/foo.o'. Stop.
Though a larger rule, that solution is nice, as long as I don't have to add a rule for which the stem could overlap:
SET_OF_FILES=src/foo.o src/subsrc/bar.o
all: src/foo.o
$(SET_OF_FILES): src/%.o: makefile my_header.h src/%.c
echo Do something with those source files
$(SET_OF_FILES): src/subsrc/%.o: makefile my_header.h src/subsrc/%.c
echo Do something with those other source files
Which results in:
makefile:8: target `src/foo.o' doesn't match the target pattern
makefile:9: warning: overriding commands for target `src/foo.o'
makefile:6: warning: ignoring old commands for target `src/foo.o'
makefile:9: warning: overriding commands for target `src/subsrc/bar.o'
makefile:6: warning: ignoring old commands for target `src/subsrc/bar.o'
The first message is here because I didn't bother $(filter)ing SET_OF_FILES. I don't know how to solve the next warnings, which for any reviewer would mean "something's wrong".
Is there another (more elegant) way to verify that the prerequisites are actually feasible, in order to avoid dropping the explicit pattern rule?
(using GNU Make 3.79.1 win32)
Add a separate rule to check for your prerequisites
all: prereqs src/foo.o
prereqs: Makefile my_header.h
src/%.o: src/%.c Makefile my_header.h
echo Do something with those source files
src/subsrc/%.o: src/subsrc/%.c Makefile my_header.h
echo Do something with those other source files
which will give you:
make
make: *** No rule to make target 'my_header.h', needed by 'prereqs'. Stop

Resources