在makefile中,"$(shell ...)"和"$$(...)"有什么区别?

3
我对“shell”MAKE函数和“$$”之间的差异感到困惑。在文档中,我找到了以下内容:

shell函数 接受一个单一的参数,该参数会被扩展(像所有参数一样),并传递给子shell执行。然后读取命令的标准输出,并将其作为函数值返回。

我认为这正是"$ $"所做的事情,但在这个小例子中:
a = $(shell find . -maxdepth 1 -type f -name "Makefile")
b = $$(find . -maxdepth 1 -type f -name "Makefile")

.PHONY: all A B

all: A B

A: $(a)
    @echo "Target: $(@)"
    @echo "Prereq: $(<)"
    @echo "Var a:  $(a)"
    @echo "Var b:  $(b)"

B: $(b)
    @echo "Target: $(@)"
    @echo "Prereq: $(<)"
    @echo "Var a:  $(a)"
    @echo "Var b:  $(b)"

输出如下:
Target: A
Prereq: Makefile
Var a:  ./Makefile
Var b:  ./Makefile
make: *** No rule to make target '$(find)', needed by 'B'.  Stop.

请注意这里说的是“没有规则可以制作目标'$(find)'”,好像参数还没有被展开。(我也尝试让变量简单地展开,“b:=$$(...)”,但是这并没有改变什么)。
我希望有人能详细说明这一点,对我来说似乎是一个微妙的差别,但可能比我现在能够理解的要深刻得多。
1个回答

4

$(shell ...) 是一个 Make 的文本函数。Make 会展开它,所以在你的例子中,a将被展开为find命令的结果。如果你将它定义为简单扩展的变量,那么shell命令只会被评估一次。

$$ 只是被展开为 $,所以在你的例子中,b 将替换为值$(find . -maxdepth 1 -type f -name "Makefile")。无论是否使用=:=定义b,都将得到相同的结果。

当你在命令,如echo $(b)中使用$(b)时,运行该命令的shell 将把它看作命令替换。换句话说,你在shell命令中有echo $(find ...)

在Make目标或依赖项中使用$(b),正如你所见,不会执行进一步的评估。

这里有另一个示例Makefile,我希望它可以说明问题。我们使用单引号来显示shell实际接收到的内容:

a = $$(echo true)
b = $(shell echo true)

print:
    echo '$$a: $a' = "$a"
    echo '$$b: $b' = "$b"

.PHONY: print

这让我感到
echo '$a: $(echo true)' = "$(echo true)"
$a: $(echo true) = true
echo '$b: true' = "true"
$b: true = true

显示在第一种情况下,shell命令中使用了“$(echo true)”,而在第二种情况下,Make会评估“echo true”,并将结果“true”简单地替换为命令。

谢谢您提供详尽的答案,示例非常出色。我必须承认,如何扩展变量对我来说一直有些神奇。 - Sigve Karolius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接