Merge branch 'bugfix/update_menuconfig' into 'master'

Update kconfiglib from v12.14.0 to v13.3.2

See merge request espressif/esp-idf!6816
This commit is contained in:
Angus Gratton 2019-12-04 15:06:09 +08:00
commit af382a1b8e
2 changed files with 324 additions and 208 deletions

View file

@ -554,7 +554,7 @@ from glob import iglob
from os.path import dirname, exists, expandvars, islink, join, realpath
VERSION = (12, 14, 0)
VERSION = (13, 3, 2)
# File layout:
@ -773,8 +773,8 @@ class Kconfig(object):
See Kconfig.load_config() as well.
srctree:
The value of the $srctree environment variable when the configuration was
loaded, or the empty string if $srctree wasn't set. This gives nice
The value the $srctree environment variable had when the Kconfig instance
was created, or the empty string if $srctree wasn't set. This gives nice
behavior with os.path.join(), which treats "" as the current directory,
without adding "./".
@ -789,13 +789,22 @@ class Kconfig(object):
if multiple configurations are loaded with different values for $srctree.
config_prefix:
The value of the $CONFIG_ environment variable when the configuration was
loaded. This is the prefix used (and expected) on symbol names in .config
files and C headers. Defaults to "CONFIG_". Used in the same way in the C
tools.
The value the CONFIG_ environment variable had when the Kconfig instance
was created, or "CONFIG_" if CONFIG_ wasn't set. This is the prefix used
(and expected) on symbol names in .config files and C headers. Used in
the same way in the C tools.
Like for srctree, only the value of $CONFIG_ when the configuration is
loaded matters.
config_header:
The value the KCONFIG_CONFIG_HEADER environment variable had when the
Kconfig instance was created, or the empty string if
KCONFIG_CONFIG_HEADER wasn't set. This string is inserted verbatim at the
beginning of configuration files. See write_config().
header_header:
The value the KCONFIG_AUTOHEADER_HEADER environment variable had when the
Kconfig instance was created, or the empty string if
KCONFIG_AUTOHEADER_HEADER wasn't set. This string is inserted verbatim at
the beginning of header files. See write_autoconf().
filename/linenr:
The current parsing location, for use in Python preprocessor functions.
@ -810,11 +819,13 @@ class Kconfig(object):
"_warn_assign_no_prompt",
"choices",
"comments",
"config_header",
"config_prefix",
"const_syms",
"defconfig_list",
"defined_syms",
"env_vars",
"header_header",
"kconfig_filenames",
"m",
"menus",
@ -943,6 +954,9 @@ class Kconfig(object):
self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
self.config_prefix))
self.config_header = os.getenv("KCONFIG_CONFIG_HEADER", "")
self.header_header = os.getenv("KCONFIG_AUTOHEADER_HEADER", "")
self.syms = {}
self.const_syms = {}
self.defined_syms = []
@ -1245,7 +1259,7 @@ class Kconfig(object):
self._warn("'{}' is not a valid value for the {} "
"symbol {}. Assignment ignored."
.format(val, TYPE_TO_STR[sym.orig_type],
_name_and_loc(sym)),
sym.name_and_loc),
filename, linenr)
continue
@ -1272,7 +1286,7 @@ class Kconfig(object):
if not match:
self._warn("malformed string literal in "
"assignment to {}. Assignment ignored."
.format(_name_and_loc(sym)),
.format(sym.name_and_loc),
filename, linenr)
continue
@ -1341,7 +1355,7 @@ class Kconfig(object):
user_val = sym.user_value
msg = '{} set more than once. Old value "{}", new value "{}".'.format(
_name_and_loc(sym), user_val, new_val)
sym.name_and_loc, user_val, new_val)
if user_val == new_val:
if self.warn_assign_redun:
@ -1349,8 +1363,7 @@ class Kconfig(object):
elif self.warn_assign_override:
self._warn(msg, filename, linenr)
def write_autoconf(self, filename,
header="/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n"):
def write_autoconf(self, filename=None, header=None):
r"""
Writes out symbol values as a C header file, matching the format used
by include/generated/autoconf.h in the kernel.
@ -1364,22 +1377,37 @@ class Kconfig(object):
like the modification time and possibly triggering redundant work in
build tools.
filename:
Self-explanatory.
filename (default: None):
Path to write header to.
header (default: "/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n"):
Text that will be inserted verbatim at the beginning of the file. You
would usually want it enclosed in '/* */' to make it a C comment,
and include a final terminating newline.
If None (the default), the path in the environment variable
KCONFIG_AUTOHEADER is used if set, and "include/generated/autoconf.h"
otherwise. This is compatible with the C tools.
header (default: None):
Text inserted verbatim at the beginning of the file. You would
usually want it enclosed in '/* */' to make it a C comment, and
include a trailing newline.
If None (the default), the value of the environment variable
KCONFIG_AUTOHEADER_HEADER had when the Kconfig instance was created
will be used if it was set, and no header otherwise. See the
Kconfig.header_header attribute.
"""
if filename is None:
filename = os.getenv("KCONFIG_AUTOHEADER",
"include/generated/autoconf.h")
self._write_if_changed(filename, self._autoconf_contents(header))
def _autoconf_contents(self, header):
# write_autoconf() helper. Returns the contents to write as a string,
# with 'header' at the beginning.
# with 'header' or KCONFIG_AUTOHEADER_HEADER at the beginning.
# "".join()ed later
chunks = [header]
if header is None:
header = self.header_header
chunks = [header] # "".join()ed later
add = chunks.append
for sym in self.unique_defined_syms:
@ -1415,9 +1443,8 @@ class Kconfig(object):
return "".join(chunks)
def write_config(self, filename=None,
header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n",
save_old=True, verbose=None):
def write_config(self, filename=None, header=None, save_old=True,
verbose=None):
r"""
Writes out symbol values in the .config format. The format matches the
C implementation, including ordering.
@ -1439,16 +1466,21 @@ class Kconfig(object):
(OSError/IOError). KconfigError is never raised here.
filename (default: None):
Filename to save configuration to (a string).
Path to write configuration to (a string).
If None (the default), the filename in the environment variable
If None (the default), the path in the environment variable
KCONFIG_CONFIG is used if set, and ".config" otherwise. See
standard_config_filename().
header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
Text that will be inserted verbatim at the beginning of the file. You
would usually want each line to start with '#' to make it a comment,
and include a final terminating newline.
header (default: None):
Text inserted verbatim at the beginning of the file. You would
usually want each line to start with '#' to make it a comment, and
include a trailing newline.
if None (the default), the value of the environment variable
KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
be used if it was set, and no header otherwise. See the
Kconfig.config_header attribute.
save_old (default: True):
If True and <filename> already exists, a copy of it will be saved to
@ -1493,7 +1525,7 @@ class Kconfig(object):
def _config_contents(self, header):
# write_config() helper. Returns the contents to write as a string,
# with 'header' at the beginning.
# with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
#
# More memory friendly would be to 'yield' the strings and
# "".join(_config_contents()), but it was a bit slower on my system.
@ -1505,13 +1537,15 @@ class Kconfig(object):
for sym in self.unique_defined_syms:
sym._visited = False
if header is None:
header = self.config_header
chunks = [header] # "".join()ed later
add = chunks.append
# Did we just print an '# end of ...' comment?
after_end_comment = False
# "".join()ed later
chunks = [header]
add = chunks.append
node = self.top_node
while 1:
# Jump to the next node with an iterative tree walk
@ -1564,8 +1598,7 @@ class Kconfig(object):
add("\n#\n# {}\n#\n".format(node.prompt[0]))
after_end_comment = False
def write_min_config(self, filename,
header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
def write_min_config(self, filename, header=None):
"""
Writes out a "minimal" configuration file, omitting symbols whose value
matches their default value. The format matches the one produced by
@ -1581,12 +1614,17 @@ class Kconfig(object):
(OSError/IOError). KconfigError is never raised here.
filename:
Self-explanatory.
Path to write minimal configuration to.
header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
Text that will be inserted verbatim at the beginning of the file. You
would usually want each line to start with '#' to make it a comment,
and include a final terminating newline.
header (default: None):
Text inserted verbatim at the beginning of the file. You would
usually want each line to start with '#' to make it a comment, and
include a final terminating newline.
if None (the default), the value of the environment variable
KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
be used if it was set, and no header otherwise. See the
Kconfig.config_header attribute.
Returns a string with a message saying which file got saved. This is
meant to reduce boilerplate in tools, which can do e.g.
@ -1603,9 +1641,12 @@ class Kconfig(object):
def _min_config_contents(self, header):
# write_min_config() helper. Returns the contents to write as a string,
# with 'header' at the beginning.
# with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
chunks = [header]
if header is None:
header = self.config_header
chunks = [header] # "".join()ed later
add = chunks.append
for sym in self.unique_defined_syms:
@ -2603,10 +2644,9 @@ class Kconfig(object):
while 1:
match = _name_special_search(s, i)
if match.group() == "$(":
s, i = self._expand_macro(s, match.start(), ())
else:
if match.group() != "$(":
return (s, match.start())
s, i = self._expand_macro(s, match.start(), ())
def _expand_str(self, s, i):
# Expands a quoted string starting at index 'i' in 's'. Handles both
@ -2649,14 +2689,12 @@ class Kconfig(object):
# Returns the expanded 's' (including the part before the macro) and
# the index of the first character after the expanded macro in 's'.
start = i
res = s[:i]
i += 2 # Skip over "$("
# Start of current macro argument
arg_start = i
# Arguments of this macro call
new_args = []
arg_start = i # Start of current macro argument
new_args = [] # Arguments of this macro call
nesting = 0 # Current parentheses nesting level
while 1:
match = _macro_special_search(s, i)
@ -2664,32 +2702,42 @@ class Kconfig(object):
self._parse_error("missing end parenthesis in macro expansion")
if match.group() == ")":
if match.group() == "(":
nesting += 1
i = match.end()
elif match.group() == ")":
if nesting:
nesting -= 1
i = match.end()
continue
# Found the end of the macro
new_args.append(s[arg_start:match.start()])
prefix = s[:start]
# $(1) is replaced by the first argument to the function, etc.,
# provided at least that many arguments were passed
try:
# Does the macro look like an integer, with a corresponding
# argument? If so, expand it to the value of the argument.
prefix += args[int(new_args[0])]
res += args[int(new_args[0])]
except (ValueError, IndexError):
# Regular variables are just functions without arguments,
# and also go through the function value path
prefix += self._fn_val(new_args)
res += self._fn_val(new_args)
return (prefix + s[match.end():],
len(prefix))
return (res + s[match.end():], len(res))
elif match.group() == ",":
i = match.end()
if nesting:
continue
# Found the end of a macro argument
new_args.append(s[arg_start:match.start()])
arg_start = i = match.end()
arg_start = i
else: # match.group() == "$("
# A nested macro call within the macro
@ -2836,7 +2884,7 @@ class Kconfig(object):
if node.is_menuconfig and not node.prompt:
self._warn("the menuconfig symbol {} has no prompt"
.format(_name_and_loc(sym)))
.format(sym.name_and_loc))
# Equivalent to
#
@ -3173,8 +3221,7 @@ class Kconfig(object):
# UNKNOWN is falsy
if node.item.orig_type and node.item.orig_type is not new_type:
self._warn("{} defined with multiple types, {} will be used"
.format(_name_and_loc(node.item),
TYPE_TO_STR[new_type]))
.format(node.item.name_and_loc, TYPE_TO_STR[new_type]))
node.item.orig_type = new_type
@ -3184,7 +3231,7 @@ class Kconfig(object):
# multiple times
if node.prompt:
self._warn(_name_and_loc(node.item) +
self._warn(node.item.name_and_loc +
" defined with multiple prompts in single location")
prompt = self._tokens[1]
@ -3194,7 +3241,7 @@ class Kconfig(object):
self._parse_error("expected prompt string")
if prompt != prompt.strip():
self._warn(_name_and_loc(node.item) +
self._warn(node.item.name_and_loc +
" has leading or trailing whitespace in its prompt")
# This avoid issues for e.g. reStructuredText documentation, where
@ -3205,7 +3252,7 @@ class Kconfig(object):
def _parse_help(self, node):
if node.help is not None:
self._warn(_name_and_loc(node.item) + " defined with more than "
self._warn(node.item.name_and_loc + " defined with more than "
"one help text -- only the last one will be used")
# Micro-optimization. This code is pretty hot.
@ -3261,7 +3308,7 @@ class Kconfig(object):
self._line_after_help(line)
def _empty_help(self, node, line):
self._warn(_name_and_loc(node.item) +
self._warn(node.item.name_and_loc +
" has 'help' but empty help text")
node.help = ""
if line:
@ -3641,26 +3688,26 @@ class Kconfig(object):
if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
self._warn("{} selects the {} symbol {}, which is not "
"bool or tristate"
.format(_name_and_loc(sym),
.format(sym.name_and_loc,
TYPE_TO_STR[target_sym.orig_type],
_name_and_loc(target_sym)))
target_sym.name_and_loc))
for target_sym, _ in sym.implies:
if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
self._warn("{} implies the {} symbol {}, which is not "
"bool or tristate"
.format(_name_and_loc(sym),
.format(sym.name_and_loc,
TYPE_TO_STR[target_sym.orig_type],
_name_and_loc(target_sym)))
target_sym.name_and_loc))
elif sym.orig_type: # STRING/INT/HEX
for default, _ in sym.defaults:
if default.__class__ is not Symbol:
raise KconfigError(
"the {} symbol {} has a malformed default {} -- expected "
"a single symbol"
.format(TYPE_TO_STR[sym.orig_type], _name_and_loc(sym),
expr_str(default)))
"the {} symbol {} has a malformed default {} -- "
"expected a single symbol"
.format(TYPE_TO_STR[sym.orig_type],
sym.name_and_loc, expr_str(default)))
if sym.orig_type is STRING:
if not default.is_constant and not default.nodes and \
@ -3671,22 +3718,22 @@ class Kconfig(object):
# (and no symbol named 'foo' exists).
self._warn("style: quotes recommended around "
"default value for string symbol "
+ _name_and_loc(sym))
+ sym.name_and_loc)
elif not num_ok(default, sym.orig_type): # INT/HEX
self._warn("the {0} symbol {1} has a non-{0} default {2}"
.format(TYPE_TO_STR[sym.orig_type],
_name_and_loc(sym),
_name_and_loc(default)))
sym.name_and_loc,
default.name_and_loc))
if sym.selects or sym.implies:
self._warn("the {} symbol {} has selects or implies"
.format(TYPE_TO_STR[sym.orig_type],
_name_and_loc(sym)))
sym.name_and_loc))
else: # UNKNOWN
self._warn("{} defined without a type"
.format(_name_and_loc(sym)))
.format(sym.name_and_loc))
if sym.ranges:
@ -3694,7 +3741,7 @@ class Kconfig(object):
self._warn(
"the {} symbol {} has ranges, but is not int or hex"
.format(TYPE_TO_STR[sym.orig_type],
_name_and_loc(sym)))
sym.name_and_loc))
else:
for low, high, _ in sym.ranges:
if not num_ok(low, sym.orig_type) or \
@ -3703,9 +3750,9 @@ class Kconfig(object):
self._warn("the {0} symbol {1} has a non-{0} "
"range [{2}, {3}]"
.format(TYPE_TO_STR[sym.orig_type],
_name_and_loc(sym),
_name_and_loc(low),
_name_and_loc(high)))
sym.name_and_loc,
low.name_and_loc,
high.name_and_loc))
def _check_choice_sanity(self):
# Checks various choice properties that are handiest to check after
@ -3714,43 +3761,43 @@ class Kconfig(object):
def warn_select_imply(sym, expr, expr_type):
msg = "the choice symbol {} is {} by the following symbols, but " \
"select/imply has no effect on choice symbols" \
.format(_name_and_loc(sym), expr_type)
.format(sym.name_and_loc, expr_type)
# si = select/imply
for si in split_expr(expr, OR):
msg += "\n - " + _name_and_loc(split_expr(si, AND)[0])
msg += "\n - " + split_expr(si, AND)[0].name_and_loc
self._warn(msg)
for choice in self.unique_choices:
if choice.orig_type not in _BOOL_TRISTATE:
self._warn("{} defined with type {}"
.format(_name_and_loc(choice),
.format(choice.name_and_loc,
TYPE_TO_STR[choice.orig_type]))
for node in choice.nodes:
if node.prompt:
break
else:
self._warn(_name_and_loc(choice) + " defined without a prompt")
self._warn(choice.name_and_loc + " defined without a prompt")
for default, _ in choice.defaults:
if default.__class__ is not Symbol:
raise KconfigError(
"{} has a malformed default {}"
.format(_name_and_loc(choice), expr_str(default)))
.format(choice.name_and_loc, expr_str(default)))
if default.choice is not choice:
self._warn("the default selection {} of {} is not "
"contained in the choice"
.format(_name_and_loc(default),
_name_and_loc(choice)))
.format(default.name_and_loc,
choice.name_and_loc))
for sym in choice.syms:
if sym.defaults:
self._warn("default on the choice symbol {} will have "
"no effect, as defaults do not affect choice "
"symbols".format(_name_and_loc(sym)))
"symbols".format(sym.name_and_loc))
if sym.rev_dep is not sym.kconfig.n:
warn_select_imply(sym, sym.rev_dep, "selected")
@ -3762,12 +3809,12 @@ class Kconfig(object):
if node.parent.item is choice:
if not node.prompt:
self._warn("the choice symbol {} has no prompt"
.format(_name_and_loc(sym)))
.format(sym.name_and_loc))
elif node.prompt:
self._warn("the choice symbol {} is defined with a "
"prompt outside the choice"
.format(_name_and_loc(sym)))
.format(sym.name_and_loc))
def _parse_error(self, msg):
raise KconfigError("{}couldn't parse '{}': {}".format(
@ -3907,6 +3954,13 @@ class Symbol(object):
The type as given in the Kconfig file, without any magic applied. Used
when printing the symbol.
tri_value:
The tristate value of the symbol as an integer. One of 0, 1, 2,
representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
This is the symbol value that's used outside of relation expressions
(A, !A, A && B, A || B).
str_value:
The value of the symbol as a string. Gives the value for string/int/hex
symbols. For bool/tristate symbols, gives "n", "m", or "y".
@ -3914,17 +3968,20 @@ class Symbol(object):
This is the symbol value that's used in relational expressions
(A = B, A != B, etc.)
Gotcha: For int/hex symbols, the exact format of the value must often be
preserved (e.g., when writing a .config file), hence why you can't get it
Gotcha: For int/hex symbols, the exact format of the value is often
preserved (e.g. when writing a .config file), hence why you can't get it
directly as an int. Do int(int_sym.str_value) or
int(hex_sym.str_value, 16) to get the integer value.
tri_value:
The tristate value of the symbol as an integer. One of 0, 1, 2,
representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
user_value:
The user value of the symbol. None if no user value has been assigned
(via Kconfig.load_config() or Symbol.set_value()).
This is the symbol value that's used outside of relation expressions
(A, !A, A && B, A || B).
Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other
symbol types.
WARNING: Do not assign directly to this. It will break things. Use
Symbol.set_value().
assignable:
A tuple containing the tristate user values that can currently be
@ -3965,16 +4022,6 @@ class Symbol(object):
The visibility of the symbol. One of 0, 1, 2, representing n, m, y. See
the module documentation for an overview of symbol values and visibility.
user_value:
The user value of the symbol. None if no user value has been assigned
(via Kconfig.load_config() or Symbol.set_value()).
Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other
symbol types.
WARNING: Do not assign directly to this. It will break things. Use
Symbol.set_value().
config_string:
The .config assignment string that would get written out for the symbol
by Kconfig.write_config(). Returns the empty string if no .config
@ -4002,6 +4049,15 @@ class Symbol(object):
though you might get some special symbols and possibly some "redundant"
n-valued symbol entries in there.
name_and_loc:
Holds a string like
"MY_SYMBOL (defined at foo/Kconfig:12, bar/Kconfig:14)"
, giving the name of the symbol and its definition location(s).
If the symbol is undefined, the location is given as "(undefined)".
nodes:
A list of MenuNodes for this symbol. Will contain a single MenuNode for
most symbols. Undefined and constant symbols have an empty nodes list.
@ -4232,7 +4288,7 @@ class Symbol(object):
"being outside the active range ([{}, {}]) -- falling "
"back on defaults"
.format(num2str(user_val), TYPE_TO_STR[self.orig_type],
_name_and_loc(self),
self.name_and_loc,
num2str(low), num2str(high)))
else:
# If the user value is well-formed and satisfies range
@ -4282,7 +4338,7 @@ class Symbol(object):
self.kconfig._warn(
"default value {} on {} clamped to {} due to "
"being outside the active range ([{}, {}])"
.format(val_num, _name_and_loc(self),
.format(val_num, self.name_and_loc,
num2str(clamp), num2str(low),
num2str(high)))
@ -4323,7 +4379,7 @@ class Symbol(object):
self.kconfig._warn(
"The {} symbol {} is being evaluated in a logical context "
"somewhere. It will always evaluate to n."
.format(TYPE_TO_STR[self.orig_type], _name_and_loc(self)))
.format(TYPE_TO_STR[self.orig_type], self.name_and_loc))
self._cached_tri_val = 0
return 0
@ -4433,6 +4489,13 @@ class Symbol(object):
return '{}{}="{}"\n' \
.format(self.kconfig.config_prefix, self.name, escape(val))
@property
def name_and_loc(self):
"""
See the class documentation.
"""
return self.name + " " + _locs(self)
def set_value(self, value):
"""
Sets the user value of the symbol.
@ -4454,8 +4517,8 @@ class Symbol(object):
value:
The user value to give to the symbol. For bool and tristate symbols,
n/m/y can be specified either as 0/1/2 (the usual format for tristate
values in Kconfiglib) or as one of the strings "n"/"m"/"y". For other
symbol types, pass a string.
values in Kconfiglib) or as one of the strings "n", "m", or "y". For
other symbol types, pass a string.
Note that the value for an int/hex symbol is passed as a string, e.g.
"123" or "0x0123". The format of this string is preserved in the
@ -4502,7 +4565,7 @@ class Symbol(object):
"assignment ignored"
.format(TRI_TO_STR[value] if value in TRI_TO_STR else
"'{}'".format(value),
_name_and_loc(self), TYPE_TO_STR[self.orig_type]))
self.name_and_loc, TYPE_TO_STR[self.orig_type]))
return False
@ -4790,7 +4853,7 @@ class Symbol(object):
return
if self.kconfig._warn_assign_no_prompt:
self.kconfig._warn(_name_and_loc(self) + " has no prompt, meaning "
self.kconfig._warn(self.name_and_loc + " has no prompt, meaning "
"user values have no effect on it")
def _str_default(self):
@ -4836,7 +4899,7 @@ class Symbol(object):
msg = "{} has direct dependencies {} with value {}, but is " \
"currently being {}-selected by the following symbols:" \
.format(_name_and_loc(self), expr_str(self.direct_dep),
.format(self.name_and_loc, expr_str(self.direct_dep),
TRI_TO_STR[expr_value(self.direct_dep)],
TRI_TO_STR[expr_value(self.rev_dep)])
@ -4854,7 +4917,7 @@ class Symbol(object):
msg += "\n - {}, with value {}, direct dependencies {} " \
"(value: {})" \
.format(_name_and_loc(selecting_sym),
.format(selecting_sym.name_and_loc,
selecting_sym.str_value,
expr_str(selecting_sym.direct_dep),
TRI_TO_STR[expr_value(selecting_sym.direct_dep)])
@ -4938,19 +5001,9 @@ class Choice(object):
Corresponding attributes have the same name in the Symbol and Choice
classes, for consistency and compatibility.
assignable:
See the symbol class documentation. Gives the assignable values (modes).
visibility:
See the Symbol class documentation. Acts on the value (mode).
selection:
The Symbol instance of the currently selected symbol. None if the Choice
is not in y mode or has no selected symbol (due to unsatisfied
dependencies on choice symbols).
WARNING: Do not assign directly to this. It will break things. Call
sym.set_value(2) on the choice symbol you want to select instead.
str_value:
Like choice.tri_value, but gives the value as one of the strings
"n", "m", or "y"
user_value:
The value (mode) selected by the user through Choice.set_value(). Either
@ -4960,6 +5013,17 @@ class Choice(object):
WARNING: Do not assign directly to this. It will break things. Use
Choice.set_value() instead.
assignable:
See the symbol class documentation. Gives the assignable values (modes).
selection:
The Symbol instance of the currently selected symbol. None if the Choice
is not in y mode or has no selected symbol (due to unsatisfied
dependencies on choice symbols).
WARNING: Do not assign directly to this. It will break things. Call
sym.set_value(2) on the choice symbol you want to select instead.
user_selection:
The symbol selected by the user (by setting it to y). Ignored if the
choice is not in y mode, but still remembered so that the choice "snaps
@ -4969,6 +5033,19 @@ class Choice(object):
WARNING: Do not assign directly to this. It will break things. Call
sym.set_value(2) on the choice symbol to be selected instead.
visibility:
See the Symbol class documentation. Acts on the value (mode).
name_and_loc:
Holds a string like
"<choice MY_CHOICE> (defined at foo/Kconfig:12)"
, giving the name of the choice and its definition location(s). If the
choice has no name (isn't defined with 'choice MY_CHOICE'), then it will
be shown as "<choice>" before the list of locations (always a single one
in that case).
syms:
List of symbols contained in the choice.
@ -5088,6 +5165,14 @@ class Choice(object):
self._cached_vis = _visibility(self)
return self._cached_vis
@property
def name_and_loc(self):
"""
See the class documentation.
"""
# Reuse the expression format, which is '<choice (name, if any)>'.
return standard_sc_expr_str(self) + " " + _locs(self)
@property
def selection(self):
"""
@ -5128,7 +5213,7 @@ class Choice(object):
"assignment ignored"
.format(TRI_TO_STR[value] if value in TRI_TO_STR else
"'{}'".format(value),
_name_and_loc(self), TYPE_TO_STR[self.orig_type]))
self.name_and_loc, TYPE_TO_STR[self.orig_type]))
return False
@ -6050,21 +6135,36 @@ def unescape(s):
_unescape_sub = re.compile(r"\\(.)").sub
def standard_kconfig():
def standard_kconfig(description=None):
"""
Helper for tools. Loads the top-level Kconfig specified as the first
command-line argument, or "Kconfig" if there are no command-line arguments.
Returns the Kconfig instance.
Argument parsing helper for tools that take a single optional Kconfig file
argument (default: Kconfig). Returns the Kconfig instance for the parsed
configuration. Uses argparse internally.
Exits with sys.exit() (which raises a SystemExit exception) and prints a
usage note to stderr if more than one command-line argument is passed.
Exits with sys.exit() (which raises SystemExit) on errors.
description (default: None):
The 'description' passed to argparse.ArgumentParser().
argparse.RawDescriptionHelpFormatter is used, so formatting is preserved.
"""
if len(sys.argv) > 2:
sys.exit("usage: {} [Kconfig]".format(sys.argv[0]))
import argparse
# Only show backtraces for unexpected exceptions
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=description)
parser.add_argument(
"kconfig",
metavar="KCONFIG",
default="Kconfig",
nargs="?",
help="Kconfig file (default: Kconfig)")
args = parser.parse_args()
# Suppress backtraces for expected exceptions
try:
return Kconfig("Kconfig" if len(sys.argv) < 2 else sys.argv[1])
return Kconfig(args.kconfig)
except (EnvironmentError, KconfigError) as e:
# Some long exception messages have extra newlines for better
# formatting when reported as an unhandled exception. Strip them here.
@ -6286,20 +6386,16 @@ def _save_old(path):
pass
def _name_and_loc(sc):
# Helper for giving the symbol/choice name and location(s) in e.g. warnings
def _locs(sc):
# Symbol/Choice.name_and_loc helper. Returns the "(defined at ...)" part of
# the string. 'sc' is a Symbol or Choice.
# Reuse the expression format. That way choices show up as
# '<choice (name, if any)>'
name = standard_sc_expr_str(sc)
if sc.nodes:
return "(defined at {})".format(
", ".join("{0.filename}:{0.linenr}".format(node)
for node in sc.nodes))
if not sc.nodes:
return name + " (undefined)"
return "{} (defined at {})".format(
name,
", ".join("{}:{}".format(node.filename, node.linenr)
for node in sc.nodes))
return "(undefined)"
# Menu manipulation
@ -6554,7 +6650,7 @@ def _found_dep_loop(loop, cur):
msg += "the choice symbol "
msg += "{}, with definition...\n\n{}\n\n" \
.format(_name_and_loc(item), item)
.format(item.name_and_loc, item)
# Small wart: Since we reuse the already calculated
# Symbol/Choice._dependents sets for recursive dependency detection, we
@ -6578,7 +6674,7 @@ def _found_dep_loop(loop, cur):
msg += "(imply-related dependencies: {})\n\n" \
.format(expr_str(item.rev_dep))
msg += "...depends again on {}".format(_name_and_loc(loop[0]))
msg += "...depends again on " + loop[0].name_and_loc
raise KconfigError(msg)
@ -7015,8 +7111,8 @@ _assignment_lhs_fragment_match = _re_match("[A-Za-z0-9_-]*")
# variable assignment
_assignment_rhs_match = _re_match(r"\s*(=|:=|\+=)\s*(.*)")
# Special characters/strings while expanding a macro (')', ',', and '$(')
_macro_special_search = _re_search(r"\)|,|\$\(")
# Special characters/strings while expanding a macro ('(', ')', ',', and '$(')
_macro_special_search = _re_search(r"\(|\)|,|\$\(")
# Special characters/strings while expanding a string (quotes, '\', and '$(')
_string_special_search = _re_search(r'"|\'|\\|\$\(')

View file

@ -76,7 +76,7 @@ This is the current list of built-in styles:
- 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
- aquatic blue-tinted style loosely resembling the lxdialog theme
It is possible to customize the current style by changing colors of UI
elements on the screen. This is the list of elements that can be stylized:
@ -140,13 +140,16 @@ If there's an error in the style definition or if a missing style is assigned
to, the assignment will be ignored, along with a warning being printed on
stderr.
The 'default' theme is always implicitly parsed first (or the 'monochrome'
theme if the terminal lacks colors), so the following two settings have the
same effect:
The 'default' theme is always implicitly parsed first, so the following two
settings have the same effect:
MENUCONFIG_STYLE="selection=fg:white,bg:red"
MENUCONFIG_STYLE="default selection=fg:white,bg:red"
If the terminal doesn't support colors, the 'monochrome' theme is used, and
MENUCONFIG_STYLE is ignored. The assumption is that the environment is broken
somehow, and that the important thing is to get something usable.
Other features
==============
@ -177,27 +180,41 @@ Other features
Limitations
===========
Doesn't work out of the box on Windows, but can be made to work with 'pip
install windows-curses'. See the
https://github.com/zephyrproject-rtos/windows-curses repository.
Doesn't work out of the box on Windows, but can be made to work with
'pip install kconfiglib' on Windows automatically installs windows-curses
to make the menuconfig usable.
pip install windows-curses
See the https://github.com/zephyrproject-rtos/windows-curses repository.
"""
from __future__ import print_function
import os
import sys
_IS_WINDOWS = os.name == "nt" # Are we running on Windows?
try:
import curses
except ImportError:
print('"windows-curses" package is required in Windows command line. Please install it by running '
'"{} -m pip install --user windows-curses" (or without the "--user" option)'
''.format(sys.executable))
exit(1)
except ImportError as e:
if not _IS_WINDOWS:
raise
sys.exit("""\
menuconfig failed to import the standard Python 'curses' library. Try
installing a package like windows-curses
(https://github.com/zephyrproject-rtos/windows-curses) by running this command
in cmd.exe:
pip install windows-curses
Starting with Kconfiglib 13.0.0, windows-curses is no longer automatically
installed when installing Kconfiglib via pip on Windows (because it breaks
installation on MSYS2).
Exception:
{}: {}""".format(type(e).__name__, e))
import errno
import locale
import os
import re
import textwrap
@ -309,19 +326,18 @@ _STYLES = {
text=
""",
# Blue tinted style loosely resembling lxdialog
# Blue-tinted style loosely resembling lxdialog
"aquatic": """
path=fg:cyan,bg:blue,bold
separator=fg:white,bg:cyan,bold
help=path
frame=fg:white,bg:cyan,bold
body=fg:brightwhite,bg:blue
body=fg:white,bg:blue
edit=fg:black,bg:white
"""
}
# Standard colors definition
_STYLE_STD_COLORS = {
_NAMED_COLORS = {
# Basic colors
"black": curses.COLOR_BLACK,
"red": curses.COLOR_RED,
@ -547,9 +563,6 @@ def _style_to_curses(style_def):
def parse_color(color_def):
color_def = color_def.split(":", 1)[1]
if color_def in _STYLE_STD_COLORS:
return _color_from_num(_STYLE_STD_COLORS[color_def])
# HTML format, #RRGGBB
if re.match("#[A-Fa-f0-9]{6}", color_def):
return _color_from_rgb((
@ -557,19 +570,20 @@ def _style_to_curses(style_def):
int(color_def[3:5], 16),
int(color_def[5:7], 16)))
try:
color_num = _color_from_num(int(color_def, 0))
except ValueError:
_warn("Ignoring color ", color_def, "that's neither predefined "
"nor a number")
return -1
if color_def in _NAMED_COLORS:
color_num = _color_from_num(_NAMED_COLORS[color_def])
else:
try:
color_num = _color_from_num(int(color_def, 0))
except ValueError:
_warn("Ignoring color", color_def, "that's neither "
"predefined nor a number")
return -1
if not -1 <= color_num < curses.COLORS:
_warn("Ignoring color {}, which is outside the range "
"-1..curses.COLORS-1 (-1..{})"
.format(color_def, curses.COLORS - 1))
return -1
return color_num
@ -602,15 +616,26 @@ def _style_to_curses(style_def):
def _init_styles():
if curses.has_colors():
curses.use_default_colors()
try:
curses.use_default_colors()
except curses.error:
# Ignore errors on funky terminals that support colors but not
# using default colors. Worst it can do is break transparency and
# the like. Ran across this with the MSYS2/winpty setup in
# https://github.com/msys2/MINGW-packages/issues/5823, though there
# seems to be a lot of general brokenness there.
pass
# Use the 'monochrome' style template as the base on terminals without
# color
_parse_style("default" if curses.has_colors() else "monochrome", True)
# Add any user-defined style from the environment
if "MENUCONFIG_STYLE" in os.environ:
_parse_style(os.environ["MENUCONFIG_STYLE"], False)
# Use the 'default' theme as the base, and add any user-defined style
# settings from the environment
_parse_style("default", True)
if "MENUCONFIG_STYLE" in os.environ:
_parse_style(os.environ["MENUCONFIG_STYLE"], False)
else:
# Force the 'monochrome' theme if the terminal doesn't support colors.
# MENUCONFIG_STYLE is likely to mess things up here (though any colors
# would be ignored), so ignore it.
_parse_style("monochrome", True)
# color_attribs holds the color pairs we've already created, indexed by a
@ -644,7 +669,7 @@ def _style_attr(fg_color, bg_color, attribs, color_attribs={}):
def _main():
menuconfig(standard_kconfig())
menuconfig(standard_kconfig(__doc__))
def menuconfig(kconf):
@ -2210,9 +2235,7 @@ def _sorted_sc_nodes(cached_nodes=[]):
key=lambda choice: choice.name or "")
cached_nodes += sorted(
[node
for choice in choices
for node in choice.nodes],
[node for choice in choices for node in choice.nodes],
key=lambda node: node.prompt[0] if node.prompt else "")
return cached_nodes
@ -3273,8 +3296,5 @@ def _change_c_lc_ctype_to_utf8():
return
# Are we running on Windows?
_IS_WINDOWS = os.name == "nt"
if __name__ == "__main__":
_main()