Reputation: 29096
In this example, I have a process that takes some time. Let's says 1 second. If I write the following Makefile
, FOO
will be expanded 3 times for make all
and none for make clean
.
If I want to save some execution time for all
I can assign FOO
using :=
instead of =
. However this will cause FOO
to be expanded for the target clean even if it doesn't use it.
FOO = $(shell echo -e "+1" >> foo && echo "Hello" && sleep 1)
all:
@echo $(FOO)
@echo $(FOO)
@echo $(FOO)
@cat foo
clean:
rm foo
The output:
$ make
Hello
Hello
Hello
+1
+1
+1
I would like to force Make to expand a variable only once only if required.
Is it possible to do it so?
Upvotes: 2
Views: 788
Reputation: 100836
Try this:
FOO = $(eval FOO := $(shell echo "+1" >> foo && echo "Hello" && sleep 1))$(value FOO)
The first time make expands $(FOO)
it will first expand the eval
, which resets the variable FOO
using :=
. Then it resolves the value of the FOO
variable. In subsequent expansions, due to the eval
, FOO
expands directly to the value.
I should also point out that if you have at least GNU make 4.0 you can use a new feature added to the POSIX standard for make recently, the !=
operator:
FOO != echo "+1" >> foo && echo "Hello" && sleep 1
which does exactly what you want here.
Upvotes: 2
Reputation: 80931
The "best" solution I can come up with looks like this:
$ cat coin.mk
FOO = FOO:=$(shell echo -e "+1" >> foo && echo "Hello" && sleep 1)
defined = $(and $(filter-out undefined,$(origin $1)),$($1))
all:
echo $(eval $(FOO))$(FOO)
echo $(FOO)
echo $(FOO)
cat foo
clean:
rm foo
$ time make -f coin.mk clean
rm foo
rm: cannot remove `foo': No such file or directory
make: *** [clean] Error 1
real 0m0.003s
user 0m0.003s
sys 0m0.000s
$ time make -f coin.mk
echo Hello
Hello
echo Hello
Hello
echo Hello
Hello
cat foo
+1
real 0m1.009s
user 0m0.002s
sys 0m0.004s
$ time make -f coin.mk clean
rm foo
real 0m0.003s
user 0m0.000s
sys 0m0.003s
Which works but requires special-casing the first use of the variable in the make run ($(eval $(FOO))
run a second time will cause a make error).
I tried briefly to encapsulate the eval logic inside the value of FOO
but most attempts were blocked by make complaining that *** Recursive variable 'FOO' references itself (eventually). Stop.
Upvotes: 2