强制变量扩展一次且仅在使用时
Force variable expansion once and when used only
在这个例子中,我有一个需要一些时间的过程。让我们说1秒。如果我写下面的 Makefile
,FOO
将扩展 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
变量的值。在后续展开中,由于eval
,FOO
直接展开为值
我还应该指出,如果你至少有 GNU make 4.0,你可以使用最近添加到 POSIX make 标准的新功能,the !=
operator:
FOO != echo "+1" >> foo && echo "Hello" && sleep 1
这正是您想要的。
在这个例子中,我有一个需要一些时间的过程。让我们说1秒。如果我写下面的 Makefile
,FOO
将扩展 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
变量的值。在后续展开中,由于eval
,FOO
直接展开为值
我还应该指出,如果你至少有 GNU make 4.0,你可以使用最近添加到 POSIX make 标准的新功能,the !=
operator:
FOO != echo "+1" >> foo && echo "Hello" && sleep 1
这正是您想要的。