Special case pattern rules in Makefile - makefile

I have a generic rule for files with a given extension, but I want to add special cases for files with a prefix. I tried this:
special-%.dat: special-%.raw
echo "Running rule 1"
%.dat:
echo "Running rule 2"
but when I run make special-1.dat, it ignores the first rule and runs the second. Switching the order of the rules makes no difference. How can I fix this?

Does the file special-1.raw exist? If that file doesn't exist (and you have no rule that can create it) make will never choose this rule because the prerequisite is not available. That's not an error in itself: only if make can find NO rule that can build this target will it fail.
Running make -d will show you exactly how make is considering every target and its prerequisites, recursively.

Related

Target dependency: Makefile no rule to make target error

Here is the make file that I am running,
.PHONY: build
build: pre_build_script $(OUTPUTDIR)/%.cpp
$(OUTPUTDIR)/%.cpp: $(INTXTDIR)/%.txt
python.exe $(SOMEDIR)/somepythonscript.py $(INTXTDIR) $(OUTPUTDIR)
.PHONY: pre_build_script
pre_build_script:
pythonscript.exe $(PREBUILDDIR)
This is the output that I get:
$ make build
pythonscript.exe $(SAMPLEDIR)
make: *** No rule to make target '../obj/CPP/%.cpp', needed by 'build'. Stop.
Looks like I'm missing on some sytanx as I get this error inspite of declaring the target dependency. Any suggestions?
This means make cannot find a file named $(OUTPUTDIR)/%.cpp, a prerequisite for the first rule.
You cannot use % as a wildcard anywhere in a rules like this:
build: pre_build_script $(OUTPUTDIR)/%.cpp
it needs to be a part of pattern rule or a static pattern rule.
You can use $(wildcard $(OUTPUTDIR)/*.cpp) to get a complete list of files, but it's an anti-pattern (pun intended). You are supposed to either exactly know what files are used in what rules, or (know it even better and) create a generic pattern rule.
The second pattern rule (one using somepythonscript.py) is supposed to work on a single source-target file pair, $(INTXTDIR)/%.txt -> $(OUTPUTDIR)/%.cpp. The command seems to process all the files in the directory, which is not incremental: it will redo all the work even if only one file was updated.

Makefile rule only works if file exists before make is invoked

Consider the following (MCVE of a) Makefile:
my_target: prepare test.bin
prepare:
echo >test.dat
%.bin: %.dat
cp $? $#
If you run make in a clean directory, it fails:
echo >test.dat
make: *** No rule to make target 'test.bin', needed by 'my_target'. Stop.
Run it again and it succeeds:
echo >test.dat
cp test.dat test.bin
What seems to happen is that the rule to make *.bin from *.dat only recognises that it knows how to make test.bin if test.dat exists before anything is executed, even though according to the output it has already created test.dat before it tries to create test.bin.
This is inconvenient for me as I have to prepare a few files first (import them from a different, earlier part of the build).
Is there a solution? Perhaps some way to allow the rules to be (re)evaluated in the light of the files which are now present?
There are a number of issues with your makefile. However based on your comments I'm inclined to assume that the MCVE here is just a little too "M" and it's been reduced so much that it has a number of basic problems. So I won't discuss them, unless you want me to.
The issue here is that you're creating important files without indicating to make that that's what you're doing. Make keeps internally a cache of the contents of directories that it's worked with, for performance reasons, and that cache is only updated when make invokes a rule that it understands will modify it.
Here your target is prepare but the recipe actually creates a completely different file, test.dat. So, make doesn't modify its internal cache of the directory contents and when it checks the cache to see if the file test.dat exists, it doesn't.
You need to be sure that your makefile is written such that it doesn't trick make: if a recipe creates a file foo then the target name should be foo, not bar.
This happens for wildcard targets, like %.bin. They get evaluated at the first pass. You could add an explicit target of test.bin. Or, follow the advice of tkausl and have test.dat depend on prepare (a phony target). In this case, you don't need the double dependency anymore:
my_target: test.bin
you have to write
test.dat: prepare
or (when when you want to stay with wildcards)
%.dat: prepare
#:
Usually, you might want to create and use .stamp files instead of a prepare target.

GNU Make -- Pattern-Matched Dependencies

Let the file "prefix-hi.c" being present in the current directory ("> touch prefix-hi.c"). Then, create the following Makefile:
prefix-%.o: prefix-%.c prefix-%-generated.c
#echo Specific Rule
%.o: %.c
#echo General Rule
prefix-%-generated.c:
touch prefix-$*-generated.c
Making in two steps gives the sequence
> make prefix-hi-generated.c
touch prefix-hi-generated.c
> make prefix-hi.o
Specific Rule
Deleting the generated file and trying to build in one step results in
> rm -f prefix-hi-generated.c
> make prefix-hi.o
General Rule
That is, GNU Make does not recognize the opportunity to build "prefix-hi-generated.c" from the last rule. Adding an explicit rule
prefix-hi-generated.c:
touch prefix-hi-generated.c
changes everything. Now the one-step sequence results in
> rm -f prefix-hi-generated.c
> make prefix-hi.o
touch prefix-hi-generated.c
Specific Rule
From my angle, this behavior seems quirky.
Is there a rational explanation for things being the way they are?
How could one force GNU Make to apply the 'Specific Rule' for files starting with "prefix-" without using explicit rules?
This is explained fully in the GNU make manual
Specifically:
Note however, that a rule whose prerequisites actually exist or are mentioned always takes priority over a rule with prerequisites that must be made by chaining other implicit rules.
This is an explicit exception to the general rule that the most narrowly matching (shortest stemmed) rule will always be caused to match. The reason your last example works how you like, is because it isn't a pattern matching based rule at all so is checked before pattern matching is resolved.

Rule with wildcard to depend on other rules [duplicate]

I'm observing an interesting behavior of make and I wonder if there is a reasonable explanation to it besides a bug in gmake.
Let's say we have the following in makefile:
%-animal:
echo "$* is an animal"
%-fox: %-fox-animal
%-wolf: %-wolf-animal
The difference between the last two targets is that "%-wolf" does not have any recipe, and "%-fox" has an empty recipe (i.e. just a line with a tab at the beginning).
When we try to execute the rules, here's what happens:
[root#cv19 tmp]# make freddy-animal
echo "freddy is an animal"
freddy is an animal
[root#cv19 tmp]# make freddy-wolf
make: *** No rule to make target `freddy-wolf'. Stop.
[root#cv19 tmp]# make freddy-fox
echo "freddy-fox is an animal"
freddy-fox is an animal
i.e.the pattern rule that has a recipe (although an empty one) works, the one that doesn't does not. Am I missing something in the way it's supposed to work?
Pattern rules with no recipes at all are documented as meaning something quite different from those providing a recipe, even an empty one. Instead they cancel any pre-existing implicit rule:
You can cancel a built-in implicit rule by defining a pattern rule with the same target and prerequisites, but no recipe.
Thus your "%-wolf" pattern actually serves to cancel any existing implicit rule for %-wolf-animal -> %-wolf. And there wasn't one anyway.

Get error for "make: Nothing to be done for 'target'"

Let me illustrate it with an example.
mkdir test
cd test
touch target
make target
This will result in: make: Nothing to be done for 'target'.
So make tells me there is nothing to do. This is because make did not find a rule to make target, but because the target already exists make tells me there is nothing to do.
Now, I don't want that. I want make to give me an error when it cannot find a rule for target, even though the target already exists.
I have tried the following:
echo '.DEFAULT:
echo make: *** No rule to make target `$#'. Stop.
false'> Makefile
But this does not stop the make when making multiple targets.
The problem is, that make assumes the target name is also a file which will be build by the given commands.
But sometimes this is not true (e.g. think of "clean").
To tell make that some targets don't build this file, you need to make them "phony". Put the following line into your Makefile:
.PHONY: target
If you think about it, you would end up with a chicken-and-egg situation (or infinite regress). Suppose you managed to have a rule that said 'You must have a rule to create target before it is legitimate', then you'd have a rule that says 'target depends on X' for some other file X. That's written:
target: X
command to build target from X
But then you'd be back to the starting point: you'd also want a rule to create X. How can you do that? You might have a rule that depends on nothing and magically creates the file X when it is needed:
X:
command to build X from nothing
Without a rule like that, you have an infinite regress.
So, make is designed to ensure that files exist and are up to date. If a file exists and there are no rules - implicit or explicit - to specify how it is made, then the file is up to date. What you are seeking to do is not possible.
Actually this sounds like a perfectly reasonable (if misguided ;-)) request. You will have to explicitly list every source file with a rule with an empty recipe though.
Here you go:
Makefile: ;
.PHONY: dodgy
dodgy%: dodgy; $(error You must provide a rule for $*)
%: dodgy% ;
The proof of the pudding:
$ rm aa
$ make aa
Makefile:4: *** You must provide a rule for aa. Stop.
$ touch aa
$ make aa
Makefile:4: *** You must provide a rule for aa. Stop.
Note that the line Makefile: ; is necessary. After all, the first thing make tries to do is rebuild the Makefile.
Note also that the catch-all pattern rule is non-terminal. This can be a massive performance hit. As the manual says about match anything rules "They are very useful, but it can take a lot of time for make to think about them."

Resources