I have the following makefile I use to generate files from some templates, the generated files have two possible extensions:
%.tex: %.tex*_tpl
./generate
Seems to me this does what you are looking for:
#
# I've assumed that files of the form:
#
# a.xml_tpl
# b.tex_tpl
#
# determine what targets you want to build
#
TARGETS:=$(patsubst %_tpl,%,$(wildcard *.xml_tpl *.tex_tpl))
.PHONY: all
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): %: $$(wildcard %*_tpl)
./generate $^ -o $@
The key is to use .SECONDEXPANSION to allow $$(wildcard %*_tpl)
to be evaluated in a second expansion phase. The double $
is not a typo, by the way; it protects the expression from being evaluated at the time of the first expansion.
If I populate a directory with these files:
a.tex-subpart1_tpl
a.tex_tpl
a.xml-subpart1_tpl
a.xml-subpart2_tpl
a.xml_tpl
and run make -n
, I get this on the console:
./generate a.xml_tpl a.xml-subpart1_tpl a.xml-subpart2_tpl -o a.xml
./generate a.tex_tpl a.tex-subpart1_tpl -o a.tex
Without the second expansion, you'd have to have $(wildcard %*_tpl)
in the dependency because with the $$
the wildcard function would never execute. Instead, make would treat $$(wildcard..)
literally as the dependency, which is obviously wrong.
Ok, so $(wildcard %*_tpl)
would be evaluated at the time make first runs across that line (this is the "first expansion"). At that time %
has no value yet so wildcard
would roughly be doing something like what would be ls %*_tpl
at the command line.
For reasons of speed, make does not by default give you the opportunity to do any evaluation later than during the first expansion. If you want a later opportunity you have to specify .SECONDEXPANSION
, which turns on the second expansion processing. Make still performs the firts expansion as usual. This is why you need to have $$(wildcard
: it is transformed to $(wildcard
during the first expansion. At the time of the second expansion make sees $(wildcard %*_tpl)
, replaces %
with the actual stem and then executes the wildcard
function with the actual stem rather than with a literal %
.
$(TARGETS)
in the Pattern Rule?The pattern rule could be written:
%: $$(wildcard %*_tpl)
./generate $^ -o $@
without $(TARGETS)
. However, this rule would do nothing, as it would be a "match-anything rule". Basically, if make took such a rule at face value, then the computation cost would be significant, and most likely it is not the case that the author of the Makefile
really means to apply this rule to any file whatsoever. So such a rule comes with restrictions, which in the Makefile here make it useless.
Adding $(TARGETS)
makes it into a static pattern rule, which is not a match-anything rule. The addition of $(TARGETS)
in front of the target pattern tells make that the rule applies only to these targets, and nothing else.