21

CODE:

LIST=0 1 2 3 4 5
PREFIX=rambo

# some looping logic to interate over LIST

EXPECTED RESULT:

rambo0:
    sh rambo_script0.sh

rambo1:
    sh rambo_script1.sh

Since my LIST has 6 elements, 6 targets should be generated. In future, if I want to add more targets, I want to be able to just modify my LIST and not touch any other part of the code.

How should the looping logic be written?

Lazer
  • 90,700
  • 113
  • 281
  • 364

3 Answers3

32

If you're using GNU make, you can generate arbitrary targets at run-time:

LIST = 0 1 2 3 4 5
define make-rambo-target
  rambo$1:
         sh rambo_script$1.sh
  all:: rambo$1
endef

$(foreach element,$(LIST),$(eval $(call make-rambo-target,$(element))))
Idelic
  • 14,976
  • 5
  • 35
  • 40
  • Not the most elegant solution for the question that was asked, but this *is* the answer I was looking for: a way to generate targets at run-time. – EvertW May 15 '19 at 10:03
  • This is terribly inefficient for the question asked, but a very good example if this is what you actually want to do. – BUFU Nov 13 '20 at 10:42
  • @BUFU what's the terrible inefficiency? – Idelic Nov 13 '20 at 14:03
  • @'idelic' Defining a macro which defines multiple specific targets instead of a pattern rule for all those (systematically named) targets. – BUFU Nov 15 '20 at 16:45
  • 1
    How is that inefficient? Besides, the question is not how to use pattern rules with systematically named targets, but how to generate those targets programatically. – Idelic Nov 16 '20 at 20:22
  • @Idelic It is inefficient by typing 7 lines instead of 4 and by eval'ing and calling a macro however many times instead of letting make decide which rules are actually needed. If you use this construct for a million rules, it will take forever for make to actually start doing anything, because it will spend quite some time just constructing rules that are never called. With a simple pattern rule this is no issue at all. – BUFU Dec 16 '20 at 11:14
23

Use text-transforming functions. With patsubst you can make quite general transformations. For constructing filenames, addsuffix and addprefix are both convenient.

For the rules, use pattern rules.

The overall result might look something like this:

LIST = 0 1 3 4 5
targets = $(addprefix rambo, $(LIST))

all: $(targets)

$(targets): rambo%: rambo%.sh
    sh $<
Michael J. Barber
  • 24,518
  • 9
  • 68
  • 88
0

Just my 2 cents to @Idelic answer, if you need to use some Make $cmd you must escape them using $$ e.g.

LIST = 0 1 2 3 4 5
define make-rambo-target
$(info create target: $(addprefix rambo_script, $(addsuffix .sh, $1)).)
rambo$1: $$(addprefix rambo_script, $$(addsuffix .sh, $1))
    sh $$<
endef

all: $(addprefix rambo, $(LIST))

$(foreach element, $(LIST), $(eval $(call make-rambo-target,$(element))))

output:

$ make
create target: rambo_script0.sh.
create target: rambo_script1.sh.
create target: rambo_script2.sh.
create target: rambo_script3.sh.
create target: rambo_script4.sh.
create target: rambo_script5.sh.
sh rambo_script0.sh
sh rambo_script1.sh
sh rambo_script2.sh
sh rambo_script3.sh
sh rambo_script4.sh
sh rambo_script5.sh

note: here rules "are seen" by Make as

rambo0: $(addprefix rambo_script, $(addsuffix .sh, 0))
    sh $<

But here we could have written without escaping i.e.

rambo$1: $(addprefix rambo_script, $(addsuffix .sh, $1))
    sh $$<

So rule "are seen" as:

rambo0 : rambo_script0.sh
    sh $<

when Make parse it.

Mizux
  • 8,222
  • 7
  • 32
  • 48