在宏内部定义的变量初始化为shell输出。

huangapple go评论63阅读模式
英文:

Defining variables inside macro initialized to shell output

问题

I understand, you want the code part to remain in English. Here is the translated version:

这个问题类似于 https://stackoverflow.com/questions/24607558。

我尝试过使用 GNU Make v3.82 和 v4.3 进行测试。在下面的片段中,当我用宏内部的shell语句的输出初始化变量时,出现了一个错误。在我的系统上,__LINUX_GCC_MAIN_VERSION 应该被初始化为11,然后我希望它用于初始化 __LINUX_GCC_V7_OR_LATER。但似乎并没有发生这种情况,导致了来自 expr 的语法错误。

我添加了 $(info) 语句以帮助调试。

```makefile
LINUX_CC = gcc -m32

define __DECLARE_VERSION_FLAGS_GCC
$(info [1])
__$(2)_GCC_MAIN_VERSION = $(shell $($(2)_CC) -dumpversion | sed -e 's/\..*//')
__$(2)_GCC_V7_OR_LATER  = $(shell expr $(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])
endef

$(info [6])
$(eval $(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX))

$(info [7])
# This outputs '11' as expected
$(info $(__LINUX_GCC_MAIN_VERSION))

$(info [8])
# This should be the result of "expr 11 >= 7", but the result is "FALSE"
ifeq ($(__LINUX_GCC_V7_OR_LATER),1)
V7 = "TRUE"
else
V7 = "FALSE"
endif
$(info $(V7))

$(info [9])

.PHONY: all

all:
	@echo Done
~/temp/tmp$ make -f Makefile3
[6]
[1]
expr: 语法错误: 意外参数 ‘7’
[5]
[7]
11
[8]
"FALSE"
[9]
Done

我期望看到的是:

~/temp/tmp$ make -f Makefile3
[6]
[1]
[5]
[7]
11
[8]
"TRUE"
[9]
Done

为什么这不像预期的那样工作,我需要做什么来纠正它?

英文:

This question is similar to https://stackoverflow.com/questions/24607558.

I've tried this with both GNU Make v3.82 and v4.3. In the snippet below, I'm getting an error when I have a variable initialized with output from a shell statement from within a macro. On my system, __LINUX_GCC_MAIN_VERSION should be initialized with 11, which I then expect to be used in the initialization of __LINUX_GCC_V7_OR_LATER. This does not appear to be happening, resulting in the syntax error from expr.

I've added $(info) statements to help with debugging.

LINUX_CC = gcc -m32

define __DECLARE_VERSION_FLAGS_GCC
$(info [1])
__$(2)_GCC_MAIN_VERSION = $(shell $($(2)_CC) -dumpversion | sed -e 's/\..*//')
__$(2)_GCC_V7_OR_LATER  = $(shell expr $(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])
endef

$(info [6])
$(eval $(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX))

$(info [7])
# This outputs '11' as expected
$(info $(__LINUX_GCC_MAIN_VERSION))

$(info [8])
# This should be the result of "expr 11 >= 7", but the result is "FALSE"
ifeq ($(__LINUX_GCC_V7_OR_LATER),1)
V7 = "TRUE"
else
V7 = "FALSE"
endif
$(info $(V7))

$(info [9])

.PHONY: all

all:
	@echo Done
~/temp/tmp$ make -f Makefile3
[6]
[1]
expr: syntax error: unexpected argument ‘7’
[5]
[7]
11
[8]
"FALSE"
[9]
Done

What I expect to see is this:

~/temp/tmp$ make -f Makefile3
[6]
[1]
[5]
[7]
11
[8]
"TRUE"
[9]
Done

Why is this not working as desired, and what do I need to do to correct it?

答案1

得分: 0

The right way to debug eval issues is with info. 重要的是使用 info 来调试 eval 问题。 Repeat the same line as the eval but replace the eval with info, and make will print out the section of makefile that eval will parse. 重复与 eval 相同的行,但用 info 替换 eval,make 将打印出 eval 将解析的 makefile 部分。

The problem, as with basically every eval issue that ever existed, is not fully understanding how expansion works. 问题与基本上存在的每个 eval 问题一样,都是没有完全理解扩展的工作原理。

Consider this line:

$(eval $(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX))

How does make handle it? 考虑这一行:

$(eval $(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX))

make 如何处理它?

As with ALL expansion, it works from the "inside out". 与所有扩展一样,它从“内部向外”工作。

So first the innermost variables are expanded, moving from left to right (but here we don't have any values at the same level so that doesn't matter). 所以首先展开最里面的变量,从左到右移动(但在这里我们没有任何在同一级别的值,所以这没关系)。

This means that the first thing make does is expand this:

$(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX)

This is very simple string replacement here: call has no concept of makefile syntax. 它只是简单的字符串替换:call 没有 makefile 语法的概念。

It's just expanding the value of the first argument interpreted as a variable name __DECLARE_VERSION_FLAGS_GCC with $1 set to linux and $2 set to LINUX. 它只是将第一个参数的值扩展为变量名 __DECLARE_VERSION_FLAGS_GCC,其中 $1 设置为 linux$2 设置为 LINUX

How does this work? 可以这样解释:

$(info [1])
__$(2)_GCC_MAIN_VERSION = $(shell $($(2)_CC) -dumpversion | sed -e 's/\..*//')
__$(2)_GCC_V7_OR_LATER  = $(shell expr $(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])

so we start expanding. 我们开始扩展。

Remember, for call this is just a string. 记住,对于 call 来说,这只是一个字符串。

It has no concept of make syntax. 它对 makefile 语法没有概念。

So first it expands the $(info [1]) and prints out [1]. 首先它扩展 $(info [1]) 并打印出 [1]

Then it expands __$(2)_GCC_MAIN_VERSION = and the result is __LINUX_GCC_MAIN_VERSION = . 然后它扩展 __$(2)_GCC_MAIN_VERSION = ,结果是 __LINUX_GCC_MAIN_VERSION =

Then it expands the shell command, which first expands the arguments to the shell, which will expand $($(2)_CC) to gcc -m32, so it will run the shell command:

gcc -m32 -dumpversion | sed -e 's/\..*//'

which will print 11, so so far we've expanded the call function to:

__LINUX_GCC_MAIN_VERSION = 11

But crucially, and I repeat, this is just a string. 但关键的是,我重申一下,这只是一个字符串。

We have not actually set the make variable __LINUX_GCC_MAIN_VERSION to 11. 事实上,我们并没有将 make 变量 __LINUX_GCC_MAIN_VERSION 设置为 11

As far as make is concerned this could be zippity doo dah 11. 就 make 而言,这可能是 zippity doo dah 11

Then we go to the next line:

__$(2)_GCC_V7_OR_LATER  = $(shell expr $(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])

Now we expand this the same way, and here when we get to $(__LINUX_GCC_MAIN_VERSION) this variable is not set, because we're still in the call. 现在我们以相同的方式扩展它,当我们到达 $(__LINUX_GCC_MAIN_VERSION) 时,这个变量没有设置,因为我们还在 call 中。

And so it expands to empty, and the shell runs expr \>= 7 which is invalid syntax and you get the error you see. 因此,它会扩展为空,shell 运行 expr \>= 7,这是无效的语法,你会看到错误。

Here's the trick when working with eval and call together:

As a general rule the content of the variable should escape all $ except for the call arguments ($1, $2, etc.) 与 evalcall 一起工作时的窍门如下:

一般来说,变量的内容应该_转义_所有 $,除了 call 的参数($1$2 等)。

This way, all those $ are passed through the call and not expanded, and then the eval will expand them. 这样,所有这些 $ 都会通过 call 传递而不会被扩展,然后 eval 将会扩展它们。

So rewrite your define like this: 因此,像这样重写你的定义:

define __DECLARE_VERSION_FLAGS_GCC
$(info [1])
__$(2)_GCC_MAIN_VERSION = $$(shell $$($(2)_CC) -dumpversion | sed -e 's/\..*//')
__$(2)_GCC_V7_OR_LATER  = $$(shell expr $$(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])
endef

(whether you escape the $(info ...) is up to you since it has no impact on the result of the expansion). (是否转义 `

英文:

The right way to debug eval issues is with info. Repeat the same line as the eval but replace the eval with info, and make will print out the section of makefile that eval will parse.

The problem, as with basically every eval issue that ever existed, is not fully understanding how expansion works. Consider this line:

$(eval $(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX))

How does make handle it? As with ALL expansion, it works from the "inside out". So first the innermost variables are expanded, moving from left to right (but here we don't have any values at the same level so that doesn't matter). This means that the first thing make does is expand this:

$(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX)

This is very simple string replacement here: call has no concept of makefile syntax. It's just expanding the value of the first argument interpreted as a variable name __DECLARE_VERSION_FLAGS_GCC with $1 set to linux and $2 set to LINUX. How does this work? The variable value is:

$(info [1])
__$(2)_GCC_MAIN_VERSION = $(shell $($(2)_CC) -dumpversion | sed -e 's/\..*//')
__$(2)_GCC_V7_OR_LATER  = $(shell expr $(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])

so we start expanding. Remember, for call this is just a string. It has no concept of make syntax. So first it expands the $(info [1]) and prints out [1]. Then it expands __$(2)_GCC_MAIN_VERSION = and the result is __LINUX_GCC_MAIN_VERSION = . Then it expands the shell command, which first expands the arguments to the shell, which will expand $($(2)_CC) to gcc -m32, so it will run the shell command:

gcc -m32 -dumpversion | sed -e 's/\..*//'

which will print 11, so so far we've expanded the call function to:

__LINUX_GCC_MAIN_VERSION = 11

But crucially, and I repeat, this is just a string. We have not actually set the make variable __LINUX_GCC_MAIN_VERSION to 11. As far as make is concerned this could be zippity doo dah 11.

Then we go to the next line:

__$(2)_GCC_V7_OR_LATER  = $(shell expr $(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])

Now we expand this the same way, and here when we get to $(__LINUX_GCC_MAIN_VERSION) this variable is not set, because we're still in the call. And so it expands to empty, and the shell runs expr \>= 7 which is invalid syntax and you get the error you see.

Here's the trick when working with eval and call together:

As a general rule the content of the variable should escape all $ except for the call arguments ($1, $2, etc.)

This way, all those $ are passed through the call and not expanded, and then the eval will expand them. So rewrite your define like this:

define __DECLARE_VERSION_FLAGS_GCC
$(info [1])
__$(2)_GCC_MAIN_VERSION = $$(shell $$($(2)_CC) -dumpversion | sed -e 's/\..*//')
__$(2)_GCC_V7_OR_LATER  = $$(shell expr $$(__$(2)_GCC_MAIN_VERSION) \>= 7)
$(info [5])
endef

(whether you escape the $(info ...) is up to you since it has no impact on the result of the expansion).

Now after the call, the shell etc. will not have run yet and you'll pass this string to the eval:

__LINUX_GCC_MAIN_VERSION = $(shell $(LINUX)_CC) -dumpversion | sed -e 's/\..*//')
__LINUX_GCC_V7_OR_LATER  = $(shell expr $(__LINUX_GCC_MAIN_VERSION) \>= 7)

and then it will work: eval will first set the __LINUX_GCC_MAIN_VERSION variable, then when it gets to the second line it will be set.

huangapple
  • 本文由 发表于 2023年3月23日 09:00:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75818450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定