在初始化为 shell 输出的宏中定义变量

问题描述 投票:0回答:1

本题类似于Defining Variables inside macro.

我在 v3.82 和 v4.3 上都试过了。在下面的代码片段中,当我使用宏中的 shell 语句的输出初始化变量时,出现错误。在我的系统上,__LINUX_GCC_MAIN_VERSION 应该初始化为 11,然后我希望在 __LINUX_GCC_V7_OR_LATER 的初始化中使用它。这似乎没有发生,导致 expr.

的语法错误

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

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

我希望看到的是:

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

为什么这没有按预期工作,我需要做什么来纠正它?

gnu-make
1个回答
0
投票

调试

eval
问题的正确方法是使用
info
。重复与
eval
相同的行,但将
eval
替换为
info
,make 将打印出
eval
将解析的 makefile 部分。

问题,就像曾经存在的几乎所有

eval
问题一样,都没有完全理解扩展是如何工作的。考虑这一行:

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

make 是怎么处理的?与 ALL 扩展一样,它的工作原理是“由内而外”。所以首先最里面的变量被展开,从左向右移动(但这里我们没有任何同一级别的值,所以这无关紧要)。这意味着 make 做的第一件事就是扩展它:

$(call __DECLARE_VERSION_FLAGS_GCC,linux,LINUX)

这里是非常简单的字符串替换:

call
没有 makefile 语法的概念。它只是将第一个参数的值扩展为变量名
__DECLARE_VERSION_FLAGS_GCC
,其中
$1
设置为
linux
$2
设置为
LINUX
。这是如何运作的?变量值为:

$(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])

所以我们开始扩张。请记住,对于

call
,这只是一个字符串。它没有 make 语法的概念。所以首先它展开
$(info [1])
并打印出
[1]
。然后展开
__$(2)_GCC_MAIN_VERSION = 
,结果是
__LINUX_GCC_MAIN_VERSION = 
。然后它扩展 shell 命令,它首先将参数扩展到 shell,它将
$($(2)_CC)
扩展为
gcc -m32
,因此它将运行 shell 命令:

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

将打印

11
,到目前为止,我们已经将
call
函数扩展为:

__LINUX_GCC_MAIN_VERSION = 11

但至关重要的是,我重复一遍,这只是一个字符串。我们实际上并没有将 make 变量

__LINUX_GCC_MAIN_VERSION
设置为
11
。就品牌而言,这可能是
zippity doo dah 11
.

然后我们转到下一行:

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

现在我们以同样的方式展开它,在这里当我们到达

$(__LINUX_GCC_MAIN_VERSION)
时这个变量没有设置,因为我们仍然在
call
。所以它扩展为空,shell 运行
expr  \>= 7
这是无效的语法,你会得到你看到的错误。

这是一起使用

eval
call
的技巧:

作为一般规则,变量的内容应该escape所有

$
除了
call
参数(
$1
$2
等)

这样,所有那些

$
都通过了
call
而不展开,然后
eval
将它们展开。所以像这样重写你的定义:

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 ...)
由您决定,因为它对扩展结果没有影响)。

现在在调用之后,shell 等还没有运行,你将把这个字符串传递给

eval
:

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

然后它将起作用:eval 将首先设置

__LINUX_GCC_MAIN_VERSION
变量,然后当它到达第二行时将被设置。

© www.soinside.com 2019 - 2024. All rights reserved.