1

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?

asked Nov 28, 2013 at 11:20

2 Answers 2

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.

answered Nov 28, 2013 at 14:10
Sign up to request clarification or add additional context in comments.

2 Comments

I disagree that "it isn't pretty". It is exactly the right thing to do and is as pretty as it is going to get.
Thanks for both good answers. This one is plain make so I'm going to accept it.
1

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]]]
answered Nov 28, 2013 at 14:13

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.