How to get “at most once” semantics in variable as

2020-02-07 12:37发布

问题:

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

回答1:

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.



回答2:

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