make implicit rules don't work without command - makefile

I have a simple Makefile like this:
%.t1: %.t2
%.t2:
echo "t2"
When I type
make x.t2
it works fine. But when I type
make x.t1
I get
make: *** No rule to make target 'x.t1'. Stop.
If I modify the %.t1 target to say
%.t1: %.t2
echo
Then it works. Why doesn't it work without a command? I'm using GNU make 4.0 on Fedora 23.

A pattern rule with no recipe does not add a rule, it cancels implicit rules. See http://www.gnu.org/software/make/manual/make.html#Canceling-Rules:
You can cancel a built-in implicit rule by defining a pattern rule with the same target and prerequisites, but no recipe. For example, the following would cancel the rule that runs the assembler:
%.o : %.s
A semi-colon can be added to tell make that it has an empty recipe:
%.t1: %.t2 ;
%.t2:
echo t2
Which yields:
$ make x.t1
t2

Related

Multiple stems (%) in a Makefile prerequisite

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

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.

a surprising (?) behaviour of GNU Make when using ``%`` as target

Consider the following Makefile
foo:
#echo '$#'
test:
#echo '$#'
#echo '---'
# Catch-all target
%: test
#echo '+++'
#echo '$#'
When issuing make bar the following is the console output:
$ make bar
test
---
+++
Makefile
+++
bar
I would like to understand the origin of Makefile which shows it is received as argument at some point, and also how to get rid of it in such a scheme. This is using
GNU Make 4.1
Built for x86_64-apple-darwin13.4.0
GNU make treats the makefile itself as a target that needs to be updated. See How Makefiles Are Remade:
... after reading in all makefiles, make will consider each as a goal target and attempt to update it. If a makefile has a rule which says how to update it (found either in that very makefile or in another one) or if an implicit rule applies to it (see Using Implicit Rules), it will be updated if necessary...
If you know that one or more of your makefiles cannot be remade and you want to keep make from performing an implicit rule search on them, perhaps for efficiency reasons, you can use any normal method of preventing implicit rule look-up to do so. For example, you can write an explicit rule with the makefile as the target, and an empty recipe (see Using Empty Recipes).
Hence, the catch-all-target % is used to update Makefile.
Makefiles often do not have to be updated, so it is customary to add an empty rule for that:
Makefile : ;

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).

error using makefile, targets and %

I'm trying to debug the following code:
TESTS=$(shell cat yoursourcefile)
all: $(TESTS)
%: compile_design
compile $#_tb.vhd >> log_file.log
simulate $#
I got this error:
makefile_tb.vhd >> log_file.log
as if makefile is a target
this error disappears when I add a character or more before %:
T%: compile_design
compile $#_tb.vhd >> log_file.log
simulate $#
This works but implies that all my targets starts with "T" which is not always the case.
My questions are:
what's exactly the function of % here ?
How to get rid of this error?
As suggested, I added
makefile: ; $#:
at the end, so I have now:
TESTS=$(shell cat yoursourcefile)
all: $(TESTS)
%: compile_design
compile $#_tb.vhd >> log_file.log
simulate $#
makefile: ; $#:
then when I do:
make all
I get [all] error2 all_tb.vhd >> log_file.log
but all_tb.vhd does not exist !
The %: compile_design rule is a "match-anything" pattern rule. It says "hey make, if you ever want to build any file, with any name, then you can do it by running these commands. Oh and by the way, if you have a file you want to build and it's older than the compile_design file, then you need to rebuild it". Generally you want to avoid match-anything rules, but if your target names truly have no specific pattern, you can't.
When you add the T before it then it tells make that instead of any file, that rule can only build files that begin with T.
The reason make is trying to rebuild the makefile is that GNU make has a special feature that allows it to remake its own makefiles. So after it reads its makefile it will try to re-make it. Normally this has no effect because there's no rule to build a makefile, but here you've added a rule that you've told make can build anything. Adding the T keeps the pattern from matching Makefile because Makefile doesn't begin with T.
The simplest thing for you to do is define an explicit rule for the makefile: make always chooses an explicit rule, if it exists, over an implicit rule like a pattern rule:
Makefile: ; #:
This creates an explicit rule that does nothing (: is the shell built-in command that does nothing).

Resources