【问题标题】:GNU Make does not execute recipe lineGNU Make 不执行配方行
【发布时间】:2022-01-16 12:46:48
【问题描述】:

鉴于 Makefile 的以下部分:

$(call pkg-source,$(1)):
$(call unpack,$(call tarball,$(1)))
$(foreach pch, $(sort $(wildcard src/$(notdir $(ver_$(1))).patch*)), patch -d $(1)* -p1 < $(pch))
$(foreach var, $(shell find $(1)* -type f -iname abc.xyz), cp my_abc.xyz $(var))
touch $$@
noopstop

noopstop 用于强制 make 以“找不到命令”终止。

make clean 之后首先运行,解压外部源代码,然后(第一个foreach)修补(如果存在修补程序)。

然后(第二个foreach)必须用我自己的版本覆盖源附带的一个已知名称的文件。这在第一次运行时总是失败,但文件在那里。

删除touch $$@创建的文件,再次运行make,文件现在被替换了。

-j1 选项开始make 没有区别; -d 选项也无助于查找问题。

GNU Make 页面声明:“通常,make 一次只会执行一个配方,等待它完成后再执行下一个。”那么在第一个foreach(用于打补丁)之后,为什么第二个看不到文件?

这是在 Fedora_35、x86_64、GNU make 4.3 上

谢谢,马特

【问题讨论】:

  • 你怎么知道它失败了?它在哪一步失败?你能给我们一个minimal complete example吗?
  • @Beta,很抱歉没有提供完整的示例,我以为我对 make 的工作原理有一个简单的误解。我可以检测到文件替换不起作用,因为文件仍然是原始日期和大小,而不是副本运行良好时的大小和日期。对我来说,看起来 foreach 仍然没有获取在配方执行期间对目录内容所做的更改。我会尝试做一个完整的例子,但它是一个复杂的 Makefile。
  • 我尝试制作一个简单的makefile来演示这个问题,但每次我有一个问题也消失了。我已经放弃了这种方法,因为在 makefile 的其他地方我找到了另一种方法来做我想做的事情,并且它有效。

标签: foreach makefile


【解决方案1】:

很难理解您的示例,因为您没有在实际规则的上下文中提供它。但我假设您在上面提供的全部内容都在您与call 一起使用的某个define 变量中。但是,如何使用它的细节是关键的,你没有提供它们。

问题是您正在使用 make 变量和函数而不是 shell 命令来完成所有这些工作。 make 的工作方式是首先扩展整个配方的所有行(因此所有的 make 变量和函数都被扩展),只有在完成之后才会使用配方的每一行一次调用一个 shell。

一个好的经验法则是,如果您发现自己在配方中运行了 make shell 函数,那么您可能做错了什么。配方已经在 shell 中运行,所以除了使用 make 的 shell 函数造成混乱之外几乎没有什么好处。

此外,如果他们找到多个文件,您的操作将无法正常工作。

例如:

$(foreach pch, $(sort $(wildcard src/$(notdir $(ver_$(1))).patch*)), patch -d $(1)* -p1 < $(pch))

假设这里的wildcard 扩展为两个文件,src/xyz.patch-01src/xyz.patch-02。现在扩展它的结果将是这样的:

patch -d foo* -p1 < src/xyz.patch-01 patch -d foo* -p1 < src/xyz.patch-02

这显然是错误的。如果你想让一个 make foreach 循环扩展到多个 shell 命令,你必须记住添加一个分号或 &amp;&amp; 来分隔命令:

$(foreach pch, ..., patch -d $(1)* -p1 < $(pch) &&) true

您将遇到与其他命令相同的问题,除非您不想像我上面讨论的那样使用 make foreachshell 来执行此操作。您应该在这里使用 shell 构造,而不是 make 构造:

for pch in $$(ls -1 src/$(notdir $(ver_$(1))).patch*); do \
    patch -d $(1)* -p1 < $$pch; \
done
for var in $$(find $(1)* -type f -iname abc.xyz); do \
    cp my_abc.xyz $$var; \
done

【讨论】:

  • 补丁确实有效,多年来一直如此。例如,如果 $(1) 是 "libaom",并且 src 目录包含 libaom-v3.2.0.patch0 libaom-v3.2.0.patch1 libaom-v3.2.0.tar.xz 则源将解压缩到目录 libaom-v3.2.0 并且生成日志显示 tar -xJf src/libaom-v3.2.0.tar.xz patch -d libaom* -p1 &lt; src/libaom-v3.2.0.patch0 patching file CMakeLists.txt patch -d libaom* -p1 &lt; src/libaom-v3.2.0.patch1 patching file aom_ports/arm_cpudetect.c 格式化很抱歉,我还在学习中
  • 它行不通:如果它对您有效,那么您的环境肯定与您在此处向我们展示的内容有本质上的不同。 make 中的 foreach 循环是一个简单的文本替换。它对运行命令一无所知,甚至对命令是什么一无所知。如果您有$(foreach A, 1 2 3, echo $A),则结果将是文本echo 1 echo 2 echo 3。这里没有换行符或命令分隔符,因此当此文本发送到 shell 时,它将看到一个带有参数 1 echo 2 echo 3 的单个 echo 命令,而不是三个不同的 echo 命令。
  • 啊,我开始明白了。我抄错了foreach line,实际上它是'$(foreach pch,$(sort $(wildcard src/$(notdir $(ver_$(1))).patch*)), patch -d $(1)* -p1 &lt; $(pch)),所以那里有一个换行符,我不知道这很重要。 (不能在补丁语句之前获取换行符,它由两个制表符缩进)。
  • 它开始工作了,我现在保留了'foreach',排序很关键,因为补丁必须按照补丁文件名的最后一部分确定的特定顺序应用。 for var ... 循环几乎可以工作,除了文件被复制到(字面上)ar ,所以两个美元符号加上var 的第一个字符被吃掉了。我还缺少什么?
  • 很抱歉,我无法理解您的情况;在无法获得正确格式的情况下,无法查看使用 cmets 进行设置的方式。请编辑您的问题并添加更多上下文:您引用的代码到底是做什么的?我猜它在define/endef?请证明这一点。然后请显示使用define 的实际规则,以便我们了解它是如何使用的。在不了解完整环境的情况下,绝对无法解决变量扩展问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多