0
votes

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
...
2
Where do you see the "multiple times"? Also, your recipe isn't making the target, which will lead to a re-execution of the recipe every time, as long as there are no test_1.cpp and test_2.cpp present. - Vroomfondel
The output indicates that echo is being invoked twice-once for each file. This is a minimal reproduction, so re-execution isn't the concern here. - Brent
echo test_1.cpp is not the output of the echo command but the output of make itself - by default the shell echos everything it is executing. This is more or less as if you had typed it on the console. You can suppress this behaviour by pegging a @ in front of every shell line: @echo $@ - Vroomfondel
You seem to be missing the point of this post. The command that I'm running, and its output are irrelevant. All that matters is that its running the recipe multiple times. - Brent
As your output indicates, it is not - it seems you missed the point by giving an irrelevant example. - Vroomfondel

2 Answers

4
votes

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

0
votes

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