Reputation: 3279
This is a toy Makefile. I want to create file "a", "b", "c" as well as "a.out", "b.out", "c.out".
LIST=a b c
all: $(addsuffix .out,$(LIST))
define FUN
in := $(1)
out := $(1).out
$$(in) :
echo $(1) > $$(in)
$$(out) : $$(in)
echo $$(in) > $$(out)
endef
$(foreach p,$(LIST),\
$(eval $(call FUN,$(p))))
When I test run it using "make -n", version GNU Make 4.1, the real commands executed are:
echo a > c
echo c > c.out
echo b > c
echo c > c.out
echo c > c
echo c > c.out
However, the expected outcome is:
echo a > a
echo a > a.out
echo b > b
echo b > b.out
echo c > c
echo c > c.out
Can someone help explain the behavior of Makefile or how to fix this script? Thanks.
Upvotes: 2
Views: 7536
Reputation: 100781
Why don't you just use the $1
variable everywhere instead of creating new variables? There's no rule that says you have to use a variable in the expansion. You could just write it as:
define FUN
$1 :
echo $1 > $1
$1.out : $1
echo $1 > $1.out
endef
If you don't want to do that, you can use automatic variables like this:
define FUN
$1 :
echo $$@ > $$@
$1.out : $1
echo $$< > $$@
endef
EDIT
Without seeing some sort of realistic example it's hard to judge. The short answer to your question is no, there's no magic way to create "variable scope" inside a makefile.
You should always use $@
, $<
, and other automatic variables. That's what they're for. If you have other variable assignments that are needed, and you want to set them as variables instead of using the call parameters directly, you have only two choices: target-specific variables or constructed macro names.
But I don't really see the problem of "long list of target-specific variables". Why bother to set the variables globally then set them again as target-specific? If you just set them directly as target-specific variables you only have to write them once. It's not clear exactly what you find "awkward" about their use so I can't say if that's sufficient.
Also, you should set the target-specific variables on the main target, not the prerequisite, since target-specific variables are "inherited" through prerequisite lists.
So, like this:
define FUN
$1.out : FOO = $(some complex thing)
$1 :
echo $$(FOO) > $$@
$1.out : $1
echo $$(FOO) from $$< > $$@
endef
Upvotes: 4
Reputation: 3279
Here is my attempt to answer this question and it comes with a tedious fix to the codes. The reason why I posted here is to wait for a better answer.
From GNU Make manual (https://www.gnu.org/software/make/manual/make.html#Reading-Makefiles), the target and prerequisites are immediately evaluated, and the recipe parts are deferred.
In my example, $$(out) : $$(in)
are evaluated immediately. That's what I expected. But echo $$(in) > $$(out)
is expanded later. At that time, $$(in)
becomes c
and $$(out)
becomes c.out
. But that's not what I want.
An awkward fix in my point of view is to use target-specific variable (https://www.gnu.org/software/make/manual/make.html#Target_002dspecific). Now the Makefile is like below:
LIST=a b c
all: $(addsuffix .out,$(LIST))
define FUN
in := $(1)
out := $(1).out
$$(in) : in=$(1)
$$(in) :
echo $(1) > $$(in)
$$(out) : $$(in)
echo $$(in) > $$(out)
endef
$(foreach p,$(LIST),\
$(eval $(call FUN,$(p))))
The target-specific variable is specified in $$(in) : in=$(1)
.
However, when there are more than one variables in the recipe part, you will have to add multiple target-specific variables, which does not look nice.
Upvotes: 3