【问题标题】:GNU Make - Set MAKEFILE variable from shell command output within a rule/targetGNU Make - 从规则/目标中的 shell 命令输出设置 MAKEFILE 变量
【发布时间】:2014-06-19 23:09:53
【问题描述】:

我正在尝试将一些复杂的 makefile 规则放在一起,以针对多个编译器自动构建项目。我有一个规则,它创建一些动态生成的变量并将变量分配给它们,然后将这些变量传递给调用以构建单独的规则。

.PHONY: all
all:
    @echo "Detected CPULIST:${CPULIST_DETECTED}"
    @echo "CPULIST:${CPULIST}"
    @for cpu in $(CPULIST_DETECTED); do                   \
        echo "CPU:$${cpu}:";                                \
        eval VARIANTLIST_DETECTED=$(shell 2>&1 find ./.build/linux/$$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' |  grep -v warning ); \
        eval echo "Detected Variant List:$${VARIANTLIST_DETECTED}"; \
        eval variant_$${cpu}=$${cpu};                      \
        eval echo "variant_\$${cpu}:\$${variant_$${cpu}}"; \
        $(MAKE) build CPULIST=$${cpu};                     \
    done

.PHONY: build
build: sanity_check $(TARGET)
    @true

我有两个问题。首先是,尽管通过$$cpucpu 进行了双重转义,但它在该行中转换为null:

eval VARIANTLIST_DETECTED=$(shell 2>&1 find ./.build/linux/$$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' |  grep -v warning ); \

因此,find 命令在循环的每次迭代中搜索./.build/linux/,而不是像我预期的那样循环遍历./.build/linux/arm./.build/linux/x86。我已经包含了一个帖子,在下面的参考资料中对此进行了描述。我怀疑这可能会导致问题,因为我是在规则本身而不是在 makefile 的全局分配部分(在规则之前)尝试这样做。

另一个问题发生在完全相同的行。似乎shell 命令被评估,分配给VARIANTLIST_DETECTED,但随后VARIANTLIST_DETECTED 被执行,就像它是一个命令一样,因为我在构建过程中收到以下错误:

Detected CPULIST:arm x86
CPULIST:
CPU:arm:
/bin/sh: 3: ./.build/linux/x86: Permission denied
Detected Variant List:
variant_arm:arm

它试图在我的查询中运行第一个结果,就好像它是一个命令一样。该行也应该类似于./.build/linux/x86/so

我该如何解决这两个问题?它们是阻碍我完成 makefile 的最后两根刺。

谢谢!

参考文献

  1. 将 makefile 变量值分配给 bash 命令结果?,于 2014 年 6 月 19 日访问,<https://stackoverflow.com/questions/2373081/assign-a-makefile-variable-value-to-a-bash-command-result>

【问题讨论】:

    标签: c++ c makefile gnu-make


    【解决方案1】:

    您需要退后一步,想一想这里发生了什么:您似乎只是将一些东西扔到您的 makefile 中并希望结果是您想要的,而不是真正了解正在发生的事情并继续一个目的解决你的问题。

    首先要了解 make 如何调用配方:首先,make 扩展配方。扩展意味着 make 查看配方的文本并找到以 $ 开头的任何内容,并将其视为 make 函数或变量,并对其进行评估。因此,$$ 被转换为单个 $$(CPU_LIST_DETECTED) 被扩展为该变量的值,$(shell ....) 是一个运行的 make 函数,其结果包含在配方文本中。也许你已经可以看出你的食谱哪里出了问题……

    然后,第二个,在配方的所有被扩展之后,make 获取配方的扩展文本并将其传递给 shell。然后 Make 等待 shell 完成,然后 make 查看 shell 的退出代码。如果是0,那么配方成功并继续制作。如果不是0,则配方失败并退出并出现错误。 make 和 shell 之间没有交互,除了 make 使用要运行的脚本启动它,并取回退出代码。如果你以前不能,也许你现在可以看到你的食谱出了什么问题。

    实际上总是在配方中使用$(shell ...) 函数是一个错误(或至少是不必要的)...配方已经在shell 中运行!您不需要先运行另一个 shell,然后将这些结果提供给新的 shell。

    您的 shell 调用正在尝试使用 shell 变量 $cpu。但是该变量在这里没有任何价值,当 make 运行 $(shell ...) 函数时,因为 make 运行该函数 before 它会启动您的 shell 脚本。您的 make 函数中不能有引用在配方运行之前不会定义的东西的东西。这将运行 shell 脚本 2>&1 find ./.build/linux/$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' | grep -v warning,但未设置 shell 变量 cpu,因此 find 将遍历 ./.build/linux/ 下的所有内容。

    其次,你在这里误用了eval。 shell 函数扩展为一个单词列表,比如 foo bar baz。然后eval 将如下所示:

    eval VARIANTLIST_DETECTED=foo bar baz
    

    这和写这个是一样的:

    VARIANTLIST_DETECTED=foo bar baz
    

    这与在将变量VARIANTLIST_DETECTED 设置为值foo 之后运行带有参数baz 的命令bar 相同。这就是为什么它似乎在“运行你的目录”。

    你可能想要这个:

    VARIANTLIST_DETECTED="foo bar baz"
    

    (注意引号)。但是,通过eval 获取引号很棘手”,您必须将其转义。幸运的是,您根本不需要eval,因为您没有尝试分配动态命名的变量。您只需编写直接赋值。

    所以,丢掉了shell函数和eval,那一行应该写成:

    VARIANTLIST_DETECTED=`2>&1 find ./.build/linux/$$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' |  grep -v warning`; \
    

    回声同上:你不需要eval

    但是,我真的看不出设置这个变量或variant_$cpu 变量的意义。你永远不会对他们做任何事情。

    【讨论】:

    • 我的意思是你正在设置 shell 变量 VARIANTLIST_DETECTEDvariant_$cpu。这些是本地 shell 变量。然后你运行一个 sub-make,你不会以任何方式将这些变量传递给 sub-make。因此,设置它们完全没有用:即使您从未设置这些变量,您的 makefile 也将以完全相同的方式工作。除非你所做的比你展示的更多;如果是这样,如果您展示了您真正想做的事情,我们将能够为您提供更好的建议。
    【解决方案2】:

    一般来说,尽量避免将 Make 语法放在命令中。试试这个:

    VARIANTLIST_DETECTED=$$(find ./.build/linux/$${cpu} -mindepth 1 -maxdepth 1 | grep -v '.svn' |  grep -v warning );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-27
      相关资源
      最近更新 更多