在 makefile 配方中找不到变量

在 makefile 配方中找不到变量

为什么这个简单的食谱不起作用?

.PHONY: test
test:
    foo := $(shell ls | grep makefile) ;\
    echo $(foo)

结果是

$> make test
makefile:65: warning: undefined variable 'foo'
foo := makefile ;\
echo 
/bin/sh: 1: foo: not found

那么,据我了解,该变量foo已设置好值makefile,但之后无法使用?然而,它是一个单行命令,在同一个 shell 中执行?

然而,这有效

@$(eval export foo := $(shell ls | grep makefile)) \
echo $(foo)

echo所以我猜第一个示例中的变量不可访问,因为在我们尝试?时尚未评估赋值。

如果我进一步挖掘,如何完成这项工作

.PHONY: test
test:
    @$(eval export files = $(shell ls))
    for f in $(files) ; do \
        t = $(ls | grep $$f) ; \
        echo $$t;\
    done

答案1

我看了你的循环...引用在这里:

.PHONY: test
test:
    @$(eval export files = $(shell ls))
    for f in $(files) ; do \
        t = $(ls | grep $$f) ; \
        echo $$t;\
    done

所以...$(eval ... )在 make 中运行一个命令。

$(shell ls)ls在 shell 中运行命令,并替换其输出。

因此,由 运行的命令$(eval ... )类似于export files = file file2 makefile source.c。该命令创建一个名为 files 的 make 变量并将其导出到子 make。因此,可能不需要导出。

整个$(eval ... )可以被替换为files = $(wildcard *) 并且它可以使用:=并放置在规则之外。

for循环(四行)在 shell 中运行。完成的第一件事是替换 make 变量和函数。奇怪的是$(ls | grep $$f)。由于 ls 不是 make 函数,因此它将尝试扩展未定义的变量。这是一个空字符串。如果这是 shell 的$(...)运算符,则需要将 $ 加倍。 $$被扩展为$. $(files)在eval的基础上进行扩展。

这变成(使用我之前的例子):

for f in file file2 makefile source.c ; do
    t =
    echo $t;
done

乍一看,这可能会回显四个空行,但事实并非如此。该命令t =实际上运行程序t并传递等号作为参数。 t可能不存在。因此,我们得到四个错误,表明 t 不是有效程序,每个错误后面都有一个空行(除非 t 在其他地方定义)。

更接近您想要的可能是:

files := $(wildcard *)
.PHONY: test
test:
    for f in $(files) ; do \
        t=$$(ls | grep $$f) ; \
        echo $$t ; \
    done

这将输出:

file file2
file2
makefile
source.c

请注意,第一行列出了两个文件,因为它们的名称中都包含“文件”。如果这不是您想要的,您可以考虑:

files := $(wildcard *)
.PHONY: test
test:
    for f in $(files) ; do \
        echo $$f ; \
    done

甚至(可能是 GNU 特定的):

files := $(wildcard *)
.PHONY: test
test:
    $(foreach f, $(files), echo $f ; )

答案2

在 make 目标配方内部,命令的处理方式与配方外部的逻辑不同(通过生成 shell)。

您可以将变量移到配方之外:

.PHONY: test
foo := $(shell ls | grep makefile)
test:
    echo $(foo)

或者,礼貌地这个问题,使用评估:

.PHONY: test
test:
    $(eval foo=$(shell ls | grep makefile))
    echo $(foo)

两者都输出以下内容:

echo makefile
makefile

相关内容