I believe this does what you asked for:
SHELL := /bin/bash
define STUFF
$(1)_%: $(1)1_% $(1)2_% $(2)
cat $$^ > $$@
endef
$(eval $(call STUFF,a))
$(eval $(call STUFF,b,%_b3))
How this works:
The general form of the rule is defined as
STUFF
. (You'd obviously want a better name in your own Makefile.) Note the doubling of dollar signs in$$^
and$$@
. This protects them from evaluation when$(call ...)
is executed.$(1)
and$(2)
will be replaced by$(call ...)
with positional arguments.$(call STUFF,a)
"calls"STUFF
with$(1)
set to the stringa
and$(2)
set to the empty string. The return value is:a_%: a1_% a2_% cat $^ > $@
Note how one
$
was stripped from the remaining variables.$(eval ...) evaluates the return value obtained in the previous step as if that string had been put in the Makefile. So it creates the rule.
Steps 2 and 3 also happen for the b
files. It is similar to what happens for the a
files except that this time $(2)
is set to the string %_b3
.
This is essentially the method I've used in the past to avoid duplication of rules for cases where the rules were rather complex. For the specific case you show in your question, I'd use the shared command variable you mention in your question.