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

Makefile:在用户定义函数中的变量内存储参数并打印它,makefile,gnu-make,Makefile,Gnu Make,我有一个简单的Makefile,我想在其中传递一个文件列表,并将它们存储在用户定义函数中的一个变量中,然后打印文件名 REQUESTS:=request/*.json define my_func file=${1} && \ echo ${file} endef run_test : $(REQUESTS) for f in $^ ; \ do \ $(call my_func , $$f) ; \

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

REQUESTS:=request/*.json

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

run_test : $(REQUESTS)
    for f in $^ ; \
    do  \
        $(call my_func , $$f)   ; \
    done
当我运行
make run\u 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
我希望它返回以下输出

request/file1.json
request/file2.json

这里有几个问题。首先,将空格放在不应放置的位置:

$(call my_func , $$f)
将宏的
${1}
替换为
$f
(请注意前导空格),最后得到
文件=$f
,shell拒绝该文件,认为该文件在语法上不正确。使用:

$(call my_func,$$f)
相反,在Makefiles中避免使用无用的空格,它们有时是有意义的

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

for f in request/foo.json request/bar.json ...; \
do \
  file=$f && echo ; \
done
要通过make退出第一次扩展,必须将此
$
加倍(而不是对应于实际make变量的另一个):

现在,扩展的配方是:

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

make会将配方扩展为:

file="request/foo" && echo "$file"; file="request/bar" && echo "$file" ...
在将其传递到外壳之前。Make以某种方式展开循环,对每次迭代进行预处理(
patsubst
),shell以简单的命令序列执行结果,而不使用shell循环

最后但并非最不重要的一点是,每次我在Makefiles中的文件列表上看到这些循环时,我都想知道它们是否是作者不了解模式规则或多目标规则的标志。以防万一,下面是一个示例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
),这可以在多核计算机上产生真正的不同。

这里有几个问题。首先,将空格放在不应放置的位置:

$(call my_func , $$f)
将宏的
${1}
替换为
$f
(请注意前导空格),最后得到
文件=$f
,shell拒绝该文件,认为该文件在语法上不正确。使用:

$(call my_func,$$f)
相反,在Makefiles中避免使用无用的空格,它们有时是有意义的

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

for f in request/foo.json request/bar.json ...; \
do \
  file=$f && echo ; \
done
要通过make退出第一次扩展,必须将此
$
加倍(而不是对应于实际make变量的另一个):

现在,扩展的配方是:

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

make会将配方扩展为:

file="request/foo" && echo "$file"; file="request/bar" && echo "$file" ...
在将其传递到外壳之前。Make以某种方式展开循环,对每次迭代进行预处理(
patsubst
),shell以简单的命令序列执行结果,而不使用shell循环

最后但并非最不重要的一点是,每次我在Makefiles中的文件列表上看到这些循环时,我都想知道它们是否是作者不了解模式规则或多目标规则的标志。以防万一,下面是一个示例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
),这在多核计算机上会产生真正的不同