I am using makefiles to manage dependencies in computational experiments. It would very often be useful to have targets with multiple patterns.
For example, I may have some conditions, say A and B, and different folds (i.e. splits of data) of a cross-validation experiment, say 1 through 10.
Currently I just copy and paste the same commands for A and B, and pattern-match on the folds, but if there are more than two conditions it quickly becomes a maintenance problem.
output-%.A: input-%
run A input-$* output-$*.A
output-%.B: input-%
run B input-$* output-$*.B
Is there a better way of doing this in makefile? If not, what other tool would solve this issue?
2 Answers 2
One drawback of Make is that it doesn't handle wildcards well. But there is a way to minimize the redundancy in this makefile.
First, let's use automatic variables to clean up the rules a little bit:
output-%.A: input-%
run A $< $@
output-%.B: input-%
run B $< $@
Then we define a rule template, use call to evaluate it for A and for B, and eval to interpret those things as actual rules:
define fold-rule
output-%.$(1): input-%
@echo run $(1) $$< $$@ # <-- note the doubled '$'
endef
$(eval $(call fold-rule,A))
$(eval $(call fold-rule,B))
Finally we put the conditions in a variable, and iterate using foreach:
CONDITIONS := A B
define fold-rule
output-%.$(1): input-%
@echo run $(1) $$< $$@
endef
$(foreach x, $(CONDITIONS), $(eval $(call fold-rule,$(x))))
Now when you want to add a new condition, just modify the first line.
It isn't pretty, but it works.
2 Comments
Using a few features of makepp, like using variable as macro-functions, and early evalution with $[] it is easy:
define runner
output-%.1ドル: input-%
run 1ドル $$(input) $$(output)
enddef
$[runner A]
$[runner B]
$[runner C]
If you want to create a lot of rules, this may still be too verbose. Problem with the foreach function is that it joins the results with a space. So to separate the rules, you can put a dummy empty expression the last line:
define runner
output-%.1ドル: input-%
run 1ドル $$(input) $$(output)
$()
enddef
$[foreach type,D E F,$[runner $[type]]]