Make: wildcard to match full path? - makefile

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

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

Why does my Makefile pattern rule run its recipe multiple times?

As per the gnu make documentation, a pattern rule's "...recipe is executed only once to make all the targets." However, I have the following Makefile
.PHONY: entrypoint
entrypoint: test_1.cpp test_2.cpp
test_%.cpp:
echo $#
And running make produces:
echo test_1.cpp
test_1.cpp
echo test_2.cpp
test_2.cpp
I'm new to make, and I'm probably misunderstanding something, but the documentation seems misleading if clear.
$ make -v
GNU Make 4.0
...
You're misreading the documentation. It means, the recipe is run only one time assuming that all the target patterns in that rule will be created.
Since you have only one target pattern in your rule (test_%.cpp`) make knows that each time it runs that recipe it will create one output file matching that pattern. To create different targets that match that pattern it will run multiple instances of the recipe.
If you had a rule like this:
%.x %.y %.z :
dothings
then make would expect that a single invocation of the recipe dothings would create all the targets matching this pattern (e.g., foo.x, foo.y, and foo.z).
Contrast this with an explicit rule like this:
foo.x foo.y foo.z :
dothings
Make here treats this exactly as if you'd written this:
foo.x :
dothings
foo.y :
dothings
foo.z :
dothings
That is, to build all three of these targets it would run the recipe three times.
There's no way to tell make "please run this recipe one time and it will produce every single target that could possibly match the pattern foo_%.cpp".
The following functions construct a dynamic list of dependencies of your multiple-target where the non-existent files are named last. This is more or less the method named "Another attempt" in the link you gave, except that it doesn't trip over missing files and is able to make a missing file by giving it as target on the command line. What it does not: execute the multitarget recipe if one of the multitargets is out of date relative to the others, but I think this is more of wanted side effect than a problem. The only drawback is the syntactic ugliness as you have to write it into an eval expression which forces you to quote all variables in the recipe which shall be evaluated at execution time.
define newline :=
endef
list2rules = $(firstword $1) $(if $(word 2,$1),: $(word 2,$1)$(newline)$(call list2rules,$(wordlist 2,1000,$1)))
multitarget = $(call list2rules,$(wildcard $1) $(filter-out $(wildcard $1),$1))
.PHONY: all
targets = test1 test2 footest3
#$(info $(call multitarget,$(targets)))
all: somefile
somefile: $(targets)
touch somefile
# here we generate the dependency list on the spot. Only one recipe to update all targets.
$(eval $(call multitarget,\
$(targets)) : ; \
touch $(targets) \
)

Makefile secondary expansion: execution of escaped function?

Consider the following makefile:
.SECONDEXPANSION:
%.result: jobs/% $$(call get-job,$$<)
echo $^
define get-job
$(shell head -n 1 $(1))
$(shell tail -n +2 $(1))
endef
The idea is that each file under jobs/ contains a list of filenames, which should be appended to the prerequisite list.
However, if I want to create xyz.result from an existing file jobs/xyz, I get the following error message:
$ make -n xyz.result
head: cannot open 'xyz.result' for reading: No such file or directory
tail: cannot open 'xyz.result' for reading: No such file or directory
head: cannot open 'xyz.result' for reading: No such file or directory
tail: cannot open 'xyz.result' for reading: No such file or directory
make: *** No rule to make target 'xyz.result'. Stop.
I am aware that $$< isn't set to what I want, as it reflects the prerequisite list of any previous rule.
What I don't understand is the following:
In my understanding, $$< should evaluate to the empty string (as shown in the example in the official doc, under the second sub-heading). However, it seems to be expanded to the value of the target here (xyz.result). Why is that?
It seems that the get-job function is called twice (head and tail both bark twice). I understand that the prerequisite list is expanded twice. But in the first run, the call is still escaped, so this isn't what I expect.
(Maybe the whole approach is flawed, and I shouldn't be (ab)using Makefile for this kind of task in the first place.)
I don't know why $$< expands that way.
You can make your configuration work by using $$* instead, though:
%.result: jobs/% $$(strip $$(call get-job,jobs/$$*))
echo $^
You need to add the strip call so that the newlines embedded in the results of the call will be turned into spaces.

In make, can we use $^ to get the dependency list for just the current rule?

I've been learning make from the software carpentry tutorial (make patterns) and it says that we can use $^ to get the dependency list for our rule but have extra dependencies for our target by writing extra empty rules. For example
all:
touch f1.txt f2.txt a.txt
result: a.txt
result: f*.txt
#echo $^
I thought that this would print f1.txt f2.txt but instead I see f1.txt f2.txt a.txt. Am I missing something or is the tutorial wrong?
Clearly, I should have been explicit, in the example I run make to create the files for the test, then I run make result to get the result shown.
Not sure I understand your problem and what you would like to do but if you type just make it is like if you were typing make all because all is the first target and thus the default goal. It should create the 3 files (or update their last modification date if they already exist), echo the recipe and you should see:
touch f1.txt f2.txt a.txt
If you then type make result you should see the result of #echo $^, that is the list of all dependencies of result:
f1.txt f2.txt a.txt
It is absolutely normal as you declared them all as dependencies of result.
Finally, if you type make result before make or make all and if a.txt does not exist or if there is no file matching f*.txt, you will get an error because make needs a.txt and at least one file matching f*.txt to make result and it does not know how to make them.
I cannot imagine a use case where the behaviour you expected ($^ being expanded as dependencies of the current rule only) would be useful, but if really you need this feature you can use the rather obscure Double-Colon Rules (DCR):
all:
touch f1.txt f2.txt a.txt
result:: a.txt
#echo $^
result:: f*.txt
#echo $^
Note that if there is a DCR for a target, all rules of this target must also be DCR. Note also that all DCR must have a recipe. A DCR recipe is applied if one of the dependencies of this rule is newer than the target. If several DCR apply, they are executed in order of appearance in the Makefile. And finally, what you were interested in: the $^ automatic variable logically expands as the dependencies of the particular DCR.
No, $^ refers to the prerequisites for the current target, not the current rule.
Also, the results you are reporting are not correct; for make result I get make: *** No rule to make target `f*.txt', needed by `result'. Stop. like I very much expected.

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