GNU Make - 配方中的变量扩展

发布于 2025-01-18 09:05:20 字数 825 浏览 3 评论 0原文

假设在工作目录中,我有:

$ find . | grep testfile
./testfile1

这是我的 Makefile:

list_files:
    @echo "Show Files..."
    @echo $(shell find . | grep testfile)
    @touch testfile2
    @echo $(shell find . | grep testfile)
    @rm testfile2

使用 make,我得到:

$ make list_files
Show Files...
./testfile1
./testfile1

为什么会发生这种情况?我期望它是这样的:

Show Files...
./testfile1
./testfile1 ./testfile2

那么我的问题是:

为什么规则内配方中的所有变量/函数在调用目标后可能同时扩展?

我从这个答案中找到了一个非常接近事实的解释:

您的尝试不起作用的原因是 make 将在开始第一行之前评估配方的所有行。

但那里没有提供参考资料,我只是无法说服自己相信GNU Make的这种工作机制。

有人可以提供一些线索吗?谢谢!

Say in the working directory, I have:

$ find . | grep testfile
./testfile1

This is my Makefile:

list_files:
    @echo "Show Files..."
    @echo $(shell find . | grep testfile)
    @touch testfile2
    @echo $(shell find . | grep testfile)
    @rm testfile2

With make, I got this:

$ make list_files
Show Files...
./testfile1
./testfile1

Why this happened? I expected it to be something like this:

Show Files...
./testfile1
./testfile1 ./testfile2

Then my question is:

Why all the variables/function in recipes inside a rule, are expanded likely simultaneously after target is invoked?

I have found an explanation from this answer that is pretty close to the truth:

The reason your attempt doesn't work is that make will evaluate all lines of the recipe before it starts the first line.

But there is no references provided there, I just cannot convinced myself of this working mechanism of GNU Make.

Could anyone give some clues? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

话少心凉 2025-01-25 09:05:20

为什么会发生这种情况?

因为,对于不适用于您的情况的警告,make函数,例如$(shell ...)在解析Makefile时,请评估,而不是在执行过程中食谱。

为什么在调用目标之后,可能同时扩展一条规则内的所有变量/功能?

他们不是。在目标食谱运行之前,它们会扩展。实际上,在制作之前,甚至确定是否应运行配方

但没有提供参考

只要手册。特别是请参见第8.14节, 函数

通过调用的命令在扩展函数调用时运行函数(请参阅如何读取makefile)。

...指第3.7节, 代码> make 读取makefile ,特别是:

gnu make以两个不同的阶段进行工作。在第一阶段,它读取所有makefiles,包括makefiles等。并内化了所有变量及其值以及隐式和显式规则,并构建了所有目标及其先决条件的依赖图。在第二阶段,make使用此内部数据来确定需要更新哪些目标并运行所需的配方以更新它们。

它还依靠第8.1节,语法

函数调用类似于变量参考。它可以出现在任何地方都可以出现的任何地方,并且使用与变量引用相同的规则进行扩展。

当然,“相同的规则”包括执行扩展时的规则。


您的配方应以目标外壳的语言编写,通常是/bin/sh。所有Make函数和每个食谱中的变量引用将在任何食谱运行之前进行扩展,因此它们的扩展无法反映在当前make> make运行期间运行任何配方的结果。尝试使用$(shell ...)函数来做到这一点特别奇怪,因为您可以在配方中直接使用shell代码。

Why this happened?

Because, with one caveat that does not apply to your case, make functions such as $(shell ...) are evaluated when the makefile is parsed, not during the execution of recipes.

Why all the variables/function in recipes inside a rule, are expanded likely simultaneously after target is invoked?

They're not. They are expanded before the target's recipe runs. In fact, before make even determines whether the recipe should be run.

But there is no references provided

This is covered in the manual. See in particular section 8.14, The shell function:

The commands run by calls to the shell function are run when the function calls are expanded (see How make Reads a Makefile).

... which refers to section 3.7, How make Reads a Makefile, in particular:

GNU make does its work in two distinct phases. During the first phase it reads all the makefiles, included makefiles, etc. and internalizes all the variables and their values and implicit and explicit rules, and builds a dependency graph of all the targets and their prerequisites. During the second phase, make uses this internalized data to determine which targets need to be updated and run the recipes necessary to update them.

It also relies on section 8.1, Function Call Syntax:

A function call resembles a variable reference. It can appear anywhere a variable reference can appear, and it is expanded using the same rules as variable references.

"The same rules" of course includes the rules for when expansions are performed.


Your recipes should be written in the language of the target shell, usually /bin/sh. All make functions and variable references in each recipe will be expanded before any recipe runs, so their expansions cannot reflect the results of running any recipe during the current make run. It's particularly peculiar to try to use the $(shell ...) function to do that, because you can just use shell code directly in a recipe.

鹿! 2025-01-25 09:05:20

如评论中所述,在执行配方中的任何行之前,所有$(shell ...)和其他功能在执行任何行之前均已执行(扩展)。但是您可以延迟扩展:

list_files:
    @echo "Show Files..."
    @echo $(shell find . | grep testfile)
    @touch testfile2
    @echo $(shell find . | grep testfile)
    @rm testfile2

这会产生预期的输出。注意双$$。 Make仍将在执行配方之前先扩展所有变量/功能,然后删除一个美元标志。执行配方行时,表达式将再次扩展。

当然,在此示例中,您最好没有$(shell)。但是,这本质上并不是一个坏主意。有时,您需要在扩展其他变量之前执行Shell命令,然后$(Shell)是微不足道的解决方案。

As explained in the comments, all $(shell ...) and other functions are executed (expanded) before executing any lines from the recipe. But you can delay the expansion:

list_files:
    @echo "Show Files..."
    @echo $(shell find . | grep testfile)
    @touch testfile2
    @echo $(shell find . | grep testfile)
    @rm testfile2

This yields the expected output. Note the double $$. Make will still expand all variables/functions first before executing the recipe, and remove one of the dollar signs. The expressions will be expanded again, when executing the recipe lines.

Of course, you're better off without the $(shell) in this example. However, it's not inherently a bad idea. Sometimes you need to execute shell commands before expanding other variables, and then $(shell) is the trivial solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文