Kaynağa Gözat

Merge branch 'bugfix/update_kconfiglib_v4.1' into 'release/v4.1'

tools: Update kconfiglib to 13.7.1 (v4.1)

See merge request espressif/esp-idf!7340
Angus Gratton 6 yıl önce
ebeveyn
işleme
51e208533e
2 değiştirilmiş dosya ile 116 ekleme ve 102 silme
  1. 99 72
      tools/kconfig_new/kconfiglib.py
  2. 17 30
      tools/kconfig_new/menuconfig.py

+ 99 - 72
tools/kconfig_new/kconfiglib.py

@@ -554,7 +554,7 @@ from glob import iglob
 from os.path import dirname, exists, expandvars, islink, join, realpath
 
 
-VERSION = (13, 3, 2)
+VERSION = (13, 7, 1)
 
 
 # File layout:
@@ -865,7 +865,7 @@ class Kconfig(object):
     #
 
     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.
         Note that Kconfig files are not the same as .config files (which store
@@ -930,7 +930,35 @@ class Kconfig(object):
           anyway.
 
           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.srctree = os.getenv("srctree", "")
@@ -1052,8 +1080,9 @@ class Kconfig(object):
         self._readline = self._open(join(self.srctree, filename), "r").readline
 
         try:
-            # Parse the Kconfig files
-            self._parse_block(None, self.top_node, self.top_node)
+            # Parse the Kconfig files. Returns the last node, which we
+            # 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.next = None
         except UnicodeDecodeError as e:
@@ -1363,6 +1392,28 @@ class Kconfig(object):
         elif self.warn_assign_override:
             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):
         r"""
         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
         if self._reuse_tokens:
             self._reuse_tokens = False
-            # self._tokens_i is known to be 1 here, because _parse_properties()
-            # leaves it like that when it can't recognize a line (or parses
-            # a help text)
+            # 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 a
+            # help text)
             return True
 
         # 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)
         # 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
 
         return True
@@ -2844,7 +2895,7 @@ class Kconfig(object):
         #
         # prev:
         #   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
         #   Choice): After parsing the children, the 'next' pointer is assigned
@@ -2880,7 +2931,7 @@ class Kconfig(object):
 
                 sym.nodes.append(node)
 
-                self._parse_properties(node)
+                self._parse_props(node)
 
                 if node.is_menuconfig and not node.prompt:
                     self._warn("the menuconfig symbol {} has no prompt"
@@ -2966,7 +3017,7 @@ class Kconfig(object):
 
                 self.menus.append(node)
 
-                self._parse_properties(node)
+                self._parse_props(node)
                 self._parse_block(_T_ENDMENU, node, node)
                 node.list = node.next
 
@@ -2986,7 +3037,7 @@ class Kconfig(object):
 
                 self.comments.append(node)
 
-                self._parse_properties(node)
+                self._parse_props(node)
 
                 prev.next = prev = node
 
@@ -3018,7 +3069,7 @@ class Kconfig(object):
 
                 choice.nodes.append(node)
 
-                self._parse_properties(node)
+                self._parse_props(node)
                 self._parse_block(_T_ENDCHOICE, node, node)
                 node.list = node.next
 
@@ -3036,17 +3087,16 @@ class Kconfig(object):
                     "no corresponding 'menu'"   if t0 is _T_ENDMENU else
                     "unrecognized construct")
 
-        # End of file reached. Terminate the final node and return it.
+        # End of file reached. Return the last node.
 
         if end_token:
             raise KconfigError(
-                "expected '{}' at end of '{}'"
+                "error: expected '{}' at end of '{}'"
                 .format("endchoice" if end_token is _T_ENDCHOICE else
                         "endif"     if end_token is _T_ENDIF else
                         "endmenu",
                         self.filename))
 
-        prev.next = None
         return prev
 
     def _parse_cond(self):
@@ -3060,7 +3110,7 @@ class Kconfig(object):
 
         return expr
 
-    def _parse_properties(self, node):
+    def _parse_props(self, node):
         # Parses and adds properties to the MenuNode 'node' (type, 'prompt',
         # 'default's, etc.) Properties are later copied up to symbols and
         # choices in a separate pass after parsing, in e.g.
@@ -3086,7 +3136,7 @@ class Kconfig(object):
 
             if t0 in _TYPE_TOKENS:
                 # 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:
                     self._parse_prompt(node)
 
@@ -3116,7 +3166,7 @@ class Kconfig(object):
                                       self._parse_cond()))
 
             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),
                                       self._parse_cond()))
 
@@ -3217,13 +3267,15 @@ class Kconfig(object):
                 self._reuse_tokens = True
                 return
 
-    def _set_type(self, node, new_type):
+    def _set_type(self, sc, new_type):
+        # Sets the type of 'sc' (symbol or choice) to 'new_type'
+
         # UNKNOWN is falsy
-        if node.item.orig_type and node.item.orig_type is not new_type:
+        if sc.orig_type and sc.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]))
+                       .format(sc.name_and_loc, TYPE_TO_STR[new_type]))
 
-        node.item.orig_type = new_type
+        sc.orig_type = new_type
 
     def _parse_prompt(self, node):
         # '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
         # 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
         # undefined symbols could theoretically be selected/implied, but it
@@ -3424,29 +3476,29 @@ class Kconfig(object):
             # The prompt conditions
             for node in sym.nodes:
                 if node.prompt:
-                    make_depend_on(sym, node.prompt[1])
+                    depend_on(sym, node.prompt[1])
 
             # The default values and their conditions
             for value, cond in sym.defaults:
-                make_depend_on(sym, value)
-                make_depend_on(sym, cond)
+                depend_on(sym, value)
+                depend_on(sym, cond)
 
             # The reverse and weak reverse dependencies
-            make_depend_on(sym, sym.rev_dep)
-            make_depend_on(sym, sym.weak_rev_dep)
+            depend_on(sym, sym.rev_dep)
+            depend_on(sym, sym.weak_rev_dep)
 
             # The ranges along with their conditions
             for low, high, cond in sym.ranges:
-                make_depend_on(sym, low)
-                make_depend_on(sym, high)
-                make_depend_on(sym, cond)
+                depend_on(sym, low)
+                depend_on(sym, high)
+                depend_on(sym, cond)
 
             # The direct dependencies. This is usually redundant, as the direct
             # dependencies get propagated to properties, but it's needed to get
             # invalidation solid for 'imply', which only checks the direct
             # dependencies (even if there are no properties to propagate it
             # 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
             # they're in, but that's handled automatically since the Choice is
@@ -3459,11 +3511,11 @@ class Kconfig(object):
             # The prompt conditions
             for node in choice.nodes:
                 if node.prompt:
-                    make_depend_on(choice, node.prompt[1])
+                    depend_on(choice, node.prompt[1])
 
             # The default symbol conditions
             for _, cond in choice.defaults:
-                make_depend_on(choice, cond)
+                depend_on(choice, cond)
 
     def _add_choice_deps(self):
         # Choices also depend on the choice symbols themselves, because the
@@ -3817,7 +3869,7 @@ class Kconfig(object):
                                    .format(sym.name_and_loc))
 
     def _parse_error(self, msg):
-        raise KconfigError("{}couldn't parse '{}': {}".format(
+        raise KconfigError("{}error: couldn't parse '{}': {}".format(
             "" if self.filename is None else
                 "{}:{}: ".format(self.filename, self.linenr),
             self._line.strip(), msg))
@@ -5336,8 +5388,8 @@ class Choice(object):
 
         self._cached_selection = _NO_CACHED_SELECTION
 
-        # is_constant is checked by _make_depend_on(). Just set it to avoid
-        # having to special-case choices.
+        # is_constant is checked by _depend_on(). Just set it to avoid having
+        # to special-case choices.
         self.is_constant = self.is_optional = False
 
         # See Kconfig._build_dep()
@@ -6158,17 +6210,9 @@ def standard_kconfig(description=None):
         metavar="KCONFIG",
         default="Kconfig",
         nargs="?",
-        help="Kconfig file (default: Kconfig)")
-
-    args = parser.parse_args()
+        help="Top-level Kconfig file (default: Kconfig)")
 
-    # 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())
+    return Kconfig(parser.parse_args().kconfig, suppress_traceback=True)
 
 
 def standard_config_filename():
@@ -6184,25 +6228,9 @@ def standard_config_filename():
 
 def load_allconfig(kconf, 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.
-
-    kconf:
-      Kconfig instance to load the configuration in.
-
-    filename:
-      Command-specific configuration filename - "allyes.config",
-      "allno.config", etc.
+    Use Kconfig.load_allconfig() instead, which was added in Kconfiglib 13.4.0.
+    Supported for backwards compatibility. Might be removed at some point after
+    a long period of deprecation warnings.
     """
     allconfig = os.getenv("KCONFIG_ALLCONFIG")
     if allconfig is None:
@@ -6278,7 +6306,7 @@ def _visibility(sc):
     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'.
     # Constant symbols in 'expr' are skipped as they can never change value
     # anyway.
@@ -6286,11 +6314,11 @@ def _make_depend_on(sc, expr):
     if expr.__class__ is tuple:
         # AND, OR, NOT, or relation
 
-        _make_depend_on(sc, expr[1])
+        _depend_on(sc, expr[1])
 
         # NOTs only have a single operand
         if expr[0] is not NOT:
-            _make_depend_on(sc, expr[2])
+            _depend_on(sc, expr[2])
 
     elif not expr.is_constant:
         # Non-constant symbol, or choice
@@ -6744,8 +6772,7 @@ def _error_if_fn(kconf, _, cond, msg):
 
 
 def _shell_fn(kconf, _, command):
-    # Only import as needed, to save some startup time
-    import subprocess
+    import subprocess  # Only import as needed, to save some startup time
 
     stdout, stderr = subprocess.Popen(
         command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE

+ 17 - 30
tools/kconfig_new/menuconfig.py

@@ -328,10 +328,10 @@ _STYLES = {
 
     # Blue-tinted style loosely resembling lxdialog
     "aquatic": """
-    path=fg:cyan,bg:blue,bold
-    separator=fg:white,bg:cyan,bold
+    path=fg:white,bg:blue
+    separator=fg:white,bg:cyan
     help=path
-    frame=fg:white,bg:cyan,bold
+    frame=fg:white,bg:cyan
     body=fg:white,bg:blue
     edit=fg:black,bg:white
     """
@@ -1107,8 +1107,7 @@ def _enter_menu(menu):
     global _menu_scroll
 
     if not menu.is_menuconfig:
-        # Not a menu
-        return False
+        return False  # Not a menu
 
     shown_sub = _shown_nodes(menu)
     # Never enter empty menus. We depend on having a current node.
@@ -1349,7 +1348,6 @@ def _draw_main():
 
     term_width = _width(_stdscr)
 
-
     #
     # Update the separator row below the menu path
     #
@@ -1396,7 +1394,6 @@ def _draw_main():
 
     _menu_win.noutrefresh()
 
-
     #
     # Update the bottom separator window
     #
@@ -1421,7 +1418,6 @@ def _draw_main():
 
     _bot_sep_win.noutrefresh()
 
-
     #
     # Update the help window, which shows either key bindings or help texts
     #
@@ -1442,7 +1438,6 @@ def _draw_main():
 
     _help_win.noutrefresh()
 
-
     #
     # 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
 
-
     #
     # 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()
 
-
     #
     # 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()
 
-
     #
     # 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()
 
-
     #
     # Update edit box. We do this last since it makes it handy to position the
     # cursor.
@@ -2448,12 +2439,10 @@ def _info_dialog(node, from_jump_to_dialog):
             # Support starting a search from within the information dialog
 
             if from_jump_to_dialog:
-                # Avoid recursion
-                return
+                return  # Avoid recursion
 
             if _jump_to_dialog():
-                # Jumped to a symbol. Cancel the information dialog.
-                return
+                return  # Jumped to a symbol. Cancel the information dialog.
 
             # Stay in the information dialog if the jump-to dialog was
             # 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()
 
-
     # 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()
 
-
     #
     # Update bottom separator line
     #
@@ -2527,7 +2514,6 @@ def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
 
     bot_sep_win.noutrefresh()
 
-
     #
     # Update help window at bottom
     #
@@ -2539,7 +2525,6 @@ def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
 
     help_win.noutrefresh()
 
-
     #
     # Update top row
     #
@@ -2598,8 +2583,7 @@ def _info_str(node):
             _kconfig_def_info(choice)
         )
 
-    # node.item in (MENU, COMMENT)
-    return _kconfig_def_info(node)
+    return _kconfig_def_info(node)  # node.item in (MENU, COMMENT)
 
 
 def _name_info(sc):
@@ -2956,7 +2940,6 @@ def _edit_text(c, s, i, hscroll, width):
         max_scroll = max(len(s) - width + 1, 0)
         hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll)
 
-
     return s, i, hscroll
 
 
@@ -3106,8 +3089,7 @@ def _check_valid(sym, s):
     # Otherwise, displays an error and returns False.
 
     if sym.orig_type not in (INT, HEX):
-        # Anything goes for non-int/hex symbols
-        return True
+        return True  # Anything goes for non-int/hex symbols
 
     base = 10 if sym.orig_type == INT else 16
     try:
@@ -3164,12 +3146,17 @@ def _is_num(name):
 
 
 def _getch_compat(win):
-    # Uses get_wch() if available (Python 3.3+) and getch() otherwise. Also
-    # handles a PDCurses resizing quirk.
+    # Uses get_wch() if available (Python 3.3+) and getch() otherwise.
+    #
+    # 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()
-    else:
+    except (AttributeError, curses.error):
         c = win.getch()
         if 0 <= c <= 255:
             c = chr(c)