Weird foreach + user-defined function behavior in

2019-07-24 05:24发布

问题:

I have the following Makefile:

X=a.jar b.jar c.jar
Y=/var/tmp/abc/a.jar /var/tmp/abc/b.jar /var/tmp/abc/c.jar

all: $(addprefix /var/tmp/abc/tmp/, $(X))

define AddRule
dst=$1
src=$2
/var/tmp/abc/tmp/$(dst): $(src)
        @echo $$@
        @echo $$^
        @mkdir -p $$(dir $$@)
        @cp $$^ $$@
endef
$(foreach jar, $(X), $(eval $(call AddRule, $(jar), $(filter %$(jar), $(Y)))))

(/var/tmp/abc/{a,b,c}.jar exist)

Basically, for each file name in $X, I want to find the full path in $Y and create a rule that copies that file to /var/tmp/abc/tmp (I know there's a much easier way to do this, but my actual Makefile is more complicated and needs a construct like this).

When I run this Makefile, I get the following output:

/var/tmp/abc/tmp/a.jar
/var/tmp/abc/a.jar
/var/tmp/abc/tmp/b.jar
/var/tmp/abc/b.jar
make: *** No rule to make target `/var/tmp/abc/tmp/c.jar', needed by `all'.  Stop.

Now the "funny" part: If I add the following line after declaring the "all" rule:

X+=d

all three jar files are processed, 'make' doesn't mention anything about 'd' (even if file 'd' doesn't exist), and the run is a success.

So it looks like the foreach loop doesn't go through the last iteration (or most probably something else that leads to the same results happens). Does anybody know what the problem is and how to fix it?

Thanks!

回答1:

It's not just the last iteration of the loop that's bad. Basically call expands $(dst) to whatever value it has when call is invoked, not when the new code is evaluated. Then the new code is evaluated, and then in the next iteration the definition is expanded-- with the values that have been set by the previous block of new code. So instead of {a, b, c} it gives { , a, b} (you have to stare at this for a while before it makes sense).

Solution: put in a couple of extra $s:

define AddRule
dst=$1
src=$2
/var/tmp/abc/tmp/$$(dst): $$(src)
    ...
endef