Python Kconfiglib初次学习

1 参考

kconfiglib库官方介绍kconfiglib · PyPI

Kconfiglib源码:GitHub - ulfalizer/Kconfiglib: A flexible Python 2/3 Kconfig implementation and library

Kconfig语法:Kconfig Language — The Linux Kernel documentation

其它参考:

【华为云技术分享】STM32L476移植华为LiteOS系列教程---Kconfig 6 - zhy_learn - 博客园 (cnblogs.com)

menuconfig 和、Kconfig 介绍及例子解析! (qq.com)

驱动模块(6)_Kconfig语法 - Hello-World3 - 博客园 (cnblogs.com)

Kconfig语法简介 - schips - 博客园 (cnblogs.com)

2 Python Kconfiglib

简介:Kconfiglib is a Python 2/3 library for scripting and extracting information from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt) configuration systems.(Kconfiglib是一个从Kconfig配置系统中编写脚本提取信息的库,Kconfig配置系统是Linux内核提出的一个配置数据库,关于Kconfig的语法可参考内核文件)。

几个概念:

  • menuconfig:配置菜单,支持配置的图形化界面。
  • Kconfig:生成menuconfig界面的一种语言,对菜单项进行了描述。
  • .config:通过配置菜单生成的配置文件,一般在Makefile中使用。

本文除了介绍python Kconfiglib库之外,还要重点学习Kconfig的语法规则,了解了语法就知道如何用menuconfig进行配置项的选择。以下章节来自官方的介绍说明。

2.1 Getting started

  1. Install the library and the utilities.(安装库和相关的工具)

  2. Write Kconfig files that describe the available configuration options. See Kconfig Language — The Linux Kernel documentation for some general Kconfig advice.(编写Kconfig文件,用来描述配置选项,可以参考内核的Kconfig语言来学习)

  3. Generate an initial configuration with e.g. menuconfig/guiconfig or alldefconfig. The configuration is saved as .config by default.(使用menuconfig/guiconfig或者alldefconfig生成一个最初的配置,默认保存为.config文件)

    For more advanced projects, the defconfig utility can be used to generate the initial configuration from an existing configuration file. Usually, this existing configuration file would be a minimal configuration file, as generated by e.g. savedefconfig.(对于更高级的用法,defconfig可以从一个已存在的配置文件中生成一个初始化配置,通常已存在的配置文件作为一个最小的配置文件,使用savedefconfig生成配置文件)

  4. Run genconfig to generate a header file. By default, it is saved as config.h.(genconfig 用于生成一个头文件,默认保存为config.h)

    Normally, genconfig would be run automatically as part of the build.(通常,genconfig 作为build的一部分自动执行)

    Before writing a header file or other configuration output, Kconfiglib compares the old contents of the file against the new contents. If there's no change, the write is skipped. This avoids updating file metadata like the modification time, and might save work depending on your build setup.(在输出一个头文件或其它配置文件之前,会先比较旧的输出文件,如果没有改变,直接跳过不输出)

    Adding new configuration output formats should be relatively straightforward. See the implementation of write_config() in kconfiglib.py. The documentation for the Symbol.config_string property has some tips as well.(添加新的配置输出格式时应该相对简单,可以查看write_config的用法,Symbol.config_string属性也有一些提示)

  5. To update an old .config file after the Kconfig files have changed (e.g. to add new options), run oldconfig (prompts for values for new options) or olddefconfig (gives new options their default value). Entering the menuconfig or guiconfig interface and saving the configuration will also update it (the configuration interfaces always prompt for saving on exit if it would modify the contents of the .config file).(当执行oldconfig 或者olddefconfig时,如果Kconfig文件改变时,会更新.config。执行menuconfig 或者guiconfig时,也会更新.config

    Due to Kconfig semantics, simply loading an old .config file performs an implicit olddefconfig, so building will normally not be affected by having an outdated configuration.

2.2 .config

.config files use Make syntax and can be included directly in Makefiles to read configuration values from there. This is why n-valued bool/tristate values are written out as # CONFIG_FOO is not set (a Make comment) in .config, allowing them to be tested with ifdef in Make.(输出的.config文件使用的是Make语法,可以直接在Makefile中包含这个文件读取配置值。bool/tristate类型的n输出为# CONFIG_FOO is not set,Makefile中使用 ifdef方式使用)

.config文件一般格式为:

# LOSCFG_DEBUG_KERNEL is not set
LOSCFG_SHELL=y

Makefile中一般的使用方式:

ifeq ($(LOSCFG_SHELL), y)
    LITEOS_SHELL_INCLUDE  += -I $(LITEOSTOPDIR)/shell/include
endif

If you make use of this, you might want to pass --config-out to genconfig and include the configuration file it generates instead of including .config directly. This has the advantage that the generated configuration file will always be a "full" configuration file, even if .config is outdated. Otherwise, it might be necessary to run old(def)config or menuconfig/guiconfig before rebuilding with an outdated .config.

If you use --sync-deps to generate incremental build information, you can include deps/auto.conf instead, which is also a full configuration file.

2.3 Library features

Kconfiglib can do the following, among other things:(Kconfiglib库可以完成以下事情)

  • Programmatically get and set symbol values(以编程方式获取和设置符合值)

    See allnoconfig.py and allyesconfig.py, which are automatically verified to produce identical output to the standard make allnoconfig and make allyesconfig.

  • Read and write .config and defconfig files(读写.config和deconfig文件)

    The generated .config and defconfig (minimal configuration) files are character-for-character identical to what the C implementation would generate (except for the header comment). The test suite relies on this, as it compares the generated files.(通过库生成的.configdeconfig文件和C语言生成的是一样的,除了头文件注释)

  • Write C headers(写C头文件)

    The generated headers use the same format as include/generated/autoconf.h from the Linux kernel. Output for symbols appears in the order that they're defined, unlike in the C tools (where the order depends on the hash table implementation).(生成的头文件格式和内核的include/generated/autoconf.h文件一样,输出的符号按它们的定义顺序显示)

  • Implement incremental builds(实现增量构建)

    This uses the same scheme as the include/config directory in the kernel: Symbols are translated into files that are touched when the symbol's value changes between builds, which can be used to avoid having to do a full rebuild whenever the configuration is changed.

    See the sync_deps() function for more information.

  • Inspect symbols

    Printing a symbol or other item (which calls __str__()) returns its definition in Kconfig format. This also works for symbols defined in multiple locations.

    A helpful __repr__() is on all objects too.

    All __str__() and __repr__() methods are deliberately implemented with just public APIs, so all symbol information can be fetched separately as well.

  • Inspect expressions

    Expressions use a simple tuple-based format that can be processed manually if needed. Expression printing and evaluation functions are provided, implemented with public APIs.

  • Inspect the menu tree

    The underlying menu tree is exposed, including submenus created implicitly from symbols depending on preceding symbols. This can be used e.g. to implement menuconfig-like functionality.

    See menuconfig.py/guiconfig.py and the minimalistic menuconfig_example.py example.

2.4 Menuconfig interfaces

Three configuration interfaces are currently available:(有三种配置接口,可以看作是三种不同的配置界面)

  • menuconfig.py is a terminal-based configuration interface implemented using the standard Python curses module. xconfig features like showing invisible symbols and showing symbol names are included, and it's possible to jump directly to a symbol in the menu tree (even if it's currently invisible).(menuconfig.py是一个基于终端配置接口)

    See the docstring at the top of menuconfig.py for more information about the terminal menuconfig implementation.(更多信息可以查看menuconfig.py文件的顶部的说明)

  • guiconfig.py is a graphical configuration interface written in Tkinter. Like menuconfig.py, it supports showing all symbols (with invisible symbols in red) and jumping directly to symbols. Symbol values can also be changed directly from the jump-to dialog.

  • pymenuconfig, built by RomaVis, is an older portable Python 2/3 TkInter menuconfig implementation.

3 Kconfig语法

官网参考:

linux/kconfig-language.rst at master · torvalds/linux · GitHub

Kconfig Language — The Linux Kernel documentation

注:以下出现entry 的地方,可以理解为一个菜单项或者一个配置项。

3.1 Introduction

简介:The configuration database is a collection of configuration options organized in a tree structure:(Kconfig就是以树形结构来组织配置选项的一个集合)

Every entry has its own dependencies. These dependencies are used to determine the visibility of an entry. Any child entry is only visible if its parent entry is also visible.(每一个配置都有自己的依赖,这些依赖关系确定了一个配置的可见性,任何子配置只有在父配置可见时才可见。)

3.2 Menu entries

Most entries define a config option; all other entries help to organize them. A single configuration option is defined like this:(大部分配置项都定义了一个配置选项,其它配置项可用来组织它们,一个简单的配置选项如下)

config MODVERSIONS
      bool "Set version information on all module symbols"
      depends on MODULES
      help
        Usually, modules have to be recompiled whenever you switch to a new
        kernel.  ...

Every line starts with a key word and can be followed by multiple arguments. “config”starts a new config entry. The following lines define attributes for this config option. Attributes can be the type of the config option, input prompt, dependencies, help text and default values. A config option can be defined multiple times with the same name, but every definition can have only a single input prompt and the type must not conflict.(每一行都以一个关键字开始,后面可以跟多个参数。config关键字表示一个配置项的开始,紧跟着的定义了一些配置项的属性。可以有以下属性:配置项类型、输入提示、依赖关系、帮助信息、默认值。一个配置项可以使用相同配置名被定义多次,但是每个定义只能有一个输入提示,并且类型不不能冲突。)

3.3 Menu attributes

A menu entry can have a number of attributes. Not all of them are applicable everywhere (see syntax).(一个菜单项可以有多个属性,具体使用参考语法)

  • type definition: bool/tristate/string/hex/int(类型定义:布尔值/三态/字符串/十六进制/整数)

    Every config option must have a type. There are only two basic types: tristate and string; the other types are based on these two. The type definition optionally accepts an input prompt, so these two examples are equivalent:(每一个配置项必须有一种类型。有两种基础类型:三态和字符串。其他的类型都是基于这两种类型。类型定义的输入提示prompt是可选的,因此下面的两种定义是等效的)

    bool "Networking support"
    

    and:

    bool
    prompt "Networking support"
    

    三态:3种状态,对应Y、N、M三种选择方式,就是这个配置项可以被三种选择。如果=y则会被编入(built-in)内核,如果=m会被单独连接成一个”.ko”模块。

  • input prompt: "prompt" ["if" ] (输入提示)

    Every menu entry can have at most one prompt, which is used to display to the user. Optionally dependencies only for this prompt can be added with "if".(每一个菜单项至少有一个输入提示,用于显示给用户,可以通过添加if判断来仅提示某一种提示)

  • default value: "default" ["if" ](默认值)

    A config option can have any number of default values. If multiple default values are visible, only the first defined one is active. Default values are not limited to the menu entry where they are defined. This means the default can be defined somewhere else or be overridden by an earlier definition. The default value is only assigned to the config symbol if no other value was set by the user (via the input prompt above). If an input prompt is visible the default value is presented to the user and can be overridden by him. Optionally, dependencies only for this default value can be added with "if".(配置项可以有任意多个默认值,如果有多个默认值,仅保留第一个默认值。默认值不限于在定义它的菜单项中定义,也可以在其他地方定义。默认值仅在用户没有分配配置值的时候使用,如果输入可见,默认值就呈现给用户。)

The default value deliberately defaults to 'n' in order to avoid bloating the build. With few exceptions, new config options should not change this. The intent is for “make oldconfig” to add as little as possible to the config from release to release.(为了避免编译膨胀,默认值默认为'n'。)

  • Note:

    Things that merit “default y/m” include:A new Kconfig option for something that used to always be built should be “default y”.A new gatekeeping Kconfig option that hides/shows other Kconfig options (but does not generate any code of its own), should be “default y” so people will see those other options.Sub-driver behavior or similar options for a driver that is “default n”. This allows you to provide sane defaults.Hardware or infrastructure that everybody expects, such as CONFIG_NET or CONFIG_BLOCK. These are rare exceptions.

  • type definition + default value:(类型定义+默认值)

    "def_bool"/"def_tristate"  ["if" ]
    

    This is a shorthand notation for a type definition plus a value. Optionally dependencies for this default value can be added with "if".(这是类型定义+值的简写方式)

  • dependencies: "depends on" (依赖)

    This defines a dependency for this menu entry. If multiple dependencies are defined, they are connected with ‘&&’. Dependencies are applied to all other options within this menu entry (which also accept an “if” expression), so these two examples are equivalent:(定义菜单项的依赖。如果要定义多个依赖,可以使用"&&"连接,依赖应用于当前菜单项的所有选项,也可以使用if表达式,下面的两种定义是等价的)

    bool "foo" if BAR
    default y if BAR
    

    and:

    depends on BAR
    bool "foo"
    default y
    
  • reverse dependencies: "select" ["if" ](反向依赖)

    While normal dependencies reduce the upper limit of a symbol (see below), reverse dependencies can be used to force a lower limit of another symbol. The value of the current menu symbol is used as the minimal value can be set to. If is selected multiple times, the limit is set to the largest selection. Reverse dependencies can only be used with boolean or tristate symbols.(正常的依赖是符号在配置项的上面,反向依赖可以让符号在配置项的下面。当前菜单符号的值被用作符号设置的最小值。如果符号被选择多次,这个限制是符号被选择的最大次数。反向依赖只能和布尔符号或三态符号使用)

    • Note:

      select should be used with care. select will force a symbol to a value without visiting the dependencies. By abusing select you are able to select a symbol FOO even if FOO depends on BAR that is not set. In general use select only for non-visible symbols (no prompts anywhere) and for symbols with no dependencies. That will limit the usefulness but on the other hand avoid the illegal configurations all over.(反向依赖应该小心使用,反向依赖将强制设置符号没有可见的依赖项。通常仅对不可见的符号使用反向依赖。)

  • weak reverse dependencies: "imply" ["if" ](弱反向依赖)

    This is similar to “select” as it enforces a lower limit on another symbol except that the "implied" symbol’s value may still be set to n from a direct dependency or with a visible prompt.(类似于反向依赖,强制另一个符号在它的下面,但是根据一个可见的提示项,符号仍然可能设置为'n')

    Given the following example:

    config FOO
        tristate "foo"
        imply BAZ
    
    config BAZ
        tristate "baz"
        depends on BAR
    

    The following values are possible:

    FOO BAR BAZ’s default choice for BAZ
    n y n N/m/y
    m y m M/y/n
    y y y Y/m/n
    n m n N/m
    m m m M/n
    y m m M/n
    y n N

    This is useful e.g. with multiple drivers that want to indicate their ability to hook into a secondary subsystem while allowing the user to configure that subsystem out without also having to unset these drivers.

    Note: If the combination of FOO=y and BAR=m causes a link error, you can guard the function call with IS_REACHABLE():

    foo_init()
    {
            if (IS_REACHABLE(CONFIG_BAZ))
                    baz_register(&foo);
            ...
    }
    

    Note: If the feature provided by BAZ is highly desirable for FOO, FOO should imply not only BAZ, but also its dependency BAR:

    config FOO
        tristate "foo"
        imply BAR
        imply BAZ
    
  • limiting menu display: "visible if" (限制菜单显示)

    This attribute is only applicable to menu blocks, if the condition is false, the menu block is not displayed to the user (the symbols contained there can still be selected by other symbols, though). It is similar to a conditional “prompt” attribute for individual menu entries. Default value of “visible” is true.(这个属性仅作用于菜单块,如果条件为false,菜单块对用户不显示,类似于菜单项的prompt属性,默认为true)

  • numerical ranges: "range" ["if" ](数值范围)

    This allows to limit the range of possible input values for int and hex symbols. The user can only input a value which is larger than or equal to the first symbol and smaller than or equal to the second symbol.(对应int和hex类型,可以限制输入值的范围,用户只能输入范围内的值)

  • help text: "help"(帮助信息)

    This defines a help text. The end of the help text is determined by the indentation level, this means it ends at the first line which has a smaller indentation than the first line of the help text.(定义帮助信息,帮助信息由缩进表示一行的结束)

  • module attribute: "modules"(模块属性)

    This declares the symbol to be used as the MODULES symbol, which enables the third modular state for all config symbols. At most one symbol may have the “modules” option set.

3.4 Menu dependencies

Dependencies define the visibility of a menu entry and can also reduce the input range of tristate symbols. The tristate logic used in the expressions uses one more state than normal boolean logic to express the module state. Dependency expressions have the following syntax:(依赖项定义了菜单项的可见性,也可以减少三态的输入范围。三态的逻辑表达式比布尔表达式多了一种模块状态,依赖的表达式语法如下)

 ::=                            (1)
          '='                 (2)
          '!='                (3)
          '              (4)
          '>'               (4)
          '             (4)
          '>='              (4)
         '('  ')'                       (5)
         '!'                            (6)
          '&&'                    (7)
          '||'                    (8)

Expressions are listed in decreasing order of precedence.

  1. Convert the symbol into an expression. Boolean and tristate symbols are simply converted into the respective expression values. All other symbol types result in 'n'.(将符号转换为表达式。布尔值和三态转换为对应的表达式,其他类型符号转换为'n')
  2. If the values of both symbols are equal, it returns ‘y’, otherwise ‘n’.(如果两个符号相等,返回'y',否则为'n')
  3. If the values of both symbols are equal, it returns ‘n’, otherwise ‘y’.(如果两个符号不相等,返回'y',否则为'n')
  4. If value of is respectively lower, greater, lower-or-equal, or greater-or-equal than value of , it returns ‘y’, otherwise ‘n’.(如果符号小于等于,返回'y',否则为'n')
  5. Returns the value of the expression. Used to override precedence.
  6. Returns the result of (2-/expr/).(返回2-表达式的值)
  7. Returns the result of min(/expr/, /expr/).(返回最小值)
  8. Returns the result of max(/expr/, /expr/).(返回最大值)

An expression can have a value of ‘n’, ‘m’ or ‘y’ (or 0, 1, 2 respectively for calculations). A menu entry becomes visible when its expression evaluates to ‘m’ or ‘y’.(一个表达式的值可以为'n'/'m'/'y',或者0/1/2用于计算,当表达式的结果为'm'/'y',菜单项才可见)

There are two types of symbols: constant and non-constant symbols. Non-constant symbols are the most common ones and are defined with the ‘config’ statement. Non-constant symbols consist entirely of alphanumeric characters or underscores. Constant symbols are only part of expressions. Constant symbols are always surrounded by single or double quotes. Within the quote, any other character is allowed and the quotes can be escaped using ''.(有两种类型符号:常量和非常量符号,非常量符号比较常见,非常量符号完全由字母和下划线组成,常量符号只是表达式的一部分,常量符号通常使用双引号或单引号括起来。)

3.5 Menu structure

menu条目用于生成菜单。

The position of a menu entry in the tree is determined in two ways. First it can be specified explicitly:(菜单项的位置有两种方式,第一种可以显示的指定。)

menu "Network device support"
      depends on NET

config NETDEVICES
      ...

endmenu

All entries within the “menu” … “endmenu” block become a submenu of “Network device support”. All subentries inherit the dependencies from the menu entry, e.g. this means the dependency “NET” is added to the dependency list of the config option NETDEVICES.(所有在"menu" … "endmenu"结构中的菜单项,都成为"Network device support"的一个子菜单,所有的子菜单都继承自菜单项,比如"NET"也是子菜单项"NETDEVICES"的依赖项)

The other way to generate the menu structure is done by analyzing the dependencies. If a menu entry somehow depends on the previous entry, it can be made a submenu of it. First, the previous (parent) symbol must be part of the dependency list and then one of these two conditions must be true:(生成菜单结构的另一种方式是解析依赖,如果一个菜单项依赖前一个菜单项,那么它就成为了子菜单,之前的菜单(父菜单)必须成为依赖的一部分)

  • the child entry must become invisible, if the parent is set to 'n'(如果父菜单项设置为'n',子菜单就不可见)

  • the child entry must only be visible, if the parent is visible:(如果父菜单可见,则子菜单必须可见)

    config MODULES
        bool "Enable loadable module support"
    
    config MODVERSIONS
        bool "Set version information on all module symbols"
        depends on MODULES
    
    comment "module support disabled"
        depends on !MODULES
    

MODVERSIONS directly depends on MODULES, this means it’s only visible if MODULES is different from 'n'. The comment on the other hand is only visible when MODULES is set to 'n'.()

Python Kconfiglib初次学习插图

3.6 Kconfig syntax

The configuration file describes a series of menu entries, where every line starts with a keyword (except help texts). The following keywords end a menu entry:(配置文件描述了一系列配置项,每一个配置项都以一个关键字开头,帮助信息除外,以关键字结束)

  • config
  • menuconfig
  • choice/endchoice
  • comment
  • menu/endmenu
  • if/endif
  • source

The first five also start the definition of a menu entry.(前5条用于开始定义菜单项)

  • config:
"config" 

This defines a config symbol and accepts any of above attributes as options.(定义一个config符号)

  • menuconfig:
"menuconfig" 

This is similar to the simple config entry above, but it also gives a hint to front ends, that all suboptions should be displayed as a separate list of options. To make sure all the suboptions will really show up under the menuconfig entry and not outside of it, every item from the list must depend on the menuconfig symbol. In practice, this is achieved by using one of the next two constructs:(和上面的config类似,但menuconfig给出了一个提示端,它所有的子选项都显示为一个单独的选项显示在菜单配置项下)

(1):
menuconfig M
if M
    config C1
    config C2
endif

(2):
menuconfig M
config C1
    depends on M
config C2
    depends on M

Python Kconfiglib初次学习插图1

In the following examples (3) and (4), C1 and C2 still have the M dependency, but will not appear under menuconfig M anymore, because of C0, which doesn’t depend on M:(以下示例中,C1和C2依赖于M,但不会出现在菜单M下面,因为C0不依赖M)

(3):
menuconfig M
    config C0
if M
    config C1
    config C2
endif

(4):
menuconfig M
config C0
config C1
    depends on M
config C2
    depends on M

Python Kconfiglib初次学习插图2

  • choices

choice条目将多个类似的配置选项组合在一起,供用户单选或者多选。

"choice" [symbol]


"endchoice"

This defines a choice group and accepts any of the above attributes as options. A choice can only be of type bool or tristate. If no type is specified for a choice, its type will be determined by the type of the first choice element in the group or remain unknown if none of the choice elements have a type specified, as well.(定义一个选项组,使用上述的任何属性作为输入。一个选项只能是布尔型或三态型,如果没有定义类型,它的类型由组内第一个元素指定)

While a boolean choice only allows a single config entry to be selected, a tristate choice also allows any number of config entries to be set to 'm'. This can be used if multiple drivers for a single hardware exists and only a single driver can be compiled/loaded into the kernel, but all drivers can be compiled as modules.(一个布尔选项仅允许一个配置项选择,一个三态选项可以允许选择任意多个配置项设置为'm',适用于一个硬件有多个驱动程序的情况并且仅一个驱动可以被编译进内核,但是所有的驱动可以被编译为模块)

A choice accepts another option "optional", which allows to set the choice to 'n' and no entry needs to be selected. If no [symbol] is associated with a choice, then you can not have multiple definitions of that choice. If a [symbol] is associated to the choice, then you may define the same choice (i.e. with the same entries) in another place.(一个选择项接受另一个可选项"optional",这种情况允许设置为'n'并且没有配置项需要被选择,如果没有[symbol]与另一个选择项关联,则不能有对这个选择项有多种定义。如果一个[symbol]关联一个选择项,那你可以在另一个地方定义相同的选择项。)

  • comment:

comment条目定义了一些帮助信息,它在配置过程中出现在界面的第一行,并且这些帮助信息会出现在配置文件中。

"comment" 

This defines a comment which is displayed to the user during the configuration process and is also echoed to the output files. The only possible options are dependencies.(定义了一个注释项,它在配置过程中显示给用户,同时也显示在输出文件中,唯一的可选项是依赖)

  • menu:
"menu" 


"endmenu"

This defines a menu block, see “Menu structure” above for more information. The only possible options are dependencies and “visible” attributes.(定义一个菜单块,可以参考“Menu structure”,可选择项可能是依赖或者visible属性)

  • if:
"if" 

"endif"

This defines an if block. The dependency expression is appended to all enclosed menu entries.(定义一个if块,依赖表达式附加到所有附带菜单项)

  • source:
"source" 

This reads the specified configuration file. This file is always parsed.(读取指定的配置文件,读取的文件也会被解析)

  • mainmenu:
"mainmenu" 

This sets the config program's title bar if the config program chooses to use it. It should be placed at the top of the configuration, before any other statement.(设置配置菜单的标签栏,这个应该放在配置文件的开头)

  • '#' Kconfig source file comment:

An unquoted # character anywhere in a source file line indicates the beginning of a source file comment. The remainder of that line is a comment.(在源文件开头,不带引号的'#'表示注释)

3.7 Kconfig hints

This is a collection of Kconfig tips, most of which aren't obvious at first glance and most of which have become idioms in several Kconfig files.(Kconfig提示,大部分提示已经在kconfig文件中)

  • Adding common features and make the usage configurable(添加常用特性并让用法可配置)

It is a common idiom to implement a feature/functionality that are relevant for some architectures but not all. The recommended way to do so is to use a config variable named HAVE_* that is defined in a common Kconfig file and selected by the relevant architectures. An example is the generic IOMAP functionality.(实现一个跟某架构不紧密关联的特定/功能,推荐的方式是使用以HAVE_*开头的配置变量,并在kconfig文件中来选择架构,以IOMAP为例说明)

We would in lib/Kconfig see:

# Generic IOMAP is used to ...
config HAVE_GENERIC_IOMAP

config GENERIC_IOMAP
      depends on HAVE_GENERIC_IOMAP && FOO

And in lib/Makefile we would see:

obj-$(CONFIG_GENERIC_IOMAP) += iomap.o

For each architecture using the generic IOMAP functionality we would see:

config X86
      select ...
      select HAVE_GENERIC_IOMAP
      select ...

Note: we use the existing config option and avoid creating a new config variable to select HAVE_GENERIC_IOMAP.

Note: the use of the internal config variable HAVE_GENERIC_IOMAP, it is introduced to overcome the limitation of select which will force a config option to 'y' no matter the dependencies. The dependencies are moved to the symbol GENERIC_IOMAP and we avoid the situation where select forces a symbol equals to 'y'.

  • Adding features that need compiler support(添加需要编译器支持的特性)

There are several features that need compiler support. The recommended way to describe the dependency on the compiler feature is to use "depends on" followed by a test macro:(有几个特性需要编译器支持,推荐在对编译器的依赖时使用"depends on"+测试宏的方式)

config STACKPROTECTOR
      bool "Stack Protector buffer overflow detection"
      depends on $(cc-option,-fstack-protector)
      ...

If you need to expose a compiler capability to makefiles and/or C source files, CC_HAS_ is the recommended prefix for the config option:(如果需要将编译器相关功能提供给Makefile/C文件使用,建议使用CC_HAS_前缀)

config CC_HAS_FOO
      def_bool $(success,$(srctree)/scripts/cc-check-foo.sh $(CC))
  • Build as module only(作为模块编译)

To restrict a component build to module-only, qualify its config symbol with "depends on m". E.g.:(将组件限制为仅编译为模块,可以使用配置符号"depends on m")

config FOO
      depends on BAR && m

limits FOO to module (=m) or disabled (=n).(将FOO限制为模块(=m),或者不使用(=n))

4 编码测试

4.1 环境信息

Linux下测试,版本:Centos7(如果是在Windows下测试,除了安装kconfiglib外,还需要安装windows_curses库。)

使用的Python版本:3.8 https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz

使用的Kconfiglib库版本:14.1.0

4.1 文件组织

需要用到以下文件:

  • config.in:Kconfig配置文件
  • Makefile:Makefile文件
  • menu_main.py:调用Kconfiglib库的脚本

输出文件:

  • .config:输出的.config配置,一般在Makefile中使用
  • autoconfig.h:输出的C头文件

config.in:Kconfig配置文件在后续测试过程中会一直更改,这里就不进行说明。

Makefile文件中的内容(根据不同的配置项执行脚本输入不同的参数):

.PHONY: menuconfig savemenuconfig defconfig allyesconfig allnoconfig

# 进入配置菜单
menuconfig:
	python menu_main.py

# 保存配置菜单,如果之前通过配置菜单更改了配置值,将保存修改后的值
savemenuconfig:
	python menu_main.py savemenuconfig

# 根据配置文件中的结构,输出默认配置,同时会覆盖menuconfig配置界面中之前的配置信息
defconfig:
	python menu_main.py defconfig

# 将尽可能多的配置项配置为'y’
allyesconfig:
	python menu_main.py allyesconfig

# 将尽可能多的配置项配置为'n’
allnoconfig:
	python menu_main.py allnoconfig

menu_main.py文件中的内容,

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os, sys
from kconfiglib import Kconfig
from menuconfig import menuconfig

# 设置Kconfig环境变量
def mconf_set_env():
    os.environ["MENUCONFIG_STYLE"]      = "default selection=fg:white,bg:red" # 配置menuconfig的颜色主题
    os.environ["KCONFIG_CONFIG"]        = os.path.join(".config")                # 输出的配置文件名
    os.environ["KCONFIG_CONFIG_HEADER"] = "# Generated by Kconfig Tooln"        # 设置生成的配置文件开头的字符串,会在生成的配置文件开头的第一行进行显示。
    os.environ["KCONFIG_AUTOHEADER"]    = os.path.join("autoconfig.h")           # 指定输出符号到C头文件的文件名
    # os.environ["CONFIG_"]               = "MYTEST"

def mconfig(argv):
    mconf_set_env()
    kconfig = os.path.join("config.in") # menuconfig配置文件
    kconf = Kconfig(filename=kconfig)

    if len(argv) == 2 and argv[1] == 'savemenuconfig':
        kconf.load_config()         # 加载配置
        print(kconf.write_config()) # 保存配置到配置文件
    elif len(argv) == 2 and argv[1] == 'defconfig':
        kconf.load_allconfig("alldef.config") # 加载默认配置
        print(kconf.write_config())
    elif len(argv) == 2 and argv[1] == 'allyesconfig':
        kconf.warn = False
        for sym in kconf.unique_defined_syms:
            sym.set_value(1 if sym.choice else 2)
        for choice in kconf.unique_choices:
            choice.set_value(2)
        kconf.warn = True
        kconf.load_allconfig("allyes.config") # 尽可能设置多的配置为'y'
        print(kconf.write_config())
    elif len(argv) == 2 and argv[1] == 'allnoconfig':
        kconf.warn = False
        for sym in kconf.unique_defined_syms:
            sym.set_value(2 if sym.is_allnoconfig_y else 0)
        kconf.warn = True
        kconf.load_allconfig("allno.config") # 尽可能设置多的配置为'n'
        print(kconf.write_config())
    else:
        print("python menuconfig ... n")
        menuconfig(kconf)

    # 保存autoconfig.h
    kconf.write_autoconf()


if __name__ == "__main__":
    print("start ... n")
    mconfig(sys.argv)

4.2 部分函数说明

  • Kconfig类

参考:https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py

功能:创建一个Kconfig配置系统,默认的配置文件名为kconfig,用户可以通过filename更改配置文件名。

class Kconfig(object):
    def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True, encoding="utf-8", suppress_traceback=False):

参数filename:加载的Kconfig文件名。

参数warn:为True(默认)表示解析配置文件过程中产生可能的警告。可以参考 Kconfig.warn_*变量,用于在使能警告时启用或禁止某些警告。所有生成的警告都会添加到Kconfig.warnings列表中。

参数warn_to_stderr:解析配置文件过程中,产生的警告是否打印到标准输出stderr(默认为True)。

  • load_config函数

功能:从.config格式文件中加载符号值。等同于调用Symbol.set_value()函数设置每个值。在.config文件中 "# CONFIG_FOO is not set"将设置符号'FOO'的值为'n'。调用这个函数同时会更新Kconfig.missing_syms属性,其中包括配置文件中未定义符号的所有赋值。如果replace为真,Kconfig.missing_syms被清除,否则被追加。请参考Kconfig.missing_syms配置文档。

def load_config(self, filename=None, replace=True, verbose=None):

参数filename:指定加载配置文件名。如果filename为空(默认),配置文件将自动加载,加载方式如下:

  1. 如果KCONFIG_CONFIG环境变量设置了,它指定了加载配置文件的路径。否则,.config被当作默认文件名。
  2. 如果第1.中指定的文件不存在,将加载kconf.defconfig_filename指定的文件,这是由'option defconfig_list'符号派生的。
  3. 如果1.和2.都没有发现配置文件,那么将不会加载配置文件,并且返回一个错误值。
  • write_config函数

功能:向.config格式的配置文件中输出符号值。符号生成顺序和kconfig文件中出现一样。对于在多个地方定义的符号,将在符号第一个地方定义的地方输出符号。

def write_config(self, filename=None, header=None, save_old=True, verbose=None):

参数filename:指定输出配置文件名。如果为空(默认),文件名将由环境变量KCONFIG_CONFIG指定,否则默认为.config

参数header:在配置文件中的开头插入的字符串,应该使用"#"开头的注释,并以换行符结束。如果为空(默认),可通过环境变量KCONFIG_CONFIG_HEADER指定,否则为空。

参数save_old:如果当前指定的配置文件名已经存在,在写入新的配置文件前,是否在当前目录重新拷贝一份并命名为.old。默认为"TRUE"。如果.old不能写,将返回错误。

返回值:返回一个字符串,说明文件已经保存或者文件没有改变。可以直接使用print(kconf.write_config()).方式调用。

  • load_allconfig函数

功能:针对所有all*config,加载(合并)由KCONFIG_ALLCONFIG指定的配置文件,可参考Linux内核Documentation/kbuild/kconfig.txt

def load_allconfig(self, filename):

参数filename:特定的配置文件名,比如:"allyes.config"、"allno.config"

(1)设置配置项的值为默认值

kconf.load_allconfig("alldef.config")

(2)将尽可能多的配置项值设为'y'

Kconfiglib/allyesconfig.py at master · ulfalizer/Kconfiglib · GitHub中的示例:

def main():
    kconf = kconfiglib.standard_kconfig(__doc__)

    # Avoid warnings that would otherwise get printed by Kconfiglib for the
    # following: (避免Kconfiglib打印以下警告)
    #
    # 1. Assigning a value to a symbol without a prompt, which never has any
    #    effect (在没有提示符的情况下对符号赋值,该符号从没有提示符)
    #
    # 2. Assigning values invalid for the type (only bool/tristate symbols
    #    accept 0/1/2, for n/m/y). The assignments will be ignored for other
    #    symbol types, which is what we want. (对某类型赋无效值,只有布尔和三态类型可以赋值为0/1/2,表示n/m/y。对于其它类型符号,赋的值会被忽略。)
    kconf.warn = False

    # Try to set all symbols to 'y'. Dependencies might truncate the value down
    # later, but this will at least give the highest possible value.(试着对所有符号赋值为'y'。依赖关系可能在后面会被截断,但这至少会给出最多的值。)
    #
    # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex
    # symbols still take a string, because they preserve formatting).(对于非布尔/三态类型的符号赋值为0/1/2没有作用,因为它们保留了格式)
    for sym in kconf.unique_defined_syms:
        # Set choice symbols to 'm'. This value will be ignored for choices in
        # 'y' mode (the "normal" mode), which will instead just get their
        # default selection, but will set all symbols in m-mode choices to 'm',
        # which is as high as they can go.(设置choice符号依赖为'm'。对于'y'模式(正常模式)下的choices,这个值会被忽略,
        # 它只会得到它们默认的选择,但会将'm'模式中的所有符号设置为'm',这是它们可以设置的最大值。)
        # Here's a convoluted example of how you might get an m-mode choice
        # even during allyesconfig:(下面是一个示例,说明配置为allyesconfig,如何获得'm'模式的choice)
        #
        #   choice OPT_BOOL_CHOICE
        #   	tristate "weird choice"
        #   	depends on m
        #   
        #   config OPT_BOOL_CHOICE_SYM_1
        #   	tristate "Optional bool choice sym 1"
        #   
        #   config OPT_BOOL_CHOICE_SYM_2
        #   	tristate "Optional bool choice sym 2"
        #   
        #   endchoice
        #   
        #   输出为:
        #        OPT_BOOL_CHOICE_SYM_1=m
        #        OPT_BOOL_CHOICE_SYM_2=m
        sym.set_value(1 if sym.choice else 2)

    # Set all choices to the highest possible mode(设置所有选项值为最高模式,也就是最大值'y')
    for choice in kconf.unique_choices:
        choice.set_value(2)

    kconf.warn = True

    kconf.load_allconfig("allyes.config")

    print(kconf.write_config())

(3)设置尽可能多的配置项值设为'n':

Kconfiglib/allnoconfig.py at master · ulfalizer/Kconfiglib · GitHub的示例:

def main():
    kconf = kconfiglib.standard_kconfig(__doc__)

    # Avoid warnings that would otherwise get printed by Kconfiglib for the
    # following: (避免Kconfiglib打印以下警告)
    #
    # 1. Assigning a value to a symbol without a prompt, which never has any
    #    effect (在没有提示符的情况下对符号赋值,该符号从没有提示符)
    #
    # 2. Assigning values invalid for the type (only bool/tristate symbols
    #    accept 0/1/2, for n/m/y). The assignments will be ignored for other
    #    symbol types, which is what we want. (对某类型赋无效值,只有布尔和三态类型可以赋值为0/1/2,表示n/m/y。对于其它类型符号,赋的值会被忽略。)
    kconf.warn = False
    for sym in kconf.unique_defined_syms:
        sym.set_value(2 if sym.is_allnoconfig_y else 0)
    kconf.warn = True

    kconf.load_allconfig("allno.config")

    print(kconf.write_config())
  • menuconfig.py

参考:Kconfiglib/menuconfig.py at master · ulfalizer/Kconfiglib · GitHub

说明:menuconfig.py既可以作为独立的可执行文件运行,也可以调用menuconfig()函数时传入Kconfig实例来运行。第二个选项有点不灵活,因为它仍然会加载和保存.config等。

当以独立模式运行时,要加载的kconfig配置文件可以通过参数传递,如果没有参数,则默认的"Kconfig"文件。环境变量KCONFIG_CONFIG指定了加载和保存的.config文件名,如果没有设置,默认为.config文件名。当覆盖一个配置文件时,旧的文件可以指定.old保存文件名。

def menuconfig(kconf):

函数说明:启动用户配置界面,用户退出后返回。

参数kconf:配置的Kconfig类的实例。

配置系统中的使用到的一些按键及其作用:

J/K     : Down/Up
L       : Enter menu/Toggle item
H       : Leave menu
Ctrl-D/U: Page Down/Page Up
G/End   : Jump to end of list
g/Home  : Jump to beginning of list
[Space] toggles values if possible, and enters menus otherwise. 
[Enter] works the other way around.
  • write_autoconf函数
def write_autoconf(self, filename=None, header=None):

函数说明:输出符号值作为C头文件,与内核的include/generated/autoconf.h文件的格式匹配。#define的顺序和write_config()生成的一致。如果文件名'filename'存在,并且它的内容和要写入的内容一致,则将保持不变。

参数filename:写入头文件路径,如果为空(默认),则文件名由环境变量KCONFIG_AUTOHEADER指定,否则为include/generated/autoconf.h

参数header:在文件的开头插入的文本,通常是一些格式为/* ... */的注释,并且以换行符结束。如果为空(默认),它的值可以由环境变量KCONFIG_AUTOHEADER_HEADER指定,否则不添加文件头注释。

返回值:返回一个字符串,说明头文件已经保存或者头文件没有改变。可以直接使用print(kconf.write_autoconf()).方式调用。

4.2 部分环境变量说明

  • MENUCONFIG_STYLE:设置menuconfig的颜色主题,使用方式可以查看库源文件menuconfig.py中的描述。比如:
os.environ["MENUCONFIG_STYLE"] = "default selection=fg:white,bg:blue"

default:表示主题类型,支持三种:

- default       classic Kconfiglib theme with a yellow accent
- monochrome    colorless theme (uses only bold and standout) attributes, this style is used if the terminal doesn't support colors
- aquatic       blue-tinted style loosely resembling the lxdialog theme

selection=fg:white,bg:blue:配置选项的样式

fg:white表示配置前景色和背景色为白色。

bg:blue表示配置背景色为蓝色。

其它使用举例:

MENUCONFIG_STYLE="aquatic selection=fg:white,bg:red"
MENUCONFIG_STYLE="default selection=fg:white,bg:red"
  • KCONFIG_CONFIG_HEADER:设置生成的配置文件开头的字符串,会在生成的配置文件开头的第一行进行显示。
  • KCONFIG_CONFIG:输出的配置文件名,如果不配置,默认输出.config
  • KCONFIG_AUTOHEADER:指定输出符号到C头文件的文件名,否则默认输出到include/generated/autoconf.h
  • CONFIG_:指定输出配置文件中字符串的前缀,比如设置为os.environ["CONFIG_"] = "MYTEST",则输出字符串的格式为MYTEST_XXXX,不配置默认格式为CONFIG_XXXX

4.3 测试

4.1 kmenuconfig

配置文件config.in如下,这里直接使用的是库中的示例(Kconfiglib/Kmenuconfig at master · ulfalizer/Kconfiglib · GitHub):

mainmenu "Example Kconfig configuration"

config MODULES
	bool "Enable loadable module support"
	option modules
	default y

menu "Bool and tristate symbols"

config BOOL
	bool "Bool symbol"
	default y

config BOOL_DEP
	bool "Dependent bool symbol"
	depends on BOOL

# Mix it up a bit with an 'if' instead of a 'depends on'
if BOOL

config TRI_DEP
	tristate "Dependent tristate symbol"
	select SELECTED_BY_TRI_DEP
	imply IMPLIED_BY_TRI_DEP

endif

config TWO_MENU_NODES
	bool "First prompt"
	depends on BOOL

config TRI
	tristate "Tristate symbol"

config TWO_MENU_NODES
	bool "Second prompt"

comment "These are selected by TRI_DEP"

config SELECTED_BY_TRI_DEP
	tristate "Tristate selected by TRI_DEP"

config IMPLIED_BY_TRI_DEP
	tristate "Tristate implied by TRI_DEP"

endmenu


menu "String, int, and hex symbols"

config STRING
	string "String symbol"
	default "foo"

config INT
	int "Int symbol"
	default 747

config HEX
	hex "Hex symbol"
	default 0xABC

endmenu


menu "Various choices"

choice BOOL_CHOICE
	bool "Bool choice"

config BOOL_CHOICE_SYM_1
	bool "Bool choice sym 1"

config BOOL_CHOICE_SYM_2
	bool "Bool choice sym 2"

endchoice

choice TRI_CHOICE
	tristate "Tristate choice"

config TRI_CHOICE_SYM_1
	tristate "Tristate choice sym 1"

config TRI_CHOICE_SYM_2
	tristate "Tristate choice sym 2"

endchoice

choice OPT_BOOL_CHOICE
	bool "Optional bool choice"
	optional

config OPT_BOOL_CHOICE_SYM_1
	bool "Optional bool choice sym 1"

config OPT_BOOL_CHOICE_SYM_2
	bool "Optional bool choice sym 2"

endchoice

endmenu

执行命令make defconfig输出默认配置。

$ more autoconfig.h 
#define CONFIG_MODULES 1
#define CONFIG_BOOL 1
#define CONFIG_STRING "foo"
#define CONFIG_INT 747
#define CONFIG_HEX 0xABC
#define CONFIG_BOOL_CHOICE_SYM_1 1

$ more .config 
# Generated by Kconfig Tool
CONFIG_MODULES=y

#
# Bool and tristate symbols
#
CONFIG_BOOL=y
# CONFIG_BOOL_DEP is not set
# CONFIG_TRI_DEP is not set
# CONFIG_TWO_MENU_NODES is not set
# CONFIG_TRI is not set

#
# These are selected by TRI_DEP
#
# CONFIG_SELECTED_BY_TRI_DEP is not set
# CONFIG_IMPLIED_BY_TRI_DEP is not set
# end of Bool and tristate symbols

#
# String, int, and hex symbols
#
CONFIG_STRING="foo"
CONFIG_INT=747
CONFIG_HEX=0xABC
# end of String, int, and hex symbols

#
# Various choices
#
CONFIG_BOOL_CHOICE_SYM_1=y
# CONFIG_BOOL_CHOICE_SYM_2 is not set
# CONFIG_TRI_CHOICE_SYM_1 is not set
# CONFIG_TRI_CHOICE_SYM_2 is not set
# end of Various choices

下面分开进行学习。

配置项为布尔类型,默认为'y'。

mainmenu "Example Kconfig configuration"

config MODULES
	bool "Enable loadable module support" # 布尔类型
	option modules # 声明将符号编译为MODULES符号
	default y # 默认为'y'

Python Kconfiglib初次学习插图3

Bool and tristate

输入的kconfig文件类型值:

mainmenu "Example Kconfig configuration"

menu "Bool and tristate symbols"

config BOOL
	bool "Bool symbol"
	default y

config BOOL_DEP
	bool "Dependent bool symbol"
	depends on BOOL

# Mix it up a bit with an 'if' instead of a 'depends on'
if BOOL

config TRI_DEP
	tristate "Dependent tristate symbol"
	select SELECTED_BY_TRI_DEP # 反向依赖
	imply IMPLIED_BY_TRI_DEP   # 弱反向依赖

endif

config TWO_MENU_NODES
	bool "First prompt"
	depends on BOOL

config TRI
	tristate "Tristate symbol"

config TWO_MENU_NODES
	bool "Second prompt"

comment "These are selected by TRI_DEP"

config SELECTED_BY_TRI_DEP
	tristate "Tristate selected by TRI_DEP"

config IMPLIED_BY_TRI_DEP
	tristate "Tristate implied by TRI_DEP"

endmenu

Python Kconfiglib初次学习插图4

(1)根据初始化配置,‘Bool symbol’默认为'y',根据缩进可以看出,它下面的三项依赖于‘Bool symbol’。

(2)根据‘Bool symbol’为'n',依赖它的配置项就不会显示。

(3)select的用法:配置项配置之后,select选择的配置项也会自动为'y',imply指定的配置项还是可以配置。

(4)两个相同名的配置项,只要有一个被选择,另一个会同时选择上。

(5)把所有的配置项都选上,输出的配置文件和头文件信息为:

$ more .config
# Generated by Kconfig Tool

#
# Bool and tristate symbols
#
CONFIG_BOOL=y
CONFIG_BOOL_DEP=y
CONFIG_TRI_DEP=y
CONFIG_TWO_MENU_NODES=y
CONFIG_TRI=y

#
# These are selected by TRI_DEP
#
CONFIG_SELECTED_BY_TRI_DEP=y
CONFIG_IMPLIED_BY_TRI_DEP=y
# end of Bool and tristate symbols

$ more autoconfig.h 
#define CONFIG_BOOL 1
#define CONFIG_BOOL_DEP 1
#define CONFIG_TRI_DEP 1
#define CONFIG_TWO_MENU_NODES 1
#define CONFIG_TRI 1
#define CONFIG_SELECTED_BY_TRI_DEP 1
#define CONFIG_IMPLIED_BY_TRI_DEP 1

补充一个介绍tristate的示例,kconfig如下:

config MODULES
	bool "Enable loadable module support"
	option modules
	default y

config BO_1
    tristate "BO_1"

Python Kconfiglib初次学习插图5

  • MODULES为'y'时,BO_1被当作三态类型,可以定义为'm'/'y'/'n';
  • MODULES为'n'时,BO_1被当作布尔类型,可以定义为'y'/'n';

int hex and string

输入的kconfig文件中的配置项:

mainmenu "Example Kconfig configuration"

menu "String, int, and hex symbols"

config STRING
	string "String symbol"
	default "foo"

config INT
	int "Int symbol"
	default 747

config HEX
	hex "Hex symbol"
	default 0xABC

endmenu

输出的默认值:

$ make defconfig
python menu_main.py defconfig
start ... 

Configuration saved to '.config'

$ more .config
# Generated by Kconfig Tool

#
# String, int, and hex symbols
#
CONFIG_STRING="foo"
CONFIG_INT=747
CONFIG_HEX=0xABC
# end of String, int, and hex symbols

$ more autoconfig.h 
#define CONFIG_STRING "foo"
#define CONFIG_INT 747
#define CONFIG_HEX 0xABC

更改配置值,更改字符串为"test",int类型为1000,hex类型为0x789:

Python Kconfiglib初次学习插图6

$ more .config
# Generated by Huawei LiteOS Kconfig Tool
#
# String, int, and hex symbols
#
STRING="test"
INT=1000
HEX=0x789
# end of String, int, and hex symbols

$ more autoconfig.h
/*
 Generated by Huawei LiteOS Kconfig Tool
*/
#define STRING "test"
#define INT 1000
#define HEX 0x789

choice

输入的kconfig配置文件:

mainmenu "Example Kconfig configuration"

menu "Various choices"

config MODULES
	bool "Enable loadable module support"
	option modules
	default y

choice BOOL_CHOICE
	bool "Bool choice"

config BOOL_CHOICE_SYM_1
	bool "Bool choice sym 1"

config BOOL_CHOICE_SYM_2
	bool "Bool choice sym 2"

endchoice

choice TRI_CHOICE
	tristate "Tristate choice"

config TRI_CHOICE_SYM_1
	tristate "Tristate choice sym 1"

config TRI_CHOICE_SYM_2
	tristate "Tristate choice sym 2"

endchoice

choice OPT_BOOL_CHOICE
	bool "Optional bool choice"
	optional

config OPT_BOOL_CHOICE_SYM_1
	bool "Optional bool choice sym 1"

config OPT_BOOL_CHOICE_SYM_2
	bool "Optional bool choice sym 2"

endchoice

endmenu

默认的配置项的值:

$ make defconfig
python usr_config.py defconfig
Configuration saved to '.config'
Kconfig header saved to 'autoconfig.h'

$ more .config
# Generated by Huawei LiteOS Kconfig Tool
#
# Various choices
#
BOOL_CHOICE_SYM_1=y
# BOOL_CHOICE_SYM_2 is not set
TRI_CHOICE_SYM_1=y
# TRI_CHOICE_SYM_2 is not set
# end of Various choices

$ more autoconfig.h
/*
 Generated by Huawei LiteOS Kconfig Tool
*/
#define BOOL_CHOICE_SYM_1 1
#define TRI_CHOICE_SYM_1 1

更改配置项的值:

Python Kconfiglib初次学习插图7

(1)默认'optional'配置项OPT_BOOL_CHOICE不会被选择,因为optional属性。

(2)BOOL_CHOICE配置项选择第二个配置。

(3)(4)当'MODULES'配置项选择为y,表示可以配置为'm','TRI_CHOICE'配置项这时可以同时配置多个配置项为'm'或者'n'。

(5)当'MODULES'配置项选择为n,'TRI_CHOICE'被当作bool类型,仅能从多个配置项中选择一个,默认选择第一个。

(6)选择OPT_BOOL_CHOICE配置项,默认会选择其子项的第一个配置项;

(5)OPT_BOOL_CHOICE配置项选择第二个配置。

$ more .config
# Generated by Huawei LiteOS Kconfig Tool
#
# Various choices
#
# BOOL_CHOICE_SYM_1 is not set
BOOL_CHOICE_SYM_2=y
# TRI_CHOICE_SYM_1 is not set
TRI_CHOICE_SYM_2=y
# OPT_BOOL_CHOICE_SYM_1 is not set
OPT_BOOL_CHOICE_SYM_2=y
# end of Various choices

$ more autoconfig.h
/*
 Generated by Huawei LiteOS Kconfig Tool
*/
#define BOOL_CHOICE_SYM_2 1
#define TRI_CHOICE_SYM_2 1
#define OPT_BOOL_CHOICE_SYM_2 1

总结:

  • choice 开始,endchoice 结束。
  • choice 中间可以加入其他配置,以及 choice 嵌套
  • choice配置之间只能有一个被设置为'y',表示进行选择。
  • MODULES配置项配置为'y',表示支持配置为'm',这时tristate类型配置项可以同时多个配置为'm'或者'n'。
  • OPT_BOOL_CHOICE配置项默认不会选择,选择之后默认会选择第一个配置项;

4.2 打印kconfig配置树

Kconfiglib库中给的示例,用于打印Kconfig文件的树形关系:Kconfiglib/print_tree.py at master · ulfalizer/Kconfiglib · GitHub

源码实现:

# Prints the menu tree of the configuration. Dependencies between symbols can
# sometimes implicitly alter the menu structure (see kconfig-language.txt), and
# that's implemented too.
#
# Note: See the Kconfig.node_iter() function as well, which provides a simpler
# interface for walking the menu tree.

import sys

from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT

def indent_print(s, indent):
    print(indent*" " + s)

def print_items(node, indent):
    while node:
        if isinstance(node.item, Symbol):
            indent_print("config " + node.item.name, indent)

        elif isinstance(node.item, Choice):
            indent_print("choice", indent)

        elif node.item == MENU:
            indent_print('menu "{}"'.format(node.prompt[0]), indent)

        elif node.item == COMMENT:
            indent_print('comment "{}"'.format(node.prompt[0]), indent)


        if node.list:
            print_items(node.list, indent + 2)

        node = node.next

if __name__ == "__main__":
    kconf = Kconfig(sys.argv[1])
    print_items(kconf.top_node, 0)

测试的Kconfig配置文件:

mainmenu "Example Kconfig configuration"

menu "Various choices"

choice BOOL_CHOICE
	bool "Bool choice"

config BOOL_CHOICE_SYM_1
	bool "Bool choice sym 1"

config BOOL_CHOICE_SYM_2
	bool "Bool choice sym 2"

endchoice

choice TRI_CHOICE
	tristate "Tristate choice"

config TRI_CHOICE_SYM_1
	tristate "Tristate choice sym 1"

config TRI_CHOICE_SYM_2
	tristate "Tristate choice sym 2"

endchoice

choice OPT_BOOL_CHOICE
	bool "Optional bool choice"
	optional

config OPT_BOOL_CHOICE_SYM_1
	bool "Optional bool choice sym 1"

config OPT_BOOL_CHOICE_SYM_2
	bool "Optional bool choice sym 2"

endchoice

endmenu

运行,测试:

$ python print_tree.py kconfig
menu "Example Kconfig configuration"
  menu "Various choices"
    choice
      config BOOL_CHOICE_SYM_1
      config BOOL_CHOICE_SYM_2
    choice
      config TRI_CHOICE_SYM_1
      config TRI_CHOICE_SYM_2
    choice
      config OPT_BOOL_CHOICE_SYM_1
      config OPT_BOOL_CHOICE_SYM_2

4.3 choice详细分析

使用Kconfiglib/Kchoice at master · ulfalizer/Kconfiglib · GitHub中的kconfig文件来进行测试:

  • bool类型的choice配置,只能二选一,默认选第一个B_1配置项。
choice BOOL
    bool "bool"
config B_1
    tristate "B_1"
config B_2
    tristate "B_2"
endchoice
  • bool类型的choice配置,只能二选一,带optional属性,默认一个都不选择。
choice BOOL_OPT
    bool "bool optional"
    optional
config BO_1
    tristate "BO_1"
config BO_2
    tristate "BO_2"
endchoice
  • tristate类型的choice配置,只能二选一,默认选第一个T_1配置项。
choice TRISTATE
    tristate "tristate"
config T_1
    tristate "T_1"
config T_2
    tristate "T_2"
endchoice
  • tristate类型的choice配置,只能二选一,带optional属性,默认一个都不选择。
choice TRISTATE_OPT
    tristate "tristate optional"
    optional
config TO_1
    tristate "TO_1"
config TO_2
    tristate "TO_2"
endchoice
  • bool类型的choice配置,并且只有在MODULES为'y'的时候才能选择,默认不能选择,选择时只能二选一。
config MODULES
    bool "modules"
	
choice BOOL_M
    bool "bool m" if m
config BM_1
    tristate "BM_1"
config BM_2
    tristate "BM_2"
endchoice

Python Kconfiglib初次学习插图8

  • tristate类型的choice配置,并且只有在MODULES为'y'的时候才能选择,默认不能选择,可以同时配置多个选项为'm'或者'n'。
config MODULES
    bool "modules"
	
choice BOOL_M
    bool "bool m" if m
config BM_1
    tristate "BM_1"
config BM_2
    tristate "BM_2"
endchoice
  • 默认OPT_4配置为'y',其余都为'n',如果TRISTATE_SYM被设置为'y',则默认为OPT_2
    config TRISTATE_SYM
        tristate "tristate"

    choice DEFAULTS
        bool "defaults"
        default OPT_1 if n
        default OPT_2 if TRISTATE_SYM
        default OPT_4
    config OPT_1
        tristate "OPT_1"
    config OPT_2
        tristate "OPT_2"
    config OPT_3
        tristate "OPT_3"
    config OPT_4
        tristate "OPT_4"
    endchoice

Python Kconfiglib初次学习插图9

  • 默认OPT_8配置为'y',其余都为'n',OPT_7不可见
choice DEFAULTS_NOT_VISIBLE
    bool "defaults not visible"
    # Skipped due to condition
    default OPT_6 if n
    # Skipped because OPT_7 is not visible
    default OPT_7
    # This one should apply
    default OPT_8
config OPT_5
    tristate "OPT_5"
config OPT_6
    tristate "OPT_6"
config OPT_7
    tristate "OPT_7" if n
config OPT_8
    tristate "OPT_8"
config OPT_9
    tristate "OPT_9"
endchoice
  • 没有指定类型的选项choice,默认为第一个配置项的类型
# Choices without an explicitly specified type should get the type of the first symbol with a type

choice NO_TYPE_BOOL
    prompt "no type bool"
config NTB_1
    bool "NTB_1"
config NTB_2
    tristate "NTB_2"
endchoice

Python Kconfiglib初次学习插图10

choice NO_TYPE_TRISTATE
    prompt "no type tristate"
config NTT_1
config NTT_2
    tristate "NTB_2"
config NTT_3
    bool "NTT_3"
endchoice

Python Kconfiglib初次学习插图11

  • 没有显示指定类型的choice选项,应该默认为choice的类型,MISSING_MEMBER_TYPES_2因为没有指定类型和提示符,不可见
# Choice items without an explicitly specified type should get the type of the choice

choice MISSING_MEMBER_TYPES_1
    bool "missing member types"
config MMT_1
config MMT_2
config MMT_3
    tristate "mmt1/2/3"
endchoice

choice MISSING_MEMBER_TYPES_2
config MMT_4
config MMT_5
    bool "mmt4/5"
endchoice
  • 默认选择第一个配置项,但由于其依赖于另一个符号DEP,因此选项中第一个符号不可见,默认就会选择第二个配置项B。
# Choice where the default selection (the first symbol) depends on another symbol.
# If that symbol becomes 'n', the default selection should change to the first visible symbol in the choice.

choice DEFAULT_WITH_DEP
    bool "default with dep"

config A
    bool "A"
    depends on DEP

config B
    bool "B"

endchoice

config DEP
    bool "dep"

Python Kconfiglib初次学习插图12

  • 由于choice依赖于前面的符号,它不应该被当做choice选项。这可能是一个bug,但有时会用到它。默认选择WS1。只有WS1被选择之后,WS2和WS5才能选择。WS1、WS6、WS9这三个只能选择一个。WS7因为没有定义提示符,所以不可见。
# Choice with symbols that shouldn't be considered choice symbols because they depend on the preceding symbol.
# This might be a kconfig bug, but some things use it, so we need to emulate it.

choice WEIRD_SYMS
    bool "weird symbols that aren't considered part of the choice"

# Only WS1 is part of the choice
config WS1
    bool "WS1"

config WS2
    bool "WS2"
    depends on WS1

config WS3
    bool
    depends on WS2

config WS4
    bool
    depends on WS1

config WS5
    bool "WS5" if WS1

# 'if' has the same effect, so only WS6 is part of the choice
config WS6
    bool "WS6"

if WS6

config WS7
    bool

config WS8
    bool "WS8"

endif

# Should also be part of the choice
config WS9
    bool "WS9"

endchoice

Python Kconfiglib初次学习插图13

4.4 guiconfig

4.1 简介

参考:Kconfiglib/guiconfig.py at master · ulfalizer/Kconfiglib (github.com)

guiconfig.py介绍(文档开头的介绍):一个基于tkinter的菜单配置实现,基于树视图控件和帮助显示。显示可以在显示完整的树和仅显示单个菜单(如menuconfig.py)之间进行切换。

  • 只有单菜单模式才能区分用'config'定义的符号和用'menuconfig'定义的符号。
  • show-all模式下所有符号都可见,不可见的项目使用红色标记出。
  • 同时支持鼠标和键盘操作,支持的快捷键如下:

Ctrl-S : Save configuration

Ctrl-O : Open configuration

Ctrl-A : Toggle show-all mode

Ctrl-N : Toggle show-name mode

Ctrl-M : Toggle single-menu mode

Ctrl-F, /: Open jump-to dialog

ESC : Close

  • guiconfig.py可以作为独立的文件运行,也可以通过使用现有的Kconfig实例调用menuconfig()函数来运行,第二种方式不是很灵活。
  • 当以独立模式运行时,要加载的Kconfig文件可以作为命令行参数传递。如果没有参数,它默认为Kconfig。
  • KCONFIG_CONFIG环境变量可以指定加载和保存的.config文件名,如果没有指定,默认为.config。
  • 覆盖一个配置文件时,旧版的文件被保存为.old。

4.2 示例

以加载库中默认的Kmenuconfig配置文件为例进行举例说明:

kconfig配置文件:Kconfiglib/Kmenuconfig at master · ulfalizer/Kconfiglib (github.com)

运行,可以直接使用鼠标操作,也可以使用键盘快捷键操作,可以看出在Windows下用这种方式配置还是比较人性话:

python .guiconfig.py .examplesKmenuconfig

Python Kconfiglib初次学习插图14

使用menuconfig方式加载这个kconfig文件:

Python Kconfiglib初次学习插图15

可以看出,使用guiconfig.py的方式配置,树形结构更加直观。

4.5 all*config

4.5.1 输出kconfig默认配置值

官方实现:Kconfiglib/alldefconfig.py at master · ulfalizer/Kconfiglib (github.com)

import kconfiglib

def main():
    kconf = kconfiglib.standard_kconfig(__doc__)
    kconf.load_allconfig("alldef.config")
    print(kconf.write_config())

if __name__ == "__main__":
    main()

使用方式:

python .alldefconfig.py .examplesKmenuconfig

4.5.2 输出尽可能多的'm'

官方:Kconfiglib/allmodconfig.py at master · ulfalizer/Kconfiglib (github.com)

说明:写入一个配置文件,将尽可能多的符号被设置为"m"。

import kconfiglib

def main():
    kconf = kconfiglib.standard_kconfig(__doc__)

    # See allnoconfig.py
    kconf.warn = False

    for sym in kconf.unique_defined_syms:
        if sym.orig_type == kconfiglib.BOOL:
            # 'bool' choice symbols get their default value, as determined by
            # e.g. 'default's on the choice
            if not sym.choice:
                # All other bool symbols get set to 'y', like for allyesconfig
                sym.set_value(2)
        elif sym.orig_type == kconfiglib.TRISTATE:
            sym.set_value(1)

    for choice in kconf.unique_choices:
        choice.set_value(2 if choice.orig_type == kconfiglib.BOOL else 1)

    kconf.warn = True

    kconf.load_allconfig("allmod.config")

    print(kconf.write_config())

if __name__ == "__main__":
    main()

使用方式:

python .alldefconfig.py .examplesKmenuconfig

4.5.2 输出尽可能多的'n'

官方:Kconfiglib/allnoconfig.py at master · ulfalizer/Kconfiglib (github.com)

说明:写入一个配置文件,将尽可能多的符号被设置为"n"。

import kconfiglib

def main():
    kconf = kconfiglib.standard_kconfig(__doc__)

    # Avoid warnings that would otherwise get printed by Kconfiglib for the
    # following:
    #
    # 1. Assigning a value to a symbol without a prompt, which never has any
    #    effect
    #
    # 2. Assigning values invalid for the type (only bool/tristate symbols
    #    accept 0/1/2, for n/m/y). The assignments will be ignored for other
    #    symbol types, which is what we want.
    kconf.warn = False
    for sym in kconf.unique_defined_syms:
        sym.set_value(2 if sym.is_allnoconfig_y else 0)
    kconf.warn = True

    kconf.load_allconfig("allno.config")

    print(kconf.write_config())

if __name__ == "__main__":
    main()

使用方式:

python .alldefconfig.py .examplesKmenuconfig

4.5.2 输出尽可能多的'y'

官方:Kconfiglib/allyesconfig.py at master · ulfalizer/Kconfiglib (github.com)

说明:写入一个配置文件,将尽可能多的符号被设置为"y"。

import kconfiglib

def main():
    kconf = kconfiglib.standard_kconfig(__doc__)

    # See allnoconfig.py
    kconf.warn = False

    # Try to set all symbols to 'y'. Dependencies might truncate the value down
    # later, but this will at least give the highest possible value.
    #
    # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex
    # symbols still take a string, because they preserve formatting).
    for sym in kconf.unique_defined_syms:
        # Set choice symbols to 'm'. This value will be ignored for choices in
        # 'y' mode (the "normal" mode), which will instead just get their
        # default selection, but will set all symbols in m-mode choices to 'm',
        # which is as high as they can go.
        #
        # Here's a convoluted example of how you might get an m-mode choice
        # even during allyesconfig:
        #
        #   choice
        #           tristate "weird choice"
        #           depends on m
        sym.set_value(1 if sym.choice else 2)

    # Set all choices to the highest possible mode
    for choice in kconf.unique_choices:
        choice.set_value(2)

    kconf.warn = True

    kconf.load_allconfig("allyes.config")

    print(kconf.write_config())

if __name__ == "__main__":
    main()

使用方式:

python .alldefconfig.py .examplesKmenuconfig

文章来源于互联网:Python Kconfiglib初次学习

THE END
分享
二维码