Matthew Hoggan
Matthew Hoggan

Reputation: 7594

GNU Make for loop with two variables

I want to write something along these lines:

$(foreach (var1, var2), ($(LIST1), $(LIST2)), cp $(var1) $(var2);)

How do I go about doing this in a GNU makefile?

Upvotes: 10

Views: 11746

Answers (4)

Rami
Rami

Reputation: 166

A bit old post but I found this lets you do exactly what you need. You will need the seq utility which is a Unix utility and will be available on Linux and Mac.

I wrote the example with printing variables but it should work with copying.

# Two lists
LIST1 = a1 a2 a3 a4
LIST2 = b1 b2 b3 b4

# This will generate
# LISTSEQ=1 2 3 4
LISTSEQ=$(shell seq $(words $(LIST1)))

# Print variable for example
define printvar
$(info $(1))
$(info $(2))
endef

# First line loops through 1 2 3 4
# and next line prints the corresponding value from LIST1 and LIST2
$(foreach j, $(LISTSEQ), \
    $(call printvar,$(word $(j), $(LIST1)) $(word $(j), $(LIST2))) \
)

Output:

a1 b1

a2 b2

a3 b3

a4 b4

Upvotes: 1

wovenhead
wovenhead

Reputation: 144

This is a good candidate for gsml (GNU Make Standard Library). You can include it by putting the files __gmsl and gml in the current directory (or in /usr/gnu/include, /usr/local/include/ etc.) and adding the line include gsml in your Makefile. It includes the pairmap function, which does exactly what you want (i.e. zipWith).

include gmsl
cp2 = cp $1 $2;
zip = $1 : $2
$(LIST2):
    @echo $(call pairmap, zip, $(LIST1), $(LIST2))
    $(call pairmap, cp2, $(LIST1), $(LIST2))

Outputs

$ make 
A : 1 B : 2 C : 3 D : 4
cp A 1; cp B 2; cp C 3; cp D 4;

Upvotes: 1

MadScientist
MadScientist

Reputation: 100856

Beta's suggestion to use join is on the right track, but the problem is that it's not so easy to use it in a way that constructs a command line containing whitespace, such as the one you originally wanted:

$(foreach (var1, var2), ($(LIST1), $(LIST2)), cp $(var1) $(var2);)

because join joins words together: it was originally intended for constructing filenames. However you can play a trick; here's an example of a way to use join that gives you the output you are looking for:

$(subst ^, ,$(join $(addprefix cp^,$(LIST1)),$(patsubst %,^%;,$(LIST2))))

If you think your lists might contain ^ characters then choose something else. Let me know if you need this unpacked/explained.

Upvotes: 11

Beta
Beta

Reputation: 99094

LIST1 := a b c
LIST2 := 1 2 3

# outside a rule:
$(foreach var1, a b c, $(foreach var2, 1 2 3, $(info $(var1)_$(var2))))

# inside a rule: first line starts with a TAB, all the rest are spaces
all:
    @for x in $(LIST1);\
    do \
      for y in $(LIST2);\
      do\
        echo $$x $$y; \
      done \
    done

(Please note that a nested loop that does cp doesn't make much sense.)

EDIT:
Well why didn't you say so?

LIST3 := $(join $(LIST1),$(LIST2))

Upvotes: 3

Related Questions