Question

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.)?

Était-ce utile?

La solution

To do it in GNU make, you can use one of two different "metaprogramming" models supported by GNU make:

  1. Auto-generated include files. In your main makefile, add -include generated.mk then write a makefile rule with the target generated.mk (probably listing Makefile 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.

  2. 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 using define ... enddef, with variables installed where you want to provide arguments, then use $(call ...) to instantiate them, use $(eval ...) on the result of the call, 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:

TESTS := sample1_test1.png sample2_test1.png sample2_test.png

Then you can use this makefile to get something like the above:

all: $(TESTS)

-include generated.mk

generated.mk : Makefile
        @rm -f '$@'
        @for t in $(TESTS); do \
            eval `echo "$$t" | sed 's/^sample\([0-9]*\)_test\([0-9]*\).*/sample=\1 test=\2/'`; \
            echo "$$t : sample$$sample.dat ; python gen_figure.py \$$< \$$@ --test $$test" >> '$@'; \
         done

Note I just wrote this off the top of my head but I think it will work.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top