Kconfig 宏语言¶
概念¶
基本思想受到 Make 的启发。当我们查看 Make 时,我们注意到一种语言中包含两种语言。一种语言描述了由目标和先决条件组成的依赖关系图。另一种是用于执行文本替换的宏语言。
两种语言阶段之间有明显的区别。例如,您可以编写如下的 makefile
APP := foo
SRC := foo.c
CC := gcc
$(APP): $(SRC)
$(CC) -o $(APP) $(SRC)
宏语言将变量引用替换为其展开形式,并将其视为源文件输入,如下所示
foo: foo.c
gcc -o foo foo.c
然后,Make 分析依赖关系图并确定要更新的目标。
Kconfig 中的想法非常相似 - 可以像这样描述 Kconfig 文件
CC := gcc
config CC_HAS_FOO
def_bool $(shell, $(srctree)/scripts/gcc-check-foo.sh $(CC))
Kconfig 中的宏语言将源文件处理为以下中间状态
config CC_HAS_FOO
def_bool y
然后,Kconfig 进入评估阶段,以解决符号间的依赖关系,如Kconfig 语言中所述。
变量¶
与 Make 中一样,Kconfig 中的变量用作宏变量。宏变量“就地”展开以产生文本字符串,然后可以进一步展开。要获取变量的值,请将变量名括在 $( ) 中。即使对于单字母变量名,也需要括号;$X 是语法错误。也不支持花括号形式,如 ${CC}。
有两种类型的变量:简单展开的变量和递归展开的变量。
简单展开的变量使用 := 赋值运算符定义。它的右侧在从 Kconfig 文件读取该行时立即展开。
递归展开的变量使用 = 赋值运算符定义。它的右侧仅存储为变量的值,而不以任何方式展开它。相反,展开在变量使用时执行。
还有另一种类型的赋值运算符;+= 用于将文本附加到变量。如果左侧最初定义为简单变量,则 += 的右侧立即展开。否则,其评估将被推迟。
变量引用可以采用参数,形式如下
$(name,arg1,arg2,arg3)
您可以将参数化引用视为函数。(更准确地说,是“用户定义的函数”,与下面列出的“内置函数”相对)。
有用的函数必须在使用时展开,因为如果传递不同的参数,则相同的函数会以不同的方式展开。因此,用户定义的函数使用 = 赋值运算符定义。参数在主体定义中用 $(1)、$(2) 等引用。
事实上,递归展开的变量和用户定义的函数在内部是相同的。(换句话说,“变量”是“零参数的函数”。)当我们广义地说“变量”时,它包括“用户定义的函数”。
内置函数¶
与 Make 类似,Kconfig 提供了几个内置函数。每个函数都接受特定数量的参数。
在 Make 中,每个内置函数至少接受一个参数。Kconfig 允许内置函数使用零参数,例如 $(filename)、$(lineno)。您可以将这些视为“内置变量”,但这只是我们如何称呼它的问题。让我们在这里说“内置函数”来指代本机支持的功能。
Kconfig 当前支持以下内置函数。
$(shell,command)
“shell”函数接受一个参数,该参数被展开并传递给子 shell 以执行。然后读取命令的标准输出并将其作为函数的值返回。输出中的每个换行符都替换为空格。任何尾随换行符都会被删除。不返回标准错误,也不返回任何程序退出状态。
$(info,text)
“info”函数接受一个参数并将其打印到标准输出。它评估为空字符串。
$(warning-if,condition,text)
“warning-if”函数接受两个参数。如果 condition 部分是“y”,则 text 部分将发送到标准错误。文本以当前 Kconfig 文件的名称和当前行号为前缀。
$(error-if,condition,text)
“error-if”函数类似于“warning-if”,但如果 condition 部分是“y”,则它会立即终止解析。
$(filename)
“filename”不带参数,$(filename) 扩展为正在解析的文件名。
$(lineno)
“lineno”不带参数,$(lineno) 扩展为正在解析的行号。
Make vs Kconfig¶
Kconfig 采用类似 Make 的宏语言,但函数调用语法略有不同。
Make 中的函数调用如下所示
$(func-name arg1,arg2,arg3)
函数名和第一个参数之间至少用一个空格分隔。然后,从第一个参数中删除前导空格,而保留其他参数中的空格。您需要使用一种技巧才能使第一个参数以空格开头。例如,如果您想让“info”函数打印“ hello”,您可以这样写
empty :=
space := $(empty) $(empty)
$(info $(space)$(space)hello)
Kconfig 仅使用逗号作为分隔符,并保留函数调用中的所有空格。有些人喜欢在每个逗号分隔符后放置一个空格
$(func-name, arg1, arg2, arg3)
在这种情况下,“func-name”将接收“ arg1”、“ arg2”、“ arg3”。前导空格的存在可能会影响函数。同样适用于 Make - 例如,$(subst .c, .o, $(sources)) 是一个典型的错误;它将“.c”替换为“ .o”。
在 Make 中,用户定义的函数通过使用内置函数“call”来引用,如下所示
$(call my-func,arg1,arg2,arg3)
Kconfig 以相同的方式调用用户定义的函数和内置函数。省略“call”使语法更短。
在 Make 中,某些函数将逗号视为字面量而不是参数分隔符。例如,$(shell echo hello, world) 运行命令“echo hello, world”。同样,$(info hello, world) 将“hello, world”打印到标准输出。你可以说这是一种_有用的_不一致。
在 Kconfig 中,为了更简单的实现和语法一致性,出现在 $( ) 上下文中的逗号始终是分隔符。这意味着
$(shell, echo hello, world)
是一个错误,因为它传递了两个参数,而“shell”函数仅接受一个参数。要在参数中传递逗号,您可以使用以下技巧
comma := ,
$(shell, echo hello$(comma) world)
注意事项¶
变量(或函数)不能跨标记展开。因此,您不能使用变量作为包含多个标记的表达式的简写。以下有效
RANGE_MIN := 1
RANGE_MAX := 3
config FOO
int "foo"
range $(RANGE_MIN) $(RANGE_MAX)
但是,以下不起作用
RANGES := 1 3
config FOO
int "foo"
range $(RANGES)
变量不能扩展为 Kconfig 中的任何关键字。以下不起作用
MY_TYPE := tristate
config FOO
$(MY_TYPE) "foo"
default y
显然从设计来看,$(shell command) 在文本替换阶段展开。您不能将符号传递给“shell”函数。
以下内容无法按预期工作
config ENDIAN_FLAG
string
default "-mbig-endian" if CPU_BIG_ENDIAN
default "-mlittle-endian" if CPU_LITTLE_ENDIAN
config CC_HAS_ENDIAN_FLAG
def_bool $(shell $(srctree)/scripts/gcc-check-flag ENDIAN_FLAG)
相反,您可以像下面这样做,以便静态展开任何函数调用
config CC_HAS_ENDIAN_FLAG
bool
default $(shell $(srctree)/scripts/gcc-check-flag -mbig-endian) if CPU_BIG_ENDIAN
default $(shell $(srctree)/scripts/gcc-check-flag -mlittle-endian) if CPU_LITTLE_ENDIAN