scripts: kconfig: kconfiglib: introduce configdefault

Introduce the `configdefault` keyword as a Kconfig extension. This new
keyword allows `default` values to be applied to externally defined
symbols without needing to respecify dependencies, or weakening the
existing dependencies.

This is primarily useful in downstream repositories that wish to define
default configurations such as:
```
config MY_COMPANY_APPS
    bool "Apply defaults for internal applications"

configdefault BT
    default y if MY_COMPANY_APPS
configdefault MCUMGR
    default y if MY_COMPANY_APPS && BT
```

Obtaining the same functionality with `config` (without weakening the
symbol dependencies) requires finding the original definition and
duplicating any `depends on` and surrounding `if` statements. This is a
non-trivial exercise that needs to be manually rechecked on each Zephyr
update.

`configdefault` simplifies this process by acting as if the `default`
statement was present at any one of the original definitions of the
symbol.

Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
This commit is contained in:
Jordan Yates 2022-10-16 14:18:34 +10:00 committed by Carles Cufí
parent c8df08d681
commit 79e467c92a

View file

@ -1096,6 +1096,8 @@ class Kconfig(object):
# Do various menu tree post-processing
self._finalize_node(self.top_node, self.y)
for s in self.syms.values():
self._finalize_sym(s)
self.unique_defined_syms = _ordered_unique(self.defined_syms)
self.unique_choices = _ordered_unique(self.choices)
@ -2305,6 +2307,7 @@ class Kconfig(object):
sym.kconfig = self
sym.name = name
sym.is_constant = False
sym.configdefaults = []
sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
if self._parsing_kconfigs:
@ -2324,6 +2327,7 @@ class Kconfig(object):
sym.kconfig = self
sym.name = name
sym.is_constant = True
sym.configdefaults = []
sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
if self._parsing_kconfigs:
@ -2913,7 +2917,7 @@ class Kconfig(object):
while self._next_line():
t0 = self._tokens[0]
if t0 is _T_CONFIG or t0 is _T_MENUCONFIG:
if t0 in [_T_CONFIG, _T_MENUCONFIG, _T_CONFIGDEFAULT]:
# The tokenizer allocates Symbol objects for us
sym = self._tokens[1]
@ -2929,6 +2933,7 @@ class Kconfig(object):
node.kconfig = self
node.item = sym
node.is_menuconfig = t0 is _T_MENUCONFIG
node.is_configdefault = t0 is _T_CONFIGDEFAULT
node.prompt = node.help = node.list = None
node.parent = parent
node.filename = self.filename
@ -2939,6 +2944,14 @@ class Kconfig(object):
self._parse_props(node)
if node.is_configdefault:
if (node.prompt or
node.dep != self.y or
len(node.ranges) > 0 or
len(node.selects) > 0 or
len(node.implies) > 0):
self._parse_error("configdefault can only contain `default`")
if node.is_menuconfig and not node.prompt:
self._warn("the menuconfig symbol {} has no prompt"
.format(sym.name_and_loc))
@ -3551,6 +3564,23 @@ class Kconfig(object):
# Post-parsing menu tree processing, including dependency propagation and
# implicit submenu creation
#
def _finalize_sym(self, sym):
# Finalizes symbol definitions
#
# - Applies configdefault node defaults to final symbols
#
# sym:
# The symbol to finalize.
inserted = 0
for (idx, defaults) in sym.configdefaults:
for d in defaults:
# Add the defaults to the node, with the requirement that
# direct dependencies are respected. The original order
# of the default statements between nodes is preserved.
default = (d[0], self._make_and(sym.direct_dep, d[1]))
sym.defaults.insert(inserted + idx, default)
inserted += 1
def _finalize_node(self, node, visible_if):
# Finalizes a menu node and its children:
@ -3697,6 +3727,14 @@ class Kconfig(object):
sym = node.item
if node.is_configdefault:
# Store any defaults for later application after the complete tree
# is known. The current length of of the default array is stored so
# the configdefaults can be inserted in the order they originally
# appeared.
sym.configdefaults.append((len(sym.defaults), node.defaults))
return
# See the Symbol class docstring
sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
@ -4249,6 +4287,7 @@ class Symbol(object):
"_write_to_conf",
"choice",
"defaults",
"configdefaults",
"direct_dep",
"env_var",
"implies",
@ -5613,6 +5652,7 @@ class MenuNode(object):
"help",
"include_path",
"is_menuconfig",
"is_configdefault",
"item",
"kconfig",
"linenr",
@ -5814,8 +5854,13 @@ class MenuNode(object):
sc = self.item
if sc.__class__ is Symbol:
lines = [("menuconfig " if self.is_menuconfig else "config ")
+ sc.name]
if self.is_menuconfig:
t = "menuconfig "
elif self.is_configdefault:
t = "configdefault "
else:
t = "config "
lines = [t + sc.name]
else:
lines = ["choice " + sc.name if sc.name else "choice"]
@ -6860,6 +6905,7 @@ except AttributeError:
_T_CLOSE_PAREN,
_T_COMMENT,
_T_CONFIG,
_T_CONFIGDEFAULT,
_T_DEFAULT,
_T_DEFCONFIG_LIST,
_T_DEF_BOOL,
@ -6903,7 +6949,7 @@ except AttributeError:
_T_TRISTATE,
_T_UNEQUAL,
_T_VISIBLE,
) = range(1, 51)
) = range(1, 52)
# Keyword to token map, with the get() method assigned directly as a small
# optimization
@ -6915,6 +6961,7 @@ _get_keyword = {
"choice": _T_CHOICE,
"comment": _T_COMMENT,
"config": _T_CONFIG,
"configdefault": _T_CONFIGDEFAULT,
"def_bool": _T_DEF_BOOL,
"def_hex": _T_DEF_HEX,
"def_int": _T_DEF_INT,