在 makefile 中调用 bash 命令

All*_*len 2 bash makefile

在 a 中makefile,我试图检查 fileA 的修改时间是否比 fileB 更新的时间长。使用之前的一些帖子(thisthis)作为参考,我想出了这个来尝试将自上次文件修改以来的时间存储为变量:

(我宁愿这是在make配方之外的函数中,但一次一个。)

    .PHONY: all clean

    all      : (stuff happens here)

    radio    :
              BASE_MOD_TIME="$( expr $(date +%s) - $(date +%s -r src/radio_interface/profile_init.c) )"
              @echo "$(BASE_MOD_TIME)"
Run Code Online (Sandbox Code Playgroud)

我以为我会将expr命令的输出分配给一个变量BASE_MOD_TIME,但输出是:

    bash-4.1$
    BASE_MOD_TIME=""
    echo ""
Run Code Online (Sandbox Code Playgroud)

我在这里做错了什么?保存输出的简单尝试ls -l也没有像这样工作。

ric*_*ici 6

Make 变量通常是全局的,您通常不会在配方中设置 make 变量。配方只是要由外壳程序执行的命令列表,因此配方中的变量赋值看起来是外壳变量赋值。

但是,make配方中的每一行都在其自己的 shell 子进程中运行。因此,在一行中设置的变量在另一行中不可见;他们不执着。这使得在配方中设置 shell 变量不太有用。[注1]

但是您可以使用行尾的反斜杠转义将配方的多行组合成一个 shell 命令,并记住用分号终止各个命令(或者,更好地,将它们与 链接&&),因为反斜杠转义的换行符不会传递给shell。另外,不要忘记对$字符进行转义,以便将它们传递给 shell,而不是被make.

因此,您可以执行以下操作:

radio:
    @BASE_MOD_TIME="$$( expr $$(date +%s) - $$(date +%s -r src/radio_interface/profile_init.c) )"; \
    echo "$$BASE_MOD_TIME"; \
    # More commands in the same subprocess
Run Code Online (Sandbox Code Playgroud)

但是如果有多个命令,这会变得很尴尬,更好的解决方案通常是编写一个 shell 脚本并从配方中调用它(尽管这意味着 Makefile 不再是自包含的。)

Gnu make 提供了两种方法来设置配方中的 make 变量:

1. 特定于目标的变量。

您可以通过添加如下一行来创建一个特定于目标的变量(它不完全是目标本地的):

target: var := value
Run Code Online (Sandbox Code Playgroud)

要从 shell 命令设置变量,请使用以下shell函数:

target: var := $(shell ....)
Run Code Online (Sandbox Code Playgroud)

此变量将在目标配方和由目标触发的所有依赖项中可用。请注意,依赖项仅评估一次,因此如果它可以由不同的目标触发,则特定于目标的变量可能会或可能不会在依赖项中可用,具体取决于make解析依赖项的顺序。

2. 使用eval功能

由于配方的扩展总是延迟的,您可以使用eval配方中的函数来延迟 make 变量的分配。该eval函数可以很好地放置在配方中的任何位置,因为它的值是空字符串。但是,eval对变量赋值会使变量赋值成为全局的;它在整个 makefile 中都是可见的,但它在其他配方中的价值将再次取决于 make 评估配方的顺序,这不一定是可预测的。

例如:

radio:
    $(eval var = $(shell ...))
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 您可以使用.ONESHELL:伪目标更改此行为,但这将适用于整个 Makefile;无法将单个配方标记为在单个子流程中执行。由于更改行为可能会以意想不到的方式破坏配方,因此我通常不推荐此功能。