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 # Do various menu tree post-processing
self._finalize_node(self.top_node, self.y) 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_defined_syms = _ordered_unique(self.defined_syms)
self.unique_choices = _ordered_unique(self.choices) self.unique_choices = _ordered_unique(self.choices)
@ -2305,6 +2307,7 @@ class Kconfig(object):
sym.kconfig = self sym.kconfig = self
sym.name = name sym.name = name
sym.is_constant = False sym.is_constant = False
sym.configdefaults = []
sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
if self._parsing_kconfigs: if self._parsing_kconfigs:
@ -2324,6 +2327,7 @@ class Kconfig(object):
sym.kconfig = self sym.kconfig = self
sym.name = name sym.name = name
sym.is_constant = True sym.is_constant = True
sym.configdefaults = []
sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
if self._parsing_kconfigs: if self._parsing_kconfigs:
@ -2913,7 +2917,7 @@ class Kconfig(object):
while self._next_line(): while self._next_line():
t0 = self._tokens[0] 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 # The tokenizer allocates Symbol objects for us
sym = self._tokens[1] sym = self._tokens[1]
@ -2929,6 +2933,7 @@ class Kconfig(object):
node.kconfig = self node.kconfig = self
node.item = sym node.item = sym
node.is_menuconfig = t0 is _T_MENUCONFIG node.is_menuconfig = t0 is _T_MENUCONFIG
node.is_configdefault = t0 is _T_CONFIGDEFAULT
node.prompt = node.help = node.list = None node.prompt = node.help = node.list = None
node.parent = parent node.parent = parent
node.filename = self.filename node.filename = self.filename
@ -2939,6 +2944,14 @@ class Kconfig(object):
self._parse_props(node) 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: if node.is_menuconfig and not node.prompt:
self._warn("the menuconfig symbol {} has no prompt" self._warn("the menuconfig symbol {} has no prompt"
.format(sym.name_and_loc)) .format(sym.name_and_loc))
@ -3551,6 +3564,23 @@ class Kconfig(object):
# Post-parsing menu tree processing, including dependency propagation and # Post-parsing menu tree processing, including dependency propagation and
# implicit submenu creation # 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): def _finalize_node(self, node, visible_if):
# Finalizes a menu node and its children: # Finalizes a menu node and its children:
@ -3697,6 +3727,14 @@ class Kconfig(object):
sym = node.item 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 # See the Symbol class docstring
sym.direct_dep = self._make_or(sym.direct_dep, node.dep) sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
@ -4249,6 +4287,7 @@ class Symbol(object):
"_write_to_conf", "_write_to_conf",
"choice", "choice",
"defaults", "defaults",
"configdefaults",
"direct_dep", "direct_dep",
"env_var", "env_var",
"implies", "implies",
@ -5613,6 +5652,7 @@ class MenuNode(object):
"help", "help",
"include_path", "include_path",
"is_menuconfig", "is_menuconfig",
"is_configdefault",
"item", "item",
"kconfig", "kconfig",
"linenr", "linenr",
@ -5814,8 +5854,13 @@ class MenuNode(object):
sc = self.item sc = self.item
if sc.__class__ is Symbol: if sc.__class__ is Symbol:
lines = [("menuconfig " if self.is_menuconfig else "config ") if self.is_menuconfig:
+ sc.name] t = "menuconfig "
elif self.is_configdefault:
t = "configdefault "
else:
t = "config "
lines = [t + sc.name]
else: else:
lines = ["choice " + sc.name if sc.name else "choice"] lines = ["choice " + sc.name if sc.name else "choice"]
@ -6860,6 +6905,7 @@ except AttributeError:
_T_CLOSE_PAREN, _T_CLOSE_PAREN,
_T_COMMENT, _T_COMMENT,
_T_CONFIG, _T_CONFIG,
_T_CONFIGDEFAULT,
_T_DEFAULT, _T_DEFAULT,
_T_DEFCONFIG_LIST, _T_DEFCONFIG_LIST,
_T_DEF_BOOL, _T_DEF_BOOL,
@ -6903,7 +6949,7 @@ except AttributeError:
_T_TRISTATE, _T_TRISTATE,
_T_UNEQUAL, _T_UNEQUAL,
_T_VISIBLE, _T_VISIBLE,
) = range(1, 51) ) = range(1, 52)
# Keyword to token map, with the get() method assigned directly as a small # Keyword to token map, with the get() method assigned directly as a small
# optimization # optimization
@ -6915,6 +6961,7 @@ _get_keyword = {
"choice": _T_CHOICE, "choice": _T_CHOICE,
"comment": _T_COMMENT, "comment": _T_COMMENT,
"config": _T_CONFIG, "config": _T_CONFIG,
"configdefault": _T_CONFIGDEFAULT,
"def_bool": _T_DEF_BOOL, "def_bool": _T_DEF_BOOL,
"def_hex": _T_DEF_HEX, "def_hex": _T_DEF_HEX,
"def_int": _T_DEF_INT, "def_int": _T_DEF_INT,