Makefile定义:递归扩展问题

发布于 2024-08-16 14:38:55 字数 951 浏览 1 评论 0原文

在 makefile 中,我使用 define 指令定义一个变量。该变量将保存我想要执行的可配置命令列表。

我希望这个变量能够获取文件列表(例如 .foo 文件)。这些文件是在 makefile 执行期间创建的。例如makefile:

MY_VAR = $(wildcard *.foo)
define MY_VAR2
    echo $(1) $(MY_VAR)
endef

foo: create_files
    $(call MY_VAR2, ls)
    rm -f *.foo

create_files:
    touch foo.foo
    touch bar.foo

我没有得到想要的结果。看来MY_VAR2是在声明时评估的。

有没有办法获得所需的行为?


编辑:

$(shell) 命令,正确为 sateesh指出,适用于上面的例子。但是,它不适用于下面的示例。此示例的主要区别在于新文件是在 MY_VAR2 内创建的。

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(shell ls *.foo)

define MY_VAR2 
    @touch foo.foo
    @touch bar.foo
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo:
    $(call MY_VAR2, ls) 
    @rm -f *.foo 

我可以通过添加规则来解决上述问题。有没有更简单的方法?

In a makefile, I define a variable using the define directive. This variable will hold a configurable list of commands that I want to execute.

I would like this variable to get a list of files (.foo files, for example). These files are created during the makefile execution. For example makefile:

MY_VAR = $(wildcard *.foo)
define MY_VAR2
    echo $(1) $(MY_VAR)
endef

foo: create_files
    $(call MY_VAR2, ls)
    rm -f *.foo

create_files:
    touch foo.foo
    touch bar.foo

I do not get the desired results. It appears that MY_VAR2 is evaluated upon declaration.

Is there a way to get the desired behavior?


edit:

The $(shell) command, as sateesh correctly pointed out, works for the example above. However, it does not work for the example below. The main difference in this example is that the new files are created inside MY_VAR2.

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(shell ls *.foo)

define MY_VAR2 
    @touch foo.foo
    @touch bar.foo
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo:
    $(call MY_VAR2, ls) 
    @rm -f *.foo 

I can solve the above by adding rules. Is there a simpler method?

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

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

发布评论

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

评论(6

我的痛♀有谁懂 2024-08-23 14:38:55

在我看来,你正在滥用 make,试图在 make 中编写 shell 脚本。

如果你写一个shell脚本,就写一个shell脚本。您按顺序执行命令,并且可以知道执行每一行时存在哪些文件。

touch foo.foo
touch bar.foo
your-command `ls *.foo`

另一方面,如果你想使用make,那么就有规则和依赖关系,如果你采用make的思维方式,你甚至不需要使用define。

foo: create_files
    your-command $(wildcard *.foo)
    rm -f *.foo

create_files:
    touch foo.foo
    touch bar.foo

It looks to me like you are abusing make, trying to write a shell script in make.

If you write a shell script, write a shell script. You execute your commands in sequence, and you are able to know what files are present when executing each line.

touch foo.foo
touch bar.foo
your-command `ls *.foo`

On the other side, if you want to make usage of make, then have rules and dependencies, you won't even have to use define if you go the make way of thinking.

foo: create_files
    your-command $(wildcard *.foo)
    rm -f *.foo

create_files:
    touch foo.foo
    touch bar.foo
被翻牌 2024-08-23 14:38:55

我假设您在这里使用的是 GNU Make。我想你可以得到想要的
使用 GNU Make 提供的“shell”内置函数得出结果。

下面是 make 文件片段,演示了如何获得结果:

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(shell ls *.foo)

define MY_VAR2 
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo: create_files 
    $(call MY_VAR2, ls) 
    @rm -f *.foo 

create_files: 
    @touch foo.foo 
    @touch bar.foo

针对目标“foo”运行上述 make 文件的结果是:

MY_VAR ls
TEST_VAR ls bar.foo foo.foo

因此,如所演示的 shell 函数的用法一样,可以获得所需的结果。

来自 GNU make 文档:

shell
函数执行的功能与大多数情况下的反引号 (''') 执行的功能相同
shell:它确实进行命令扩展。这意味着它需要一个 shell 作为参数
命令并计算命令的输出。唯一的处理品牌是
结果是将每个换行符(或回车符/换行符对)转换为单个空格。
...
通过调用 shell 函数运行的命令将在函数调用展开时运行。
...
文件 := $(shell echo .c)
将文件设置为“.c”的扩展。除非 make 使用非常奇怪的 shell,否则它具有
与 '$(wildcard *.c)' 相同的结果(只要至少存在一个 '.c' 文件)。

所以我认为使用递归扩展变量(=形式)以及 shell 函数
可以得到想要的结果。

I'm presuming here that you are using here GNU Make. I think you can get the desired
result using the "shell" built-in function provided with GNU Make.

Below is the make file snippet that demonstrates how you can get the result:

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(shell ls *.foo)

define MY_VAR2 
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo: create_files 
    $(call MY_VAR2, ls) 
    @rm -f *.foo 

create_files: 
    @touch foo.foo 
    @touch bar.foo

The result of running the above make file for the target "foo" is:

MY_VAR ls
TEST_VAR ls bar.foo foo.foo

So as demonstrated usage of shell function gets the desired result.

From the GNU make documentation:

The shell
function performs the same function that backquotes (‘‘’) perform in most
shells: it does command expansion. This means that it takes as an argument a shell
command and evaluates to the output of the command. The only processing make does on
the result is to convert each newline (or carriage-return / newline pair) to a single space.
...
The commands run by calls to the shell function are run when the function calls are expanded.
...
files := $(shell echo .c)
sets files to the expansion of ‘
.c’. Unless make is using a very strange shell, this has the
same result as ‘$(wildcard *.c)’ (as long as at least one ‘.c’ file exists).

So I think using a recursively expanded variable (= form) along with shell function you
can get the desired result.

那一片橙海, 2024-08-23 14:38:55

4.4.3 函数通配符

通配符扩展在规则中自动发生。但是,当设置变量或在函数的参数内部时,通配符扩展通常不会发生。如果想在这些地方做通配符扩展,就需要使用通配符函数

你可以围绕eval函数进行工作。

这些创建的文件应该是另一个规则的依赖项,以便依赖关系图是正确的。

参考文献:

4.4.3 The Function wildcard

Wildcard expansion happens automatically in rules. But wildcard expansion does not normally take place when a variable is set, or inside the arguments of a function. If you want to do wildcard expansion in such places, you need to use the wildcard function

You may work your away around the eval function.

These created files should be dependencies of another rule so that the dependency graph is correct.

References:

年华零落成诗 2024-08-23 14:38:55

你能尝试一下过滤命令吗?您可以在此处找到示例。

Could you try the filter command? You can find an example here.

半暖夏伤 2024-08-23 14:38:55

使用 include 怎么样?
重点是 make 需要执行两遍。
首先,你需要让 make 创建它想要的东西,
然后让 make 在第二遍中解析生成的规则
第一条规则的执行。

define MY_VAR2
    echo $(1) $(MY_VAR)
    $(1) $(MY_VAR)
endef

-include .listoffiles

foo: create_files
    $(call MY_VAR2, ls -al)
    rm -f *.foo .listoffiles

create_files: .listoffiles

.listoffiles:
    touch foo.foo
    touch bar.foo
    @echo "MY_VAR = \$(wildcard *.foo)" > $@

我想它只是做你想要的。
两次通过评估并不意味着您必须输入 make 两次。
这将由 make 自动完成。

% make -f test.mk
touch foo.foo
touch bar.foo
echo  ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan  8 08:44 bar.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan  8 08:44 foo.foo
rm -f *.foo .listoffiles

How about using include?
The point is that the make needs two pass of execution.
First, you need to let make create what it wants,
Then you'll let make to parse the generated rule in the second pass after
the execution of the first rule.

define MY_VAR2
    echo $(1) $(MY_VAR)
    $(1) $(MY_VAR)
endef

-include .listoffiles

foo: create_files
    $(call MY_VAR2, ls -al)
    rm -f *.foo .listoffiles

create_files: .listoffiles

.listoffiles:
    touch foo.foo
    touch bar.foo
    @echo "MY_VAR = \$(wildcard *.foo)" > $@

It just does what you want, I suppose.
Two pass evaluation doesn't mean you have to type make twice.
It will be done by make automagically.

% make -f test.mk
touch foo.foo
touch bar.foo
echo  ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan  8 08:44 bar.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan  8 08:44 foo.foo
rm -f *.foo .listoffiles
踏雪无痕 2024-08-23 14:38:55

在 shell 中而不是在 make 中进行扩展怎么样?

MY_VAR = $(ls *.foo)
define MY_VAR2
    @echo $(1) $(MY_VAR)
endef

foo: create_files
    $(call MY_VAR2, ls)
    @rm -f *.foo

create_files:
    @touch foo.foo
    @touch bar.foo

双 $ 将允许 shell 扩展文件搜索部分。我忍不住同意其他帖子关于有一种更优雅的方法来做到这一点,但这对你来说是一个选择。

也适用于你的第二个例子:

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(ls *.foo)

define MY_VAR2 
    @touch foo.foo
    @touch bar.foo
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

foo:
    $(call MY_VAR2, ls) 
    @rm -f *.foo

What about doing your expansion in the shell instead of in make?

MY_VAR = $(ls *.foo)
define MY_VAR2
    @echo $(1) $(MY_VAR)
endef

foo: create_files
    $(call MY_VAR2, ls)
    @rm -f *.foo

create_files:
    @touch foo.foo
    @touch bar.foo

The double-$ will allow the shell to expand the file search part. I can't help but agree with the other posts about there being a more elegant way to do this, but this is an option for you.

Works for your second example too:

MY_VAR = $(wildcard *.foo) 
TEST_VAR = $(ls *.foo)

define MY_VAR2 
    @touch foo.foo
    @touch bar.foo
    @echo "MY_VAR" $(1) $(MY_VAR)
    @echo "TEST_VAR" $(1) $(TEST_VAR)
endef 

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