I use GNU make for building reports (LaTeX for source, python for figures, etc.). For targets, I use extensively pattern matching, for example:
all : sample1_test1.png sample2_test1.png sample2_test.png
sample%_test1.png : sample%.dat
python gen_figure.py $< $@ --test 1
sample%_test2.png : sample%.dat
python gen_figure.py $< $@ --test 2
Now, to simplify the rules I would like to use multiple pattern groups (like regex groups) and use the matches separately in the build rule, for example:
all : sample1_test1.png sample2_test1.png sample2_test.png
sample(?P<Sample>[0-9]+)_test(?P<Test>[0-9]+).png : sample$(<Sample>).dat
python gen_figure.py $< $@ --test $(<Test>)
(the syntax is arbitrary, but the point is that I can define two different match groups called Sample
and Test
and use them as parameters to my script).
How would I achieve this in make or another build system (waf, scons etc.)?
To do it in GNU make, you can use one of two different "metaprogramming" models supported by GNU make:
Auto-generated include files. In your main makefile, add
-include generated.mk
then write a makefile rule with the targetgenerated.mk
(probably listingMakefile
as a prerequisite), where the recipe generates the appropriate targets based on the list of targets. You have the full power of the shell to construct your target lists however you want. Every time you modify the makefile, the included file will be rebuilt then GNU make will automatically re-exec itself so you don't have to do anything extra.Use GNU make's
$(eval ...)
function, probably combined with$(call ...)
and$(foreach ...)
, to automatically evaluate rules. To do this you define a "template" for the rule usingdefine ... enddef
, with variables installed where you want to provide arguments, then use$(call ...)
to instantiate them, use$(eval ...)
on the result of thecall
, and do it in a loop for each target. Something like:$(foreach T,$(TARGETS),$(eval $(call DEFINERULE,$(T))))
Here's an example of method 1. Suppose you have this predefined content in your makefile:
Then you can use this makefile to get something like the above:
Note I just wrote this off the top of my head but I think it will work.