【问题标题】:Invoking bash commands in makefile [duplicate]在makefile中调用bash命令[重复]
【发布时间】:2017-01-21 14:43:22
【问题描述】:

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)"

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

    bash-4.1$
    BASE_MOD_TIME=""
    echo ""

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

【问题讨论】:

    标签: bash makefile


    【解决方案1】:

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

    但是,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
    

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

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

    1。目标特定变量。

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

    target: var := value
    

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

    target: var := $(shell ....)
    

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

    2。使用eval函数

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

    例如:

    radio:
        $(eval var = $(shell ...))
    

    注意事项:

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

    【讨论】:

    • “你不能在配方中设置一个 make 变量”:实际上你可以使用类似 $(eval FOO := bar) 的东西。它必须以选项卡开头,否则它将在解析时设置。还有.ONESHELL
    • @rici,我在这里尝试了您的解决方案,但无论出于何种原因,仍然无法捕获expr 命令的输出。在尝试了一些方法来修复它之后,我选择了一个单独的 bash 脚本,我在 make 配方中调用它。我在答案中发布了更多详细信息。感谢您的帮助!
    • @Allen:是的,bash 脚本可能是最好的解决方案。我应该建议的。您当然可以在 shell 变量中捕获输出,但正如我所说,该变量是命令的本地变量,而不是配方。因此,整个配方将需要是一个命令行,这需要反斜杠转义所有 tje 行结尾和分号终止所有命令。我可能应该更清楚。
    • @andrea:我总是不愿意建议.ONESHELL,因为它的影响是全球性的,而 csn 会造成严重破坏。
    【解决方案2】:

    这是怎么回事?

    fileB: fileA
        @echo $< was modified more recently than $@
    

    【讨论】:

    • 这似乎只是在呼应 fileB 最近被修改了。我想测试是否是这种情况,如果是的话就做些事情。
    • Make 已经做到了;它就是这样做的。只需要对您感兴趣的修改时间的文件的依赖即可。
    • @tripleee:感兴趣的文件是 python 文件,所以它们没有被编译。
    • 我不明白这有什么关系。如果你有一个依赖 A: B 的配方,那么如果 B 比 A 更新,或者如果 A 丢失,当你 make A 时,它将执行,否则,Make 将得出结论,不需要做任何事情。如果这不是您想要的,那么您的问题就不清楚了。
    • 为了清楚起见,您可以将 echo 替换为您实际想要执行的命令。
    【解决方案3】:

    我没有通过一些bash 命令强制makefile 完成所有繁重的工作,而是调用了一个单独的bash 脚本。这为像我这样的 bash 脚本的新手提供了更多的清晰度,因为我不必担心转义 make 正在使用的当前 shell。

    最终解决方案:

        .PHONY: all clean
    
        all      : (stuff happens here)
    
        radio    :
                ./radio_init_check.sh
                $(MKDIR_P) $(OBJDIR)
                make $(radio_10)
    

    radio_init_check.sh 是我的缝纫脚本。

    【讨论】:

    • 这在 makefile 中不起作用。 ./.sh 不会运行
    猜你喜欢
    • 2012-04-24
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    • 2021-02-10
    • 2019-03-13
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多