tools: Update kconfiglib to 13.7.1

This fixes the crash on MacOs under Python 3 and improves the menuconfig
colors on some terminals.
This commit is contained in:
Roland Dobai 2020-01-13 12:04:54 +01:00
parent 647cb628a1
commit 3909208852
2 changed files with 118 additions and 104 deletions

View File

@ -554,7 +554,7 @@ from glob import iglob
from os.path import dirname, exists, expandvars, islink, join, realpath from os.path import dirname, exists, expandvars, islink, join, realpath
VERSION = (13, 3, 2) VERSION = (13, 7, 1)
# File layout: # File layout:
@ -865,7 +865,7 @@ class Kconfig(object):
# #
def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True, def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
encoding="utf-8"): encoding="utf-8", suppress_traceback=False):
""" """
Creates a new Kconfig object by parsing Kconfig files. Creates a new Kconfig object by parsing Kconfig files.
Note that Kconfig files are not the same as .config files (which store Note that Kconfig files are not the same as .config files (which store
@ -930,7 +930,35 @@ class Kconfig(object):
anyway. anyway.
Related PEP: https://www.python.org/dev/peps/pep-0538/ Related PEP: https://www.python.org/dev/peps/pep-0538/
suppress_traceback (default: False):
Helper for tools. When True, any EnvironmentError or KconfigError
generated during parsing is caught, the exception message is printed
to stderr together with the command name, and sys.exit(1) is called
(which generates SystemExit).
This hides the Python traceback for "expected" errors like syntax
errors in Kconfig files.
Other exceptions besides EnvironmentError and KconfigError are still
propagated when suppress_traceback is True.
""" """
try:
self._init(filename, warn, warn_to_stderr, encoding)
except (EnvironmentError, KconfigError) as e:
if suppress_traceback:
cmd = sys.argv[0] # Empty string if missing
if cmd:
cmd += ": "
# Some long exception messages have extra newlines for better
# formatting when reported as an unhandled exception. Strip
# them here.
sys.exit(cmd + str(e).strip())
raise
def _init(self, filename, warn, warn_to_stderr, encoding):
# See __init__()
self._encoding = encoding self._encoding = encoding
self.srctree = os.getenv("srctree", "") self.srctree = os.getenv("srctree", "")
@ -1052,8 +1080,9 @@ class Kconfig(object):
self._readline = self._open(join(self.srctree, filename), "r").readline self._readline = self._open(join(self.srctree, filename), "r").readline
try: try:
# Parse the Kconfig files # Parse the Kconfig files. Returns the last node, which we
self._parse_block(None, self.top_node, self.top_node) # terminate with '.next = None'.
self._parse_block(None, self.top_node, self.top_node).next = None
self.top_node.list = self.top_node.next self.top_node.list = self.top_node.next
self.top_node.next = None self.top_node.next = None
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
@ -1363,6 +1392,28 @@ class Kconfig(object):
elif self.warn_assign_override: elif self.warn_assign_override:
self._warn(msg, filename, linenr) self._warn(msg, filename, linenr)
def load_allconfig(self, filename):
"""
Helper for all*config. Loads (merges) the configuration file specified
by KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in
the Linux kernel.
Disables warnings for duplicated assignments within configuration files
for the duration of the call
(kconf.warn_assign_override/warn_assign_redun = False), and restores
the previous warning settings at the end. The KCONFIG_ALLCONFIG
configuration file is expected to override symbols.
Exits with sys.exit() (which raises a SystemExit exception) and prints
an error to stderr if KCONFIG_ALLCONFIG is set but the configuration
file can't be opened.
filename:
Command-specific configuration filename - "allyes.config",
"allno.config", etc.
"""
load_allconfig(self, filename)
def write_autoconf(self, filename=None, header=None): def write_autoconf(self, filename=None, header=None):
r""" r"""
Writes out symbol values as a C header file, matching the format used Writes out symbol values as a C header file, matching the format used
@ -2163,9 +2214,9 @@ class Kconfig(object):
# it's part of a different construct # it's part of a different construct
if self._reuse_tokens: if self._reuse_tokens:
self._reuse_tokens = False self._reuse_tokens = False
# self._tokens_i is known to be 1 here, because _parse_properties() # self._tokens_i is known to be 1 here, because _parse_props()
# leaves it like that when it can't recognize a line (or parses # leaves it like that when it can't recognize a line (or parses a
# a help text) # help text)
return True return True
# readline() returns '' over and over at EOF, which we rely on for help # readline() returns '' over and over at EOF, which we rely on for help
@ -2182,7 +2233,7 @@ class Kconfig(object):
self._tokens = self._tokenize(line) self._tokens = self._tokenize(line)
# Initialize to 1 instead of 0 to factor out code from _parse_block() # Initialize to 1 instead of 0 to factor out code from _parse_block()
# and _parse_properties(). They immediately fetch self._tokens[0]. # and _parse_props(). They immediately fetch self._tokens[0].
self._tokens_i = 1 self._tokens_i = 1
return True return True
@ -2844,7 +2895,7 @@ class Kconfig(object):
# #
# prev: # prev:
# The previous menu node. New nodes will be added after this one (by # The previous menu node. New nodes will be added after this one (by
# modifying their 'next' pointer). # modifying 'next' pointers).
# #
# 'prev' is reused to parse a list of child menu nodes (for a menu or # 'prev' is reused to parse a list of child menu nodes (for a menu or
# Choice): After parsing the children, the 'next' pointer is assigned # Choice): After parsing the children, the 'next' pointer is assigned
@ -2880,7 +2931,7 @@ class Kconfig(object):
sym.nodes.append(node) sym.nodes.append(node)
self._parse_properties(node) self._parse_props(node)
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"
@ -2966,7 +3017,7 @@ class Kconfig(object):
self.menus.append(node) self.menus.append(node)
self._parse_properties(node) self._parse_props(node)
self._parse_block(_T_ENDMENU, node, node) self._parse_block(_T_ENDMENU, node, node)
node.list = node.next node.list = node.next
@ -2986,7 +3037,7 @@ class Kconfig(object):
self.comments.append(node) self.comments.append(node)
self._parse_properties(node) self._parse_props(node)
prev.next = prev = node prev.next = prev = node
@ -3018,7 +3069,7 @@ class Kconfig(object):
choice.nodes.append(node) choice.nodes.append(node)
self._parse_properties(node) self._parse_props(node)
self._parse_block(_T_ENDCHOICE, node, node) self._parse_block(_T_ENDCHOICE, node, node)
node.list = node.next node.list = node.next
@ -3036,17 +3087,16 @@ class Kconfig(object):
"no corresponding 'menu'" if t0 is _T_ENDMENU else "no corresponding 'menu'" if t0 is _T_ENDMENU else
"unrecognized construct") "unrecognized construct")
# End of file reached. Terminate the final node and return it. # End of file reached. Return the last node.
if end_token: if end_token:
raise KconfigError( raise KconfigError(
"expected '{}' at end of '{}'" "error: expected '{}' at end of '{}'"
.format("endchoice" if end_token is _T_ENDCHOICE else .format("endchoice" if end_token is _T_ENDCHOICE else
"endif" if end_token is _T_ENDIF else "endif" if end_token is _T_ENDIF else
"endmenu", "endmenu",
self.filename)) self.filename))
prev.next = None
return prev return prev
def _parse_cond(self): def _parse_cond(self):
@ -3060,7 +3110,7 @@ class Kconfig(object):
return expr return expr
def _parse_properties(self, node): def _parse_props(self, node):
# Parses and adds properties to the MenuNode 'node' (type, 'prompt', # Parses and adds properties to the MenuNode 'node' (type, 'prompt',
# 'default's, etc.) Properties are later copied up to symbols and # 'default's, etc.) Properties are later copied up to symbols and
# choices in a separate pass after parsing, in e.g. # choices in a separate pass after parsing, in e.g.
@ -3086,7 +3136,7 @@ class Kconfig(object):
if t0 in _TYPE_TOKENS: if t0 in _TYPE_TOKENS:
# Relies on '_T_BOOL is BOOL', etc., to save a conversion # Relies on '_T_BOOL is BOOL', etc., to save a conversion
self._set_type(node, t0) self._set_type(node.item, t0)
if self._tokens[1] is not None: if self._tokens[1] is not None:
self._parse_prompt(node) self._parse_prompt(node)
@ -3116,7 +3166,7 @@ class Kconfig(object):
self._parse_cond())) self._parse_cond()))
elif t0 in _DEF_TOKEN_TO_TYPE: elif t0 in _DEF_TOKEN_TO_TYPE:
self._set_type(node, _DEF_TOKEN_TO_TYPE[t0]) self._set_type(node.item, _DEF_TOKEN_TO_TYPE[t0])
node.defaults.append((self._parse_expr(False), node.defaults.append((self._parse_expr(False),
self._parse_cond())) self._parse_cond()))
@ -3217,13 +3267,15 @@ class Kconfig(object):
self._reuse_tokens = True self._reuse_tokens = True
return return
def _set_type(self, node, new_type): def _set_type(self, sc, new_type):
# UNKNOWN is falsy # Sets the type of 'sc' (symbol or choice) to 'new_type'
if node.item.orig_type and node.item.orig_type is not new_type:
self._warn("{} defined with multiple types, {} will be used"
.format(node.item.name_and_loc, TYPE_TO_STR[new_type]))
node.item.orig_type = new_type # UNKNOWN is falsy
if sc.orig_type and sc.orig_type is not new_type:
self._warn("{} defined with multiple types, {} will be used"
.format(sc.name_and_loc, TYPE_TO_STR[new_type]))
sc.orig_type = new_type
def _parse_prompt(self, node): def _parse_prompt(self, node):
# 'prompt' properties override each other within a single definition of # 'prompt' properties override each other within a single definition of
@ -3413,7 +3465,7 @@ class Kconfig(object):
# The calculated sets might be larger than necessary as we don't do any # The calculated sets might be larger than necessary as we don't do any
# complex analysis of the expressions. # complex analysis of the expressions.
make_depend_on = _make_depend_on # Micro-optimization depend_on = _depend_on # Micro-optimization
# Only calculate _dependents for defined symbols. Constant and # Only calculate _dependents for defined symbols. Constant and
# undefined symbols could theoretically be selected/implied, but it # undefined symbols could theoretically be selected/implied, but it
@ -3424,29 +3476,29 @@ class Kconfig(object):
# The prompt conditions # The prompt conditions
for node in sym.nodes: for node in sym.nodes:
if node.prompt: if node.prompt:
make_depend_on(sym, node.prompt[1]) depend_on(sym, node.prompt[1])
# The default values and their conditions # The default values and their conditions
for value, cond in sym.defaults: for value, cond in sym.defaults:
make_depend_on(sym, value) depend_on(sym, value)
make_depend_on(sym, cond) depend_on(sym, cond)
# The reverse and weak reverse dependencies # The reverse and weak reverse dependencies
make_depend_on(sym, sym.rev_dep) depend_on(sym, sym.rev_dep)
make_depend_on(sym, sym.weak_rev_dep) depend_on(sym, sym.weak_rev_dep)
# The ranges along with their conditions # The ranges along with their conditions
for low, high, cond in sym.ranges: for low, high, cond in sym.ranges:
make_depend_on(sym, low) depend_on(sym, low)
make_depend_on(sym, high) depend_on(sym, high)
make_depend_on(sym, cond) depend_on(sym, cond)
# The direct dependencies. This is usually redundant, as the direct # The direct dependencies. This is usually redundant, as the direct
# dependencies get propagated to properties, but it's needed to get # dependencies get propagated to properties, but it's needed to get
# invalidation solid for 'imply', which only checks the direct # invalidation solid for 'imply', which only checks the direct
# dependencies (even if there are no properties to propagate it # dependencies (even if there are no properties to propagate it
# to). # to).
make_depend_on(sym, sym.direct_dep) depend_on(sym, sym.direct_dep)
# In addition to the above, choice symbols depend on the choice # In addition to the above, choice symbols depend on the choice
# they're in, but that's handled automatically since the Choice is # they're in, but that's handled automatically since the Choice is
@ -3459,11 +3511,11 @@ class Kconfig(object):
# The prompt conditions # The prompt conditions
for node in choice.nodes: for node in choice.nodes:
if node.prompt: if node.prompt:
make_depend_on(choice, node.prompt[1]) depend_on(choice, node.prompt[1])
# The default symbol conditions # The default symbol conditions
for _, cond in choice.defaults: for _, cond in choice.defaults:
make_depend_on(choice, cond) depend_on(choice, cond)
def _add_choice_deps(self): def _add_choice_deps(self):
# Choices also depend on the choice symbols themselves, because the # Choices also depend on the choice symbols themselves, because the
@ -3817,7 +3869,7 @@ class Kconfig(object):
.format(sym.name_and_loc)) .format(sym.name_and_loc))
def _parse_error(self, msg): def _parse_error(self, msg):
raise KconfigError("{}couldn't parse '{}': {}".format( raise KconfigError("{}error: couldn't parse '{}': {}".format(
"" if self.filename is None else "" if self.filename is None else
"{}:{}: ".format(self.filename, self.linenr), "{}:{}: ".format(self.filename, self.linenr),
self._line.strip(), msg)) self._line.strip(), msg))
@ -5336,8 +5388,8 @@ class Choice(object):
self._cached_selection = _NO_CACHED_SELECTION self._cached_selection = _NO_CACHED_SELECTION
# is_constant is checked by _make_depend_on(). Just set it to avoid # is_constant is checked by _depend_on(). Just set it to avoid having
# having to special-case choices. # to special-case choices.
self.is_constant = self.is_optional = False self.is_constant = self.is_optional = False
# See Kconfig._build_dep() # See Kconfig._build_dep()
@ -6158,17 +6210,9 @@ def standard_kconfig(description=None):
metavar="KCONFIG", metavar="KCONFIG",
default="Kconfig", default="Kconfig",
nargs="?", nargs="?",
help="Kconfig file (default: Kconfig)") help="Top-level Kconfig file (default: Kconfig)")
args = parser.parse_args() return Kconfig(parser.parse_args().kconfig, suppress_traceback=True)
# Suppress backtraces for expected exceptions
try:
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.
sys.exit(str(e).strip())
def standard_config_filename(): def standard_config_filename():
@ -6184,25 +6228,9 @@ def standard_config_filename():
def load_allconfig(kconf, filename): def load_allconfig(kconf, filename):
""" """
Helper for all*config. Loads (merges) the configuration file specified by Use Kconfig.load_allconfig() instead, which was added in Kconfiglib 13.4.0.
KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in the Supported for backwards compatibility. Might be removed at some point after
Linux kernel. a long period of deprecation warnings.
Disables warnings for duplicated assignments within configuration files for
the duration of the call (kconf.warn_assign_override/warn_assign_redun = False),
and restores the previous warning settings at the end. The
KCONFIG_ALLCONFIG configuration file is expected to override symbols.
Exits with sys.exit() (which raises a SystemExit exception) and prints an
error to stderr if KCONFIG_ALLCONFIG is set but the configuration file
can't be opened.
kconf:
Kconfig instance to load the configuration in.
filename:
Command-specific configuration filename - "allyes.config",
"allno.config", etc.
""" """
allconfig = os.getenv("KCONFIG_ALLCONFIG") allconfig = os.getenv("KCONFIG_ALLCONFIG")
if allconfig is None: if allconfig is None:
@ -6278,7 +6306,7 @@ def _visibility(sc):
return vis return vis
def _make_depend_on(sc, expr): def _depend_on(sc, expr):
# Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'. # Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'.
# Constant symbols in 'expr' are skipped as they can never change value # Constant symbols in 'expr' are skipped as they can never change value
# anyway. # anyway.
@ -6286,11 +6314,11 @@ def _make_depend_on(sc, expr):
if expr.__class__ is tuple: if expr.__class__ is tuple:
# AND, OR, NOT, or relation # AND, OR, NOT, or relation
_make_depend_on(sc, expr[1]) _depend_on(sc, expr[1])
# NOTs only have a single operand # NOTs only have a single operand
if expr[0] is not NOT: if expr[0] is not NOT:
_make_depend_on(sc, expr[2]) _depend_on(sc, expr[2])
elif not expr.is_constant: elif not expr.is_constant:
# Non-constant symbol, or choice # Non-constant symbol, or choice
@ -6744,8 +6772,7 @@ def _error_if_fn(kconf, _, cond, msg):
def _shell_fn(kconf, _, command): def _shell_fn(kconf, _, command):
# Only import as needed, to save some startup time import subprocess # Only import as needed, to save some startup time
import subprocess
stdout, stderr = subprocess.Popen( stdout, stderr = subprocess.Popen(
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE

View File

@ -328,10 +328,10 @@ _STYLES = {
# Blue-tinted style loosely resembling lxdialog # Blue-tinted style loosely resembling lxdialog
"aquatic": """ "aquatic": """
path=fg:cyan,bg:blue,bold path=fg:white,bg:blue
separator=fg:white,bg:cyan,bold separator=fg:white,bg:cyan
help=path help=path
frame=fg:white,bg:cyan,bold frame=fg:white,bg:cyan
body=fg:white,bg:blue body=fg:white,bg:blue
edit=fg:black,bg:white edit=fg:black,bg:white
""" """
@ -1107,8 +1107,7 @@ def _enter_menu(menu):
global _menu_scroll global _menu_scroll
if not menu.is_menuconfig: if not menu.is_menuconfig:
# Not a menu return False # Not a menu
return False
shown_sub = _shown_nodes(menu) shown_sub = _shown_nodes(menu)
# Never enter empty menus. We depend on having a current node. # Never enter empty menus. We depend on having a current node.
@ -1349,7 +1348,6 @@ def _draw_main():
term_width = _width(_stdscr) term_width = _width(_stdscr)
# #
# Update the separator row below the menu path # Update the separator row below the menu path
# #
@ -1396,7 +1394,6 @@ def _draw_main():
_menu_win.noutrefresh() _menu_win.noutrefresh()
# #
# Update the bottom separator window # Update the bottom separator window
# #
@ -1421,7 +1418,6 @@ def _draw_main():
_bot_sep_win.noutrefresh() _bot_sep_win.noutrefresh()
# #
# Update the help window, which shows either key bindings or help texts # Update the help window, which shows either key bindings or help texts
# #
@ -1442,7 +1438,6 @@ def _draw_main():
_help_win.noutrefresh() _help_win.noutrefresh()
# #
# Update the top row with the menu path. # Update the top row with the menu path.
# #
@ -2302,7 +2297,6 @@ def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
edit_width = _width(edit_box) - 2 edit_width = _width(edit_box) - 2
# #
# Update list of matches # Update list of matches
# #
@ -2333,7 +2327,6 @@ def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
matches_win.noutrefresh() matches_win.noutrefresh()
# #
# Update bottom separator line # Update bottom separator line
# #
@ -2346,7 +2339,6 @@ def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
bot_sep_win.noutrefresh() bot_sep_win.noutrefresh()
# #
# Update help window at bottom # Update help window at bottom
# #
@ -2358,7 +2350,6 @@ def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
help_win.noutrefresh() help_win.noutrefresh()
# #
# Update edit box. We do this last since it makes it handy to position the # Update edit box. We do this last since it makes it handy to position the
# cursor. # cursor.
@ -2448,12 +2439,10 @@ def _info_dialog(node, from_jump_to_dialog):
# Support starting a search from within the information dialog # Support starting a search from within the information dialog
if from_jump_to_dialog: if from_jump_to_dialog:
# Avoid recursion return # Avoid recursion
return
if _jump_to_dialog(): if _jump_to_dialog():
# Jumped to a symbol. Cancel the information dialog. return # Jumped to a symbol. Cancel the information dialog.
return
# Stay in the information dialog if the jump-to dialog was # Stay in the information dialog if the jump-to dialog was
# canceled. Resize it in case the terminal was resized while the # canceled. Resize it in case the terminal was resized while the
@ -2500,7 +2489,6 @@ def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
text_win_height, text_win_width = text_win.getmaxyx() text_win_height, text_win_width = text_win.getmaxyx()
# Note: The top row is deliberately updated last. See _draw_main(). # Note: The top row is deliberately updated last. See _draw_main().
# #
@ -2514,7 +2502,6 @@ def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
text_win.noutrefresh() text_win.noutrefresh()
# #
# Update bottom separator line # Update bottom separator line
# #
@ -2527,7 +2514,6 @@ def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
bot_sep_win.noutrefresh() bot_sep_win.noutrefresh()
# #
# Update help window at bottom # Update help window at bottom
# #
@ -2539,7 +2525,6 @@ def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
help_win.noutrefresh() help_win.noutrefresh()
# #
# Update top row # Update top row
# #
@ -2598,8 +2583,7 @@ def _info_str(node):
_kconfig_def_info(choice) _kconfig_def_info(choice)
) )
# node.item in (MENU, COMMENT) return _kconfig_def_info(node) # node.item in (MENU, COMMENT)
return _kconfig_def_info(node)
def _name_info(sc): def _name_info(sc):
@ -2956,7 +2940,6 @@ def _edit_text(c, s, i, hscroll, width):
max_scroll = max(len(s) - width + 1, 0) max_scroll = max(len(s) - width + 1, 0)
hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll) hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll)
return s, i, hscroll return s, i, hscroll
@ -3106,8 +3089,7 @@ def _check_valid(sym, s):
# Otherwise, displays an error and returns False. # Otherwise, displays an error and returns False.
if sym.orig_type not in (INT, HEX): if sym.orig_type not in (INT, HEX):
# Anything goes for non-int/hex symbols return True # Anything goes for non-int/hex symbols
return True
base = 10 if sym.orig_type == INT else 16 base = 10 if sym.orig_type == INT else 16
try: try:
@ -3164,12 +3146,17 @@ def _is_num(name):
def _getch_compat(win): def _getch_compat(win):
# Uses get_wch() if available (Python 3.3+) and getch() otherwise. Also # Uses get_wch() if available (Python 3.3+) and getch() otherwise.
# handles a PDCurses resizing quirk. #
# Also falls back on getch() if get_wch() raises curses.error, to work
# around an issue when resizing the terminal on at least macOS Catalina.
# See https://github.com/ulfalizer/Kconfiglib/issues/84.
#
# Also handles a PDCurses resizing quirk.
if hasattr(win, "get_wch"): try:
c = win.get_wch() c = win.get_wch()
else: except (AttributeError, curses.error):
c = win.getch() c = win.getch()
if 0 <= c <= 255: if 0 <= c <= 255:
c = chr(c) c = chr(c)