Simple pattern rule in Makefile not always matched - makefile

I have this very simple Makefile to create plots from tab-separated data files:
%s.png: %s.tsv
Rscript make-plots.r $< $#
I have a file genus.tsv from which I want to make a plot. This is done as follows:
$ make -v
GNU Make 3.81
$ make genus.png
Rscript make-plots.r genus.tsv genus.png
That works as expected. Here comes the strange part. If I create a copy test.tsv from genus.tsv, and I try to make a plot from that, it fails for some reason:
$ cp genus.tsv test.tsv
$ make test.png
make: *** No rule to make target `test.png'. Stop.
The same happens with any other .png target I've tried. I expect the pattern rule to match any .png target. Why doesn't this work?

Related

GNU make rule order/preference

I have two wildcard rules like so:
%important_results/output.pdf: input2.o
SETTING=$* myimportantcommand > $#
%results/output.pdf: input1.o
SETTING=$* mycommand > $#
with simple prerequisites:
input1.o: input1.c
touch input1.o
input2.o: input2.c
touch input2.o
However, I am getting varying behaviour when I run make important_results:
if input2.o exists, it uses the %important_results rule
if input2.o does not exists, but input1.o exists, it uses the %results rule instead of building input2.o. However, this runs the wrong command.
How can I give make a rule preference, so it always goes with %important_results rule even if it has to build prerequisites?
Searching for "gnu make rule order" or preference did not yield anything.
I suppose the simplest solution is to use non-overlapping wildcards.
edit: https://www.gnu.org/software/make/manual/html_node/Pattern-Match.html suggests that stems cannot be applied to directories in this way.
GNU make manual:
The target is considered a pattern for matching file names; the ‘%’
can match any nonempty substring, while other characters match only
themselves.
Try the following, maybe:
%mportant_results/output.pdf: input2.o
SETTING=$(patsubst %i,%,$*) myimportantcommand > $#
Demo:
$ ls
Makefile input1.c input1.o input2.c
$ make important_results/output.pdf
touch input2.o
SETTING= myimportantcommand > important_results/output.pdf

Make: wildcard to match full path?

Using make for generic purposes (not compiling)
Suppose I have a set of files with full path names, and I would like to do something with that file.
something --file a/b/c/X > $< # (for example)
And I have made a rule:
something-%:
something --file $*
Which matches fine against, say, "something-foo" but does not catch "something-a/b/c/foo". Is there a way to write a wildcard rule for this latter case?
The manual describes how patterns match:
When the target pattern does not contain a slash (and it usually does not), directory names in the file names are removed from the file name before it is compared with the target prefix and suffix.
In your case when calling as make something-a/b/c/foo, something-a/b/c/ is treated as directory and removed, so the rest does not match your rule. This can be easily checked:
$ cat Makefile
something-%:
echo something --file $<
f%o:
echo $*
Output:
$ make something-OtherDirectory/src/foo -dr
GNU Make 4.2.1
...
Considering target file 'something-OtherDirectory/src/foo'.
File 'something-OtherDirectory/src/foo' does not exist.
Looking for an implicit rule for 'something-OtherDirectory/src/foo'.
Trying pattern rule with stem 'o'.
Found an implicit rule for 'something-OtherDirectory/src/foo'.
Finished prerequisites of target file 'something-OtherDirectory/src/foo'.
Must remake target 'something-OtherDirectory/src/foo'.
echo something-OtherDirectory/src/o
...
Note that it matched the other pattern rule with the stem of o.
You can make it work your way if your pattern does include a slash. For sake of completeness I would also define a prerequisite if your rule is based on a file and declare target as phony if it does not generate a real output file:
$ cat Makefile
.PHONY: something/%
something/%: %
echo something --file $<
Output:
$ make something/OtherDirectory/src/foo.c
echo something --file OtherDirectory/src/foo.c
something --file OtherDirectory/src/foo.c

Makefile with multiple output rule not rebuilding nested dependency when run in parallel

I have a makefile with a rule that produces multiple outputs. To work around the issue of this rule often being run multiple times when run in parallel, I've used a dummy "timestamp file". I also have a rule that depends on the one of the outputs of this "multi-output" rule.
When all this is run from a clean state, it all works fine. However, if the source of the multi-output rule is updated, the other rule is not run, until Make is run again.
I've looked at the debug output, but haven't been able to make much headway. It almost seems like Make might be caching the old timestamp of the previous version of the multi-output file?
Hopefully the below demonstrates the problem adequately.
$ cat Makefile
all: data.txt
multioutput.stamp: sourcefile.txt
touch multioutput1.txt
touch multioutput2.txt
touch $#
FILES=multioutput1.txt multioutput2.txt
$(FILES): multioutput.stamp
data.txt: multioutput1.txt
touch data.txt
$ touch sourcefile.txt
$ make
touch multioutput1.txt
touch multioutput2.txt
touch multioutput.stamp
touch data.txt
$ touch sourcefile.txt # update
$ make # data.txt is not updated!!
touch multioutput1.txt
touch multioutput2.txt
touch multioutput.stamp
$ make # except when it's run again??
touch data.txt
What am I doing wrong here, and what should I be doing instead?
You are lying to make. Don't do that.
Once you have run the recipe of a rule, make checks to see if a file has actually been updated by the recipe. If it has not changed, you don't have to re-make any target that lists the file as a dependency.
Here you have given no recipe for multioutput1.txt, just a dependency line:
multioutput1.txt: multioutput.stamp
Make knows there is no way to update multioutput1.txt.
Cheap fix
Force make to check the dependency by supplying an explicit recipe for multioutput1.txt.
Even an empty one will do:
${FILES}: multioutput.stamp ;
Yep, that's what the ; signifies — the first line of the recipe follows on the same line.
Better fix
The only way of saying to make "this recipe creates two files" is with a pattern rule.
Then there is no need for a multioutput.stamp.
.PHONY: all
all: data.txt
%1.txt %2.txt:
touch $*1.txt
touch $*2.txt
data.txt: multioutput1.txt multioutput2.txt
touch data.txt
Here $* in the recipe expands to whatever the % matched in the dependency line.
Why have I made data.txt depend on both multioutput files?
Here I took the view that if either of multioutput1 or multioutput2 were missing, we should probably run the recipe to create both.
YMMV.
Best Fix
YMMV but I don't like pattern rules.
They are too arbitrary for my tastes.
We observe that one of multioutput1.txt and multioutput2.txt will always be younger than the other.
They will never have the same timestamp assuming a modern filesystem.
.PHONY: all
all: data.txt
multioutput2.txt: start.stamp
touch $#
touch multioutput2.txt
multioutput1.txt: multioutput2.txt ;
data.txt: multioutput1.txt
touch data.txt

Makefile match wildcard prerequisites for a rule

I want to pass some recipes to a makefile, like comp1.mt comp2.mt comp3.mt and have the makefile agregate them into a single list of MTs (it could also be mt.compN) to run them in bulk.
%.mt:
#echo $* >> list_of_mts.txt
mt: %.mt
#cat list_of_mts.txt
I want to pass these recipes in any order to the makefile so the specific case of calling make comp1.mt comp2.mt mt is not desirable.
I do not understand why you need this implemented in makefile, but here you are:
MT_TARGETS:=$(filter %.mt,$(MAKECMDGOALS))
.PHONY: $(MT_TARGETS)
$(MT_TARGETS):
#echo $# | sed -e "s:.mt$$::" >> list_of_mts.txt
mt: $(MT_TARGETS)
#cat list_of_mts.txt
Testing:
$ make mt comp1.mt comp2.mt comp3.mt
comp1
comp2
comp3
make: `comp1.mt' is up to date.
make: `comp2.mt' is up to date.
make: `comp3.mt' is up to date.
This is using special variable MAKECMDGOALS.
Note: list_of_mts.txt will grow endlessly...
Note2: writing to list_of_mts.txt is unsafe in parallel execution (the list_of_mts.txt file may get corrupted).

Makefile pattern rule with variable in target

I'm using Gnu Make 3.81, and getting an error trying to match a pattern rule that also has a variable in it.
Here's the smallest example I could come up with:
YYMMDD:=$(shell date +%y%m%d)
TMP_DIR:=/tmp/$(YYMMDD)
# create a temporary directory, and put a "source" file in it
$(TMP_DIR):
mkdir $(TMP_DIR)
echo something > $(TMP_DIR)/somefile.orig
# to build an "object" file in the temp dir, process the "source" file
$(TMP_DIR)/%.new: $(TMP_DIR)/%.orig
wc -l $< > $#
atarget: $(TMP_DIR) $(TMP_DIR)/somefile.new
Then when I run make atarget, I get:
mkdir /tmp/141021
echo something > /tmp/141021/somefile.orig
make: *** No rule to make target `/tmp/141021/somefile.new', needed by `atarget'. Stop.
Shouldn't this work? it seems like the pattern rule should match this just fine.
It's because make doesn't know that the .orig file exists: you have a rule that builds $(TMP_DIR) but make doesn't know that this rule also builds $(TMP_DIR)/somefile.orig. So when make is trying to match the pattern rule it will see that the .orig file doesn't exist and it doesn't have any way that it knows how to make that file, so the pattern doesn't match, and after that there's no way to build the .new file.
You should write:
$(TMP_DIR)/%.orig:
mkdir -p $(TMP_DIR)
echo $* > $#
then it will work.

Resources