31

I am using GNU make 3.81. Here is a test makefile that demonstrates the problem:

define BOZO

a$(1): b c
    touch a$(1)

endef

$(foreach i,1 2 3,$(call BOZO,$(i)))

The idea here is to use a macro template (BOZO) to generate rules that follow a predictable pattern.

Problem: when I run make on this makefile I get an error saying:

Makefile.fake:10: *** multiple target patterns.  Stop.

(where line 10 is the line with the foreach).

Now, I know what that error normally indicates. Let's see what that line expands to by using the info function to send the expansion to standard out. I change line 10 to be:

$(info $(foreach i,1 2 3,$(call BOZO,$(i))))

and I run:

$ make -n

a1: b c
    touch a1

a2: b c
    touch a2

a3: b c
    touch a3

make: *** No targets.  Stop.

Note that the "no targets" message is expected, since the $(info ...) function evaluates to empty but causes make to print the generated rules.

Let's run those rules then shall we?

$make -n > out.txt
make: *** No targets.  Stop.
$make -f out.txt a1 a2 a3
touch a1
touch a2
touch a3
$

AAARGH! The rules work fine. So... is the bug in make, or in my understanding?

One final clue that might help diagnose: if I change the foreach line to:

$(foreach i,1,$(call BOZO,$(i)))

(so that foreach has only one iteration)

and then do

$make a1

I get a different error:

make: *** No rule to make target `a1'.  Stop.

I don't know of any way to "see" the expansion of $(foreach ) that make sees except for $(info ), and its output is legal, so I'm quite stumped.

Joe
  • 621
  • 2
  • 8
  • 14

3 Answers3

45
$(foreach i,1 2 3,$(eval $(call BOZO,$(i))))

The eval function tells Make to parse the structures as makefile syntax, to "enact" them. I'm not sure why Make objected to the un-eval'd rules this particular way, but that's kind of academic.

Beta
  • 96,650
  • 16
  • 149
  • 150
34

Beta's answer is correct but I wanted to address the comment, "I'm not sure why Make objected to un un-eval'd rules".

The reason the un-eval'd rules don't work is that a makefile is ultimately line-based, and lines are chopped BEFORE variables are expanded. So it's just not possible for an expansion of a variable to turn into a multiline result: even if the expansion contains newlines make treats the entire thing as one "line". When make finishes expanding the foreach loop and parses the results it basically sees this:

a1: b c touch a1 a2: b c touch a2 a3: b c touch b3

which is why you get the "multiple target patterns" error.

The eval causes make to re-interpret the result of the expansion from the beginning as a complete snippet of makefile syntax, including the line-chopping etc., and that's why a multi-line expansion works there.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
1

Maybe old, but always relevant. There is no need to make this macro at all.

The solution for your problem is by simply defining this rule:

a%: b c
    touch a%

The percent acts as a wildcard. See more info here: https://stackoverflow.com/a/7406471/2522849

jaques-sam
  • 2,578
  • 1
  • 26
  • 24
  • In your example, the `touch a%` uses a literal `%`. I think you meant `touch a$*`. In this case, it can also be `touch $@`. For reference, those two are [automatic variables](https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html). – Joseph Marinier Jul 22 '23 at 14:37