dannedanne
dannedanne

Reputation: 103

Generalized makefile rules with multiple variables on both left and right hand side

What is the best way of producing a large number of files with different variable elements in the name? If possible would like to use one rule to generate all of the files.

In my case I specifically want to generate many animated GIF files from sets of PNG frames of the type animation_A_01.gif, which would depend on frame_A_01_000.png, frame_A_01_005.png, frame_A_01_010.png, etc.

A naive solution could look something like this:

SYSTEMS = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ...

TIMEPOINTS = 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ...

ROTATIONS =  000 005 010 015 020 025 030 035 040 045 050 055 060 065 070 075 080 085 090 095 100 105 110 115 120 125 130 135 140 145 150 155 160 165 170 175 180 185 190 195 200 205 210 215 220 225 230 235 240 245 250 255 260 265 270 275 280 285 290 295 300 305 310 315 320 325 330 335 340 345 350 355

animation_%1_%2.gif: $(foreach ROT, $ROTATIONS, frame_%1_%2_$ROT.png)
    convert -delay 10 -loop 0 $^ $@

Where %1 would denote a system and %2 would denote the time. This would require GNU make to support double variables on the left-hand side of the rule, but as far as I know this is not the case.

Another option would be if you could use multiple rules to define the dependencies separately from the rule, like this:

The only way I can think of, is to have an explicit rule for each of the cases, which would be a pain. Alternatively I could write my own make script in python/bash/etc, but I would prefer to stick with GNU make if possible. I would like to know how to solve this with a Makefile from a purely academic point of view.

Upvotes: 1

Views: 497

Answers (2)

Beta
Beta

Reputation: 99084

This isn't as complicated as you think:

ROTATIONS =  000 005 010 015 ...

animation_%.gif: $(addsuffix .png, $(addprefix frame_%_,$(ROTATIONS)))
    convert -delay 10 -loop 0 $^ $@

Upvotes: 0

Norman Gray
Norman Gray

Reputation: 12514

Make is a fine tool on its home territory, but it gets rapidly cranky if it's out of its comfort zone. My golden rule for Makefiles is: keep it simple; if there's complication required, do it in the rule, not in the make syntax.

The following generates a complete set of files, once with GNU-Make magic, and once in a portable way.

LL=a b c
NN=1 2 3

files.stamp:
    for f in $(foreach L, $(LL), $(foreach N, $(NN), file-$(L)-$(N))); do echo generate $$f; done

files2.stamp:
    for l in $(LL); do for n in $(NN); do echo generate file-$$l-$$n; done; done

If instead the requirement is for a generic target, then a technique for doing that is to have a simple pattern which matches the target file, and then destructure the target in the rule. Thus:

# convert image-a-1.png to image-a-1.gif
image-%.gif: image-%.png
    echo $< | sed 's/image-\([a-z]*\)-\([0-9]*\).*/echo convert -l \1 -n \2 $</' | sh

That looks a bit mad, but the technique – using sed to construct a command which you then feed to sh – is quite a powerful one.

Upvotes: 2

Related Questions