强制变量扩展一次且仅在使用时

Force variable expansion once and when used only

在这个例子中,我有一个需要一些时间的过程。让我们说1秒。如果我写下面的 MakefileFOO 将扩展 3 倍 make all,none 扩展 make clean

如果我想为 all 节省一些执行时间,我可以使用 := 而不是 = 分配 FOO。然而,这将导致 FOO 为目标 clean 扩展,即使它不使用它。

FOO = $(shell echo -e "+1" >> foo && echo "Hello" && sleep 1)

all:
    @echo $(FOO)
    @echo $(FOO)
    @echo $(FOO)
    @cat foo

clean:
    rm foo

输出:

$ make
Hello
Hello
Hello   
+1
+1
+1

我想强制 Make 只在需要时扩展一个变量一次。

这样可以吗?

我能想到的 "best" 解决方案如下所示:

$ cat coin.mk
FOO = FOO:=$(shell echo -e "+1" >> foo && echo "Hello" && sleep 1)

defined = $(and $(filter-out undefined,$(origin )),$())

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

这可行,但需要在 make 运行 中第一次使用变量时使用特殊外壳($(eval $(FOO)) 运行 第二次将导致 make 错误)。

我试图简单地将评估逻辑封装在 FOO 的值中,但大多数尝试都被 make 抱怨 *** Recursive variable 'FOO' references itself (eventually). Stop.

试试这个:

FOO = $(eval FOO := $(shell echo "+1" >> foo && echo "Hello" && sleep 1))$(value FOO)

第一次 make 扩展 $(FOO) 时,它将首先扩展 eval,这会使用 := 重置变量 FOO。然后它解析 FOO 变量的值。在后续展开中,由于evalFOO直接展开为值

我还应该指出,如果你至少有 GNU make 4.0,你可以使用最近添加到 POSIX make 标准的新功能,the != operator:

FOO != echo "+1" >> foo && echo "Hello" && sleep 1

这正是您想要的。