如何对 Makefile 目标进行基准测试?

How do I benchmark Makefile targets?

考虑以下 Makefile:

test:
    @echo "$(shell date) Before test"
    sleep 10
    @echo "$(shell date) After test"

当我运行这个时,输出如下:

Wed Nov 24 17:00:22 PST 2021 Before test
sleep 10
Wed Nov 24 17:00:22 PST 2021 After test

看起来 make 在 执行目标之前 正在执行 shell 命令。

如何强制 make 执行出现在食谱中的 shell 命令?

看来下面达到了想要的效果

test:
    @echo "$$(date) Before test"
    sleep 10
    @echo "$$(date) After test"

我对其他答案很好奇,但如果有更少的 hacky 方法可以做到这一点。

通过先将 Make 方法传递给 shell 来扩展 Make 方法。因此,当 make 扩展您的第一个食谱时,食谱变为:

    @echo "Wed Nov 24 17:00:22 PST 2021 Before test"
    sleep 10
    @echo "Wed Nov 24 17:00:22 PST 2021 After test"

因为几乎同时调用了date。只有在配方被扩展后,它才会被传递给 shell。在这种情况下,每一行都由不同的 shell 执行。最后一个在其他 10 秒后执行,但因为要回显的字符串已经设置...

Make 在将食谱传递给 shell 之前对其进行了扩展,这是有充分理由的。例如,用它们的实际值替换像 $@(规则的目标)或 $^(规则的先决条件)这样的自动 make 变量。 shell 无法做到这一点。

要获得所需的效果,正如您自己意识到的那样,您需要让食谱本身调用 date,例如使用 $(date) 或使用老式的反引号。请注意,您必须转义 $ 符号以保护它不受 make 扩展的影响:

@echo foobar."$$(date)"

展开后变成:

@echo foobar."$(date)"

这就是为什么人们仍然经常在 make 配方中使用反引号,即使现在建议使用 $(...) 语法进行命令替换;没有必要逃避他们:

@echo foobar."`date`"

在配方中使用 $(shell ...) make 函数是没有用的。食谱已经是 shell 个脚本。与大多数 make 函数相同。在食谱中使用它们的唯一原因是当它们比 shell 等价物更有效或更简单时。但是我们必须记住,它们是由 make 本身扩展的,而不是 shell,并且 make 在将配方传递给 shell 之前扩展了它们。示例:如果您想打印所有先决条件的基本名称,notdir make 函数很方便:

@echo $(notdir $^)

而不是:

@for f in $^; do basename "$$f"; done

但是您不能打印 ./project/src 中所有 C 源文件的基本名称:

@$(notdir find ./project/src -name '*.c')

因为 make 将它的 notdir 函数应用于它的每个(扩展的)单词参数,它传递给 shell 的是:

@find src -name '*.c'

然后执行 find 命令时使用错误的起始目录(src 而不是 ./project/src),即使存在这个错误的起始目录,结果的目录部分没有被已经展开的notdir剥离。