3

Shell commands sometimes take a long time to run, so you may not want to do VAR = $(shell slow-cmd) (with =, the slow-cmd will be run every time the variable is referenced). Using VAR := $(shell slow-cmd) can be useful, but if you are building a target that does not ever need the variable expanded, you will get one more invocation of the slow-cmd than is needed. In the following makefile (with gnu-make), you can get the desired behavior: the shell command to define a value for V2 is never invoked more than once, and for the target foo it is not invoked at all. But this is a heinous kludge. Is there a more reasonable way to ensure that a variable is only defined when needed, but never evaluated more than once?

V1 = $(shell echo evaluating V1 > /dev/tty; echo V1 VALUE)

all: foo bar V2
        @echo $(V1) $@
        @echo $(V2) $@

foo:
        @echo $(V1) $@

bar: V2
        @echo $(V1) $@
        @echo $(V2) $@

V2:
        $(eval V2 := $(shell echo evaluating V2 > /dev/tty; echo V2 VALUE))

.PHONY: all foo bar
William Pursell
  • 204,365
  • 48
  • 270
  • 300

2 Answers2

9

There's no way to do it without tricks, but there's a cleaner way (maybe) than you're using. You can use:

V1 = $(eval V1 := $$(shell some-comand))$(V1)

For more details and explanation of exactly how this works see this page.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • This is *much* cleaner! Having to explicitly specify the variable as a dependency of each target that uses it is a serious drawback of my approach. – William Pursell Jun 09 '18 at 17:34
  • @MadScientist: I usually learn a lot from your answers (and your web site) but this one surpasses all I saw, I think. Thanks a lot for this super-tricky and useful contribution. – Renaud Pacalet Jun 13 '18 at 14:24
0

Target-specific deferred variables are an option:

host> cat Makefile
foo: VFOO = $(shell echo "VFOO" >> log.txt; echo "VFOO")

foo:
    @echo '$(VFOO)' > $@

bar: VBAR = $(shell echo "VBAR" >> log.txt; echo "VBAR")

bar:
    @echo '$(VBAR)' > $@

host> make foo
host> cat log.txt
VFOO
host> make foo
make: 'foo' is up to date.
host> cat log.txt
VFOO
host> make bar
host> cat log.txt
VFOO
VBAR
host> make bar
make: 'bar' is up to date.
host> cat log.txt
VFOO
VBAR
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • As written, though, this won't work for the case where more than one target makes use of the "slow" variable. In that case it will be evaluated once for each target that requires it -- right? – G.M. Jun 09 '18 at 10:30
  • @G.M.: Absolutely, this works only for target-specific variables. – Renaud Pacalet Jun 09 '18 at 11:41