【问题标题】:Creating variable from bash command output in makefile从makefile中的bash命令输出创建变量
【发布时间】:2020-11-23 21:14:41
【问题描述】:

你好我想问一下如何在makefile中创建bash变量。 下面的代码确实有效,但变量“目标”一直为空。我如何在makefile中实际创建“targets”和“current_target”变量?问题是我必须从实际的 bash 命令创建这些变量。

SHELL=/bin/bash
...
MAIN_APPS = 01_prep 02_prep 03_prep 04_prep 05_prep
foo : .check_targets .check_segment 
    @for f in $(MAIN_APPS) ; do \
        targets=$(expr $(last_target) + 1); \
        echo $(targets) + 1; \
        curent_target=$(expr "$(echo $(f) | head -c 2) + 0"); \
        if [ $(targets) = $(current_target)]; then \
            break; \
        else \
            make $(f); \
        fi \
    done

输出:

make foo last_target="02" segm=KI
+ 1

如果我传递一个参数 last_target="05"

【问题讨论】:

  • 如果你想使用$()作为shell进程替换,你必须转义$来做。例如:targets=$$(expr ...
  • 您似乎混淆了可以使用$(variable) 扩展的make 变量和可以扩展为$variable${variable} 的shell 变量。请注意,如果您希望 $ 由 shell 而非 make 解释,则必须编写 $$
  • 是的,我对使用 $ $$ 感到困惑,但现在我知道如何在 makefile 中使用它们,谢谢

标签: linux bash makefile gnu-make


【解决方案1】:

你混淆了 make 变量和 shell 变量。而且你没有考虑过 make 在将它们传递给 shell 之前会扩展它们,这是你经常需要使用 $$ 而不是 $ 的原因。或者,对于命令扩展,使用 `...` 而不是 $(...)。您还在不应该使用双引号的地方使用双引号 (expr "...")。最后,您在食谱中使用make,这不是一个好主意。你应该使用$(MAKE)(详见How the MAKE Variable Works section of the GNU make manual)。

尝试以下(未测试)也许:

SHELL=/bin/bash
...
MAIN_APPS = 01_prep 02_prep 03_prep 04_prep 05_prep
foo : .check_targets .check_segment 
    @for f in $(MAIN_APPS) ; do \
        targets=`expr "$(last_target)" + 1`; \
        echo "$$targets + 1"; \
        tmp=`echo "$$f" | head -c 2`; \
        curent_target=`expr "$$tmp" + 0`; \
        if [ "$$targets" = "$$current_target" ]; then \
            break; \
        else \
            $(MAKE) "$$f"; \
        fi \
    done

但我几乎 100% 确信您正在尝试将 make 用作一种脚本语言而不是复杂的构建系统。如果我理解得很好,您正试图通过指定最后一个目标编号来仅重建 MAIN_APPS 的一个子集。您可能可以通过更化妆的风格来实现同样的效果:

TARGETS := $(shell printf '%02d_prep\n' $$(seq $(last_target)))

.PHONY: foo
foo: $(TARGETS) .check_targets .check_segment

仅此而已,foo 没有复杂的配方。什么都没有(当然,除了构建 .check_targets.check_segment 和所有 xx_prep 目标的规则,你没有显示)。

解释:我使用$(shell ...) make 函数和一个小的shell 脚本来初始化make 变量TARGETS01_prep 02_prep ... 直到$(last_target)_prep。如 cmets 中所述,此 shell 脚本甚至不需要 bash,并且可以使用默认的 make shell (sh)。因此,如果您没有其他充分的理由使用 bash,请去掉 SHELL=/bin/bash 行,您的 Makefile 会更便携。

然后,我将 foo 声明为假的,因为您可能没有名为 foo 的文件,并且如果您有一个文件,您可能无论如何都希望构建 foo

最后,我将$(TARGETS).check_targets.check_segment 声明为foo 的先决条件。如果您要求 make 构建 foo 并且这些先决条件中的任何一个已过时,make 将使用您为其声明的规则构建它。

这更像是 make-ish,因为它清楚地告诉 make 什么取决于什么,而不是把它隐藏在一个食谱中。 make 需要这样做才能正确完成其工作,其中包括比较文件的最后修改日期以确定是否必须重建某些内容。它还有额外的好处,例如,如果可能的话,利用计算机的并行性来并行构建多个xx_prep

【讨论】:

  • 这里没有什么依赖于 Bash,所以 SHELL= 真的不需要(或者相反,你可以使用更强大的内置算法来避免脆弱的 expr)。但更好的解决方案可能是将其重构为 Makefile 代码以生成所需的依赖项作为实际依赖项。
  • for 循环也不需要 Bash,尽管它很方便; TARGETS := $(shell printf '%02d_prep\n' $$(seq 1 $(last_target)))
  • @tripleee 你是对的,但作为 OP 明确使用 bash 我想这是有充分理由的。此外,就像 Paul Smith 有一条规则 1 说“使用 GNU make。不要麻烦编写可移植的 makefile,而是使用可移植的 make!”,我有一条规则 1 说“使用 bash。不要为编写可移植脚本而烦恼,改用可移植 shell!”;-)。
  • @tripleee 更新了我的答案以表明不需要 bash 并使用了您的解决方案。
  • 有点讽刺的是,seq is not portable either (-:
猜你喜欢
  • 1970-01-01
  • 2018-02-02
  • 2012-02-23
  • 1970-01-01
  • 1970-01-01
  • 2020-03-22
  • 2012-07-16
  • 2011-01-02
  • 1970-01-01
相关资源
最近更新 更多