Makefile:在用户定义的函数中存储变量内部的参数并打印它

Makefile :Store argument of inside a variable in user defined function and print it

我有一个简单的 Makefile,我想在其中传递文件列表并将它们存储在用户定义函数的变量中并打印文件名。

REQUESTS:=request/*.json

define my_func
    file=       && \
    echo ${file}
endef

run_test : $(REQUESTS)
    for f in $^ ; \
    do  \
        $(call my_func , $$f)   ; \
    done

当我 运行 make run_test

时出现以下错误
for f in request/temp.json ; \
do      \
                file= $f
/bin/sh: 3: Syntax error: end of file unexpected (expecting "done")
Makefile:53: recipe for target 'run_test' failed
make: *** [run_test] Error 2

我想要它return下面的输出

request/file1.json
request/file2.json

这里有几个问题。首先你把 spaces 放在你不应该的地方:

$(call my_func , $$f)

$f 替换宏的 </code> (注意前导 space),最后得到 <code>file= $f 被 [=68= 拒绝] 语法不正确。使用:

$(call my_func,$$f)

相反,避免在 Makefile 中使用无用的 space,它们有时是有意义的。

下一步(假设你解决了第一个问题),你在你的宏中使用 ${file} 但 make 在将结果传递给 shell 之前扩展它并且因为没有名为 file(它是一个 shell 变量,而不是 make 变量),它扩展为空 space执行的配方是:

for f in request/foo.json request/bar.json ...; \
do \
  file=$f && echo ; \
done

要通过 make 逃避第一次扩展,您必须将其加倍 $(不是对应于真实 make 变量的另一个):

define my_func
    file=       && \
    echo $${file}
endef

现在,扩展的配方是:

for f in request/foo.json request/bar.json ...; \
do \
  file=$f && echo ${file}; \
done

它的行为应该如您所愿。

旁注

您必须记住,配方仅由 make 扩展一次,就在将完整配方传递给 shell 之前,而不是在 shell 循环的每次迭代中。换句话说,当 shell 开始执行您的配方时,期望 make 进行任何额外处理已经太迟了; $(call...) 已经展开,结果不会再改变; make 已经完成了这条规则的工作。如果您期望 $(call...) 在不同的迭代中有不同的效果,您会感到失望。

如果您不希望 $(call...) 在不同的迭代中有不同的扩展,那么您写的(几乎)是可以的。但是,如果您在宏中使用其他 make 函数 (patsubst...) 和变量,则需要另一种方法:您还必须使用 make 迭代器 (foreach):

REQUESTS := $(wildcard request/*.json)

define my_func
file="$(patsubst %.json,%,$(1))" && \
echo "$$file"
endef

run_test: $(REQUESTS)
    $(foreach f,$^,$(call my_func,$(f));)

make 会将配方扩展为:

file="request/foo" && echo "$file"; file="request/bar" && echo "$file" ...

在将其传递给 shell 之前。 Make 以某种方式展开循环,pre-processes (patsubst) 每次迭代,并且 shell 将结果作为平坦的命令序列执行,没有 shell 循环。

最后但同样重要的是,每次我在 Makefile 中看到这些文件列表循环时,我想知道它们是否表明作者不了解模式规则或 multi-targets 规则。为了以防万一,这里有一个 Makefile 示例,它使用静态模式规则来处理(在此示例中只是复制)每个 request/xxx.json 文件并生成一个 foo/xxx.foo 文件:

REQUESTS := $(wildcard request/*.json)
FOOS     := $(patsubst request/%.json,foo/%.foo,$(REQUESTS))

define my_func
cp "request/$(1).json" "foo/$(1).foo"
endef

.PHONY: run_test clean

run_test: $(FOOS)

$(FOOS): foo/%.foo: request/%.json | foo
    $(call my_func,$*)

foo:
    mkdir -p $@

clean:
    rm -rf foo

一个潜在的好处是所有食谱都可以 运行 并行 (make -j),这可以在 multi-core 计算机上产生真正的不同。