Skip to contentSkip to conversation

Update Kconfiglib with 'imply' support

PATCH New Ulf Magnusson 2 participants 4 messages Sep 19, 2017 – Oct 2, 2017

lore
Share & tools
lore
#0 lore raw

v2 cover Update Kconfiglib with 'imply' support

Changes for v2:
tools/moveconfig.py previously depended on a hack that merged 'select's with 'imply's. Update it to look at both so that it will work the same way with the new kconfiglib version.

Ulf Magnusson (1):
kconfiglib: update with 'imply' support

 tools/buildman/kconfiglib.py | 333 +++++++++++++++++++++++++------------------
 tools/moveconfig.py          |   2 +-
 2 files changed, 194 insertions(+), 141 deletions(-)
Signature · 1 line
2.7.4
#1 lore raw

PATCH v2 1/1 kconfiglib: update with 'imply' support

Corresponds to 375506d (File writing nit) from upstream
(https://github.com/ulfalizer/Kconfiglib).

Adds proper 'imply' support and fixes a few minor issues, one of which previously triggered the following weird warning:

  configs/taurus_defconfig: /tmp/tmpisI45S:6: warning: assignment to SPL_LDSCRIPT changes mode of containing choice from "arch/$(ARCH)/cpu/u-boot-spl.lds" to "y"

The change in 8639f69 (genconfig.py: Print defconfig next to warnings) was reapplied.

tools/moveconfig.py previously depended on a hack that merged 'select's with 'imply's. It was modified to look at the union of Symbol.get_selected_symbols() and Symbol.get_implied_symbols(), which should give the same behavior.

tools/genboardscfg.py was verified to produce identical board.cfg's before and after the change.

---
 tools/buildman/kconfiglib.py | 333 +++++++++++++++++++++++++------------------
 tools/moveconfig.py          |   2 +-
 2 files changed, 194 insertions(+), 141 deletions(-)
tools/buildman/kconfiglib.py +193 -140
index 352ad43..68b470a 100644
@@ -73,6 +73,7 @@ email service. Don't wrestle with internal APIs. Tell me what you need and I
 might add it in a safe way as a client API instead."""
 
 import os
+import platform
 import re
 import sys
 
@@ -137,10 +138,8 @@ class Config(object):
         # The set of all symbols, indexed by name (a string)
         self.syms = {}
         # Python 2/3 compatibility hack. This is the only one needed.
-        if sys.version_info[0] >= 3:
-            self.syms_iter = self.syms.values
-        else:
-            self.syms_iter = self.syms.itervalues
+        self.syms_iter = self.syms.values if sys.version_info[0] >= 3 else \
+                         self.syms.itervalues
 
         # The set of all defined symbols in the configuration in the order they
         # appear in the Kconfig files. This excludes the special symbols n, m,
@@ -173,7 +172,7 @@ class Config(object):
         self.m = register_special_symbol(TRISTATE, "m", "m")
         self.y = register_special_symbol(TRISTATE, "y", "y")
         # DEFCONFIG_LIST uses this
-        register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2])
+        register_special_symbol(STRING, "UNAME_RELEASE", platform.uname()[2])
 
         # The symbol with "option defconfig_list" set, containing a list of
         # default .config files
@@ -183,16 +182,20 @@ class Config(object):
         self.arch = os.environ.get("ARCH")
         self.srcarch = os.environ.get("SRCARCH")
 
+        # If you set CONFIG_ in the environment, Kconfig will prefix all symbols
+        # with its value when saving the configuration, instead of using the default, "CONFIG_".
+        self.config_prefix = os.environ.get("CONFIG_")
+        if self.config_prefix is None:
+            self.config_prefix = "CONFIG_"
+
         # See Config.__init__(). We need this for get_defconfig_filename().
         self.srctree = os.environ.get("srctree")
         if self.srctree is None:
             self.srctree = "."
 
         self.filename = filename
-        if base_dir is None:
-            self.base_dir = self.srctree
-        else:
-            self.base_dir = os.path.expandvars(base_dir)
+        self.base_dir = self.srctree if base_dir is None else \
+                        os.path.expandvars(base_dir)
 
         # The 'mainmenu' text
         self.mainmenu_text = None
@@ -222,7 +225,8 @@ class Config(object):
         self._transform_m = None
 
         # Parse the Kconfig files
-        self.top_block = self._parse_file(filename, None, None, None)
+        self.top_block = []
+        self._parse_file(filename, None, None, None, self.top_block)
 
         # Build Symbol.dep for all symbols
         self._build_dep()
@@ -405,6 +409,10 @@ class Config(object):
         """
 
         self._warnings = []
+        # Regular expressions for parsing .config files
+        _set_re_match = re.compile(r"{}(\w+)=(.*)".format(self.config_prefix)).match
+        _unset_re_match = re.compile(r"# {}(\w+) is not set".format(self.config_prefix)).match
+
         # Put this first so that a missing file doesn't screw up our state
         filename = os.path.expandvars(filename)
         line_feeder = _FileFeed(filename)
@@ -524,14 +532,12 @@ class Config(object):
         with open(filename, "w") as f:
             # Write header
             if header is not None:
-                f.write(_comment(header))
-                f.write("\n")
+                f.write(_comment(header) + "\n")
 
             # Build and write configuration
             conf_strings = []
             _make_block_conf(self.top_block, conf_strings.append)
-            f.write("\n".join(conf_strings))
-            f.write("\n")
+            f.write("\n".join(conf_strings) + "\n")
 
     def eval(self, s):
         """Returns the value of the expression 's' -- where 's' is represented
@@ -609,16 +615,18 @@ class Config(object):
     # Kconfig parsing
     #
 
-    def _parse_file(self, filename, parent, deps, visible_if_deps, res=None):
-        """Parses the Kconfig file 'filename'. Returns a list with the Items in
-        the file. See _parse_block() for the meaning of the parameters."""
-        return self._parse_block(_FileFeed(filename), None, parent, deps,
-                                 visible_if_deps, res)
+    def _parse_file(self, filename, parent, deps, visible_if_deps, block):
+        """Parses the Kconfig file 'filename'. Appends the Items in the file
+        (and any file it sources) to the list passed in the 'block' parameter.
+        See _parse_block() for the meaning of the parameters."""
+        self._parse_block(_FileFeed(filename), None, parent, deps,
+                          visible_if_deps, block)
 
     def _parse_block(self, line_feeder, end_marker, parent, deps,
-                     visible_if_deps, res=None):
+                     visible_if_deps, block):
         """Parses a block, which is the contents of either a file or an if,
-        menu, or choice statement. Returns a list with the Items in the block.
+        menu, or choice statement. Appends the Items to the list passed in the
+        'block' parameter.
 
         line_feeder: A _FileFeed instance feeding lines from a file. The
           Kconfig language is line-based in practice.
@@ -634,10 +642,7 @@ class Config(object):
         visible_if_deps (default: None): 'visible if' dependencies from
            enclosing menus.
 
-        res (default: None): The list to add items to. If None, a new list is
-           created to hold the items."""
-
-        block = [] if res is None else res
+        block: The list to add items to."""
 
         while 1:
             # Do we already have a tokenized line that we determined wasn't
@@ -656,7 +661,7 @@ class Config(object):
                     if end_marker is not None:
                         raise Kconfig_Syntax_Error("Unexpected end of file {0}"
                                                  .format(line_feeder.filename))
-                    return block
+                    return
 
                 tokens = self._tokenize(line, False, line_feeder.filename,
                                         line_feeder.linenr)
@@ -679,14 +684,13 @@ class Config(object):
                 # choice statements, the choice statement takes precedence.
                 if not sym.is_defined_ or isinstance(parent, Choice):
                     sym.parent = parent
-
                 sym.is_defined_ = True
 
+                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
+
                 self.kconfig_syms.append(sym)
                 block.append(sym)
 
-                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
-
             elif t0 == T_SOURCE:
                 kconfig_file = tokens.get_next()
                 exp_kconfig_file = self._expand_sym_refs(kconfig_file)
@@ -705,7 +709,7 @@ class Config(object):
 
             elif t0 == end_marker:
                 # We have reached the end of the block
-                return block
+                return
 
             elif t0 == T_IF:
                 # If statements are treated as syntactic sugar for adding
@@ -722,38 +726,39 @@ class Config(object):
 
             elif t0 == T_COMMENT:
                 comment = Comment()
-
                 comment.config = self
                 comment.parent = parent
                 comment.filename = line_feeder.filename
                 comment.linenr = line_feeder.linenr
                 comment.text = tokens.get_next()
 
-                self.comments.append(comment)
-                block.append(comment)
-
                 self._parse_properties(line_feeder, comment, deps,
                                        visible_if_deps)
 
+                self.comments.append(comment)
+                block.append(comment)
+
             elif t0 == T_MENU:
                 menu = Menu()
-
                 menu.config = self
                 menu.parent = parent
                 menu.filename = line_feeder.filename
                 menu.linenr = line_feeder.linenr
                 menu.title = tokens.get_next()
 
-                self.menus.append(menu)
-                block.append(menu)
-
-                # Parse properties and contents
                 self._parse_properties(line_feeder, menu, deps,
                                        visible_if_deps)
-                menu.block = self._parse_block(line_feeder, T_ENDMENU, menu,
-                                               menu.dep_expr,
-                                               _make_and(visible_if_deps,
-                                                         menu.visible_if_expr))
+
+                # This needs to go before _parse_block() so that we get the
+                # proper menu ordering in the case of nested functions
+                self.menus.append(menu)
+                # Parse contents and put Items in menu.block
+                self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr,
+                                  _make_and(visible_if_deps,
+                                            menu.visible_if_expr),
+                                  menu.block)
+
+                block.append(menu)
 
             elif t0 == T_CHOICE:
                 name = tokens.get_next()
@@ -775,11 +780,12 @@ class Config(object):
                 choice.def_locations.append((line_feeder.filename,
                                              line_feeder.linenr))
 
-                # Parse properties and contents
                 self._parse_properties(line_feeder, choice, deps,
                                        visible_if_deps)
-                choice.block = self._parse_block(line_feeder, T_ENDCHOICE,
-                                                 choice, deps, visible_if_deps)
+
+                # Parse contents and put Items in choice.block
+                self._parse_block(line_feeder, T_ENDCHOICE, choice, deps,
+                                  visible_if_deps, choice.block)
 
                 choice._determine_actual_symbols()
 
@@ -819,19 +825,19 @@ class Config(object):
             """Parses '<expr1> if <expr2>' constructs, where the 'if' part is
             optional. Returns a tuple containing the parsed expressions, with
             None as the second element if the 'if' part is missing."""
-            val = self._parse_expr(tokens, stmt, line, filename, linenr, False)
-            if tokens.check(T_IF):
-                return (val, self._parse_expr(tokens, stmt, line, filename,
-                                              linenr))
-            return (val, None)
+            return (self._parse_expr(tokens, stmt, line, filename, linenr,
+                                     False),
+                    self._parse_expr(tokens, stmt, line, filename, linenr)
+                    if tokens.check(T_IF) else None)
 
         # In case the symbol is defined in multiple locations, we need to
-        # remember what prompts, defaults, and selects are new for this
-        # definition, as "depends on" should only apply to the local
+        # remember what prompts, defaults, selects, and implies are new for
+        # this definition, as "depends on" should only apply to the local
         # definition.
         new_prompt = None
         new_def_exprs = []
         new_selects = []
+        new_implies = []
 
         # Dependencies from 'depends on' statements
         depends_on_expr = None
@@ -897,18 +903,27 @@ class Config(object):
 
                 line_feeder.unget()
 
-            elif t0 == T_SELECT or t0 == T_IMPLY:
+            elif t0 == T_SELECT:
                 target = tokens.get_next()
 
                 stmt.referenced_syms.add(target)
                 stmt.selected_syms.add(target)
 
-                if tokens.check(T_IF):
-                    new_selects.append((target,
-                                        self._parse_expr(tokens, stmt, line,
-                                                         filename, linenr)))
-                else:
-                    new_selects.append((target, None))
+                new_selects.append(
+                    (target,
+                     self._parse_expr(tokens, stmt, line, filename, linenr)
+                     if tokens.check(T_IF) else None))
+
+            elif t0 == T_IMPLY:
+                target = tokens.get_next()
+
+                stmt.referenced_syms.add(target)
+                stmt.implied_syms.add(target)
+
+                new_implies.append(
+                    (target,
+                     self._parse_expr(tokens, stmt, line, filename, linenr)
+                     if tokens.check(T_IF) else None))
 
             elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
                 stmt.type = TOKEN_TO_TYPE[t0]
@@ -939,12 +954,10 @@ class Config(object):
                 stmt.referenced_syms.add(low)
                 stmt.referenced_syms.add(high)
 
-                if tokens.check(T_IF):
-                    stmt.ranges.append((low, high,
-                                        self._parse_expr(tokens, stmt, line,
-                                                         filename, linenr)))
-                else:
-                    stmt.ranges.append((low, high, None))
+                stmt.ranges.append(
+                    (low, high,
+                     self._parse_expr(tokens, stmt, line, filename, linenr)
+                     if tokens.check(T_IF) else None))
 
             elif t0 == T_DEF_TRISTATE:
                 stmt.type = TRISTATE
@@ -1051,21 +1064,20 @@ class Config(object):
             # Symbol or Choice
 
             # See comment for 'menu_dep'
-            stmt.menu_dep = depends_on_expr
+            stmt.menu_dep = _make_and(deps, depends_on_expr)
 
             # Propagate dependencies to prompts
 
             if new_prompt is not None:
-                # Propagate 'visible if' dependencies from enclosing menus
                 prompt, cond_expr = new_prompt
-                cond_expr = _make_and(cond_expr, visible_if_deps)
-                # Propagate 'depends on' dependencies
-                new_prompt = (prompt, _make_and(cond_expr, depends_on_expr))
+                # Propagate 'visible if' dependencies from menus and local
+                # 'depends on' dependencies
+                cond_expr = _make_and(_make_and(cond_expr, visible_if_deps),
+                                      depends_on_expr)
                 # Save original
-                stmt.orig_prompts.append(new_prompt)
+                stmt.orig_prompts.append((prompt, cond_expr))
                 # Finalize with dependencies from enclosing menus and ifs
-                stmt.prompts.append((new_prompt[0],
-                                     _make_and(new_prompt[1], deps)))
+                stmt.prompts.append((prompt, _make_and(cond_expr, deps)))
 
             # Propagate dependencies to defaults
 
@@ -1078,20 +1090,27 @@ class Config(object):
             stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
                                    for val_expr, cond_expr in new_def_exprs])
 
-            # Propagate dependencies to selects
+            # Propagate dependencies to selects and implies
 
-            # Only symbols can select
+            # Only symbols can select and imply
             if isinstance(stmt, Symbol):
                 # Propagate 'depends on' dependencies
                 new_selects = [(target, _make_and(cond_expr, depends_on_expr))
                                for target, cond_expr in new_selects]
+                new_implies = [(target, _make_and(cond_expr, depends_on_expr))
+                               for target, cond_expr in new_implies]
                 # Save original
                 stmt.orig_selects.extend(new_selects)
+                stmt.orig_implies.extend(new_implies)
                 # Finalize with dependencies from enclosing menus and ifs
                 for target, cond in new_selects:
-                    target.rev_dep = _make_or(target.rev_dep,
-                                              _make_and(stmt,
-                                                        _make_and(cond, deps)))
+                    target.rev_dep = \
+                        _make_or(target.rev_dep,
+                                 _make_and(stmt, _make_and(cond, deps)))
+                for target, cond in new_implies:
+                    target.weak_rev_dep = \
+                        _make_or(target.weak_rev_dep,
+                                 _make_and(stmt, _make_and(cond, deps)))
 
     def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
                     transform_m=True):
@@ -1483,7 +1502,8 @@ class Config(object):
 
         # The directly dependent symbols of a symbol are:
         #  - Any symbols whose prompts, default values, rev_dep (select
-        #    condition), or ranges depend on the symbol
+        #    condition), weak_rev_dep (imply condition) or ranges depend on the
+        #    symbol
         #  - Any symbols that belong to the same choice statement as the symbol
         #    (these won't be included in 'dep' as that makes the dependency
         #    graph unwieldy, but Symbol._get_dependent() will include them)
@@ -1497,6 +1517,7 @@ class Config(object):
                 add_expr_deps(e, sym)
 
             add_expr_deps(sym.rev_dep, sym)
+            add_expr_deps(sym.weak_rev_dep, sym)
 
             for l, u, e in sym.ranges:
                 add_expr_deps(l, sym)
@@ -1625,20 +1646,16 @@ class Config(object):
         else:
             prompts_str_rows = []
             for prompt, cond_expr in sc.orig_prompts:
-                if cond_expr is None:
-                    prompts_str_rows.append(' "{0}"'.format(prompt))
-                else:
-                    prompts_str_rows.append(
-                      ' "{0}" if {1}'.format(prompt,
-                                             self._expr_val_str(cond_expr)))
+                prompts_str_rows.append(
+                    ' "{0}"'.format(prompt) if cond_expr is None else
+                    ' "{0}" if {1}'.format(prompt,
+                                           self._expr_val_str(cond_expr)))
             prompts_str = "\n".join(prompts_str_rows)
 
         # Build locations string
-        if not sc.def_locations:
-            locations_str = "(no locations)"
-        else:
-            locations_str = " ".join(["{0}:{1}".format(filename, linenr) for
-                                      (filename, linenr) in sc.def_locations])
+        locations_str = "(no locations)" if not sc.def_locations else \
+                        " ".join(["{0}:{1}".format(filename, linenr) for
+                                  filename, linenr in sc.def_locations])
 
         # Build additional-dependencies-from-menus-and-ifs string
         additional_deps_str = " " + \
@@ -1657,13 +1674,11 @@ class Config(object):
                 else:
                     ranges_str_rows = []
                     for l, u, cond_expr in sc.ranges:
-                        if cond_expr is None:
-                            ranges_str_rows.append(" [{0}, {1}]".format(s(l),
-                                                                        s(u)))
-                        else:
-                            ranges_str_rows.append(" [{0}, {1}] if {2}"
-                              .format(s(l), s(u),
-                                      self._expr_val_str(cond_expr)))
+                        ranges_str_rows.append(
+                            " [{0}, {1}]".format(s(l), s(u))
+                            if cond_expr is None else
+                            " [{0}, {1}] if {2}"
+                            .format(s(l), s(u), self._expr_val_str(cond_expr)))
                     ranges_str = "\n".join(ranges_str_rows)
 
             # Build default values string
@@ -1685,14 +1700,24 @@ class Config(object):
             else:
                 selects_str_rows = []
                 for target, cond_expr in sc.orig_selects:
-                    if cond_expr is None:
-                        selects_str_rows.append(" {0}".format(target.name))
-                    else:
-                        selects_str_rows.append(
-                          " {0} if {1}".format(target.name,
-                                               self._expr_val_str(cond_expr)))
+                    selects_str_rows.append(
+                        " {0}".format(target.name) if cond_expr is None else
+                        " {0} if {1}".format(target.name,
+                                             self._expr_val_str(cond_expr)))
                 selects_str = "\n".join(selects_str_rows)
 
+            # Build implies string
+            if not sc.orig_implies:
+                implies_str = " (no implies)"
+            else:
+                implies_str_rows = []
+                for target, cond_expr in sc.orig_implies:
+                    implies_str_rows.append(
+                        " {0}".format(target.name) if cond_expr is None else
+                        " {0} if {1}".format(target.name,
+                                             self._expr_val_str(cond_expr)))
+                implies_str = "\n".join(implies_str_rows)
+
             res = _lines("Symbol " +
                            ("(no name)" if sc.name is None else sc.name),
                          "Type           : " + TYPENAME[sc.type],
@@ -1711,9 +1736,16 @@ class Config(object):
                           defaults_str,
                           "Selects:",
                           selects_str,
+                          "Implies:",
+                          implies_str,
                           "Reverse (select-related) dependencies:",
-                          " (no reverse dependencies)" if sc.rev_dep == "n"
-                            else " " + self._expr_val_str(sc.rev_dep),
+                          " (no reverse dependencies)"
+                          if sc.rev_dep == "n"
+                          else " " + self._expr_val_str(sc.rev_dep),
+                          "Weak reverse (imply-related) dependencies:",
+                          " (no weak reverse dependencies)"
+                          if sc.weak_rev_dep == "n"
+                          else " " + self._expr_val_str(sc.weak_rev_dep),
                           "Additional dependencies from enclosing menus "
                             "and ifs:",
                           additional_deps_str,
@@ -1735,11 +1767,10 @@ class Config(object):
         else:
             defaults_str_rows = []
             for sym, cond_expr in sc.orig_def_exprs:
-                if cond_expr is None:
-                    defaults_str_rows.append(" {0}".format(sym.name))
-                else:
-                    defaults_str_rows.append(" {0} if {1}".format(sym.name,
-                                                self._expr_val_str(cond_expr)))
+                defaults_str_rows.append(
+                    " {0}".format(sym.name) if cond_expr is None else
+                    " {0} if {1}".format(sym.name,
+                                         self._expr_val_str(cond_expr)))
             defaults_str = "\n".join(defaults_str_rows)
 
         # Build contained symbols string
@@ -1919,26 +1950,25 @@ class Symbol(Item):
                     self.write_to_conf = (mode != "n")
 
                     if mode == "y":
-                        if choice.get_selection() is self:
-                            new_val = "y"
-                        else:
-                            new_val = "n"
+                        new_val = "y" if choice.get_selection() is self \
+                                  else "n"
                     elif mode == "m":
                         if self.user_val == "m" or self.user_val == "y":
                             new_val = "m"
 
             else:
                 # If the symbol is visible and has a user value, use that.
-                # Otherwise, look at defaults.
-                use_defaults = True
+                # Otherwise, look at defaults and weak reverse dependencies
+                # (implies).
+                use_defaults_and_weak_rev_deps = True
 
                 if vis != "n":
                     self.write_to_conf = True
                     if self.user_val is not None:
                         new_val = self.config._eval_min(self.user_val, vis)
-                        use_defaults = False
+                        use_defaults_and_weak_rev_deps = False
 
-                if use_defaults:
+                if use_defaults_and_weak_rev_deps:
                     for val_expr, cond_expr in self.def_exprs:
                         cond_eval = self.config._eval_expr(cond_expr)
                         if cond_eval != "n":
@@ -1947,14 +1977,25 @@ class Symbol(Item):
                                                             cond_eval)
                             break
 
+                    weak_rev_dep_val = \
+                        self.config._eval_expr(self.weak_rev_dep)
+                    if weak_rev_dep_val != "n":
+                        self.write_to_conf = True
+                        new_val = self.config._eval_max(new_val,
+                                                        weak_rev_dep_val)
+
                 # Reverse (select-related) dependencies take precedence
                 rev_dep_val = self.config._eval_expr(self.rev_dep)
                 if rev_dep_val != "n":
                     self.write_to_conf = True
                     new_val = self.config._eval_max(new_val, rev_dep_val)
 
-            # Promote "m" to "y" for booleans
-            if new_val == "m" and self.type == BOOL:
+            # We need to promote "m" to "y" in two circumstances:
+            #  1) If our type is boolean
+            #  2) If our weak_rev_dep (from IMPLY) is "y"
+            if new_val == "m" and \
+               (self.type == BOOL or
+                self.config._eval_expr(self.weak_rev_dep) == "y"):
                 new_val = "y"
 
         elif self.type == INT or self.type == HEX:
@@ -2189,6 +2230,13 @@ class Symbol(Item):
         get_referenced_symbols()."""
         return self.selected_syms
 
+    def get_implied_symbols(self):
+        """Returns the set() of all symbols X for which this symbol has an
+        'imply X' or 'imply X if Y' (regardless of whether Y is satisfied or
+        not). This is a subset of the symbols returned by
+        get_referenced_symbols()."""
+        return self.implied_syms
+
     def set_user_value(self, v):
         """Sets the user value of the symbol.
 
@@ -2304,16 +2352,18 @@ class Symbol(Item):
         self.ranges = [] # 'range' properties (for int and hex)
         self.help = None # Help text
         self.rev_dep = "n" # Reverse (select-related) dependencies
+        self.weak_rev_dep = "n" # Weak reverse (imply-related) dependencies
         self.config = None
         self.parent = None
 
         self.user_val = None # Value set by user
 
-        # The prompt, default value and select conditions without any
+        # The prompt, default value, select, and imply conditions without any
         # dependencies from menus and ifs propagated to them
         self.orig_prompts = []
         self.orig_def_exprs = []
         self.orig_selects = []
+        self.orig_implies = []
 
         # Dependencies inherited from containing menus and ifs
         self.deps_from_containing = None
@@ -2323,13 +2373,15 @@ class Symbol(Item):
         # The set of symbols selected by this symbol (see
         # get_selected_symbols())
         self.selected_syms = set()
+        # The set of symbols implied by this symbol (see get_implied_symbols())
+        self.implied_syms = set()
         # Like 'referenced_syms', but includes symbols from
         # dependencies inherited from enclosing menus and ifs
         self.all_referenced_syms = set()
 
-        # This records only dependencies specified with 'depends on'. Needed
-        # when determining actual choice items (hrrrr...). See also
-        # Choice._determine_actual_symbols().
+        # This records only dependencies from enclosing ifs and menus together
+        # with local 'depends on' dependencies. Needed when determining actual
+        # choice items (hrrrr...). See Choice._determine_actual_symbols().
         self.menu_dep = None
 
         # See Symbol.get_ref/def_locations().
@@ -2470,18 +2522,17 @@ class Symbol(Item):
             return
 
         if self.type == BOOL or self.type == TRISTATE:
-            if val == "y" or val == "m":
-                append_fn("CONFIG_{0}={1}".format(self.name, val))
-            else:
-                append_fn("# CONFIG_{0} is not set".format(self.name))
+            append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val)
+                      if val == "y" or val == "m" else
+                      "# {0}{1} is not set".format(self.config.config_prefix, self.name))
 
         elif self.type == INT or self.type == HEX:
-            append_fn("CONFIG_{0}={1}".format(self.name, val))
+            append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val))
 
         elif self.type == STRING:
             # Escape \ and "
-            append_fn('CONFIG_{0}="{1}"'
-                      .format(self.name,
+            append_fn('{0}{1}="{2}"'
+                      .format(self.config.config_prefix, self.name,
                               val.replace("\\", "\\\\").replace('"', '\\"')))
 
         else:
@@ -2635,7 +2686,7 @@ class Menu(Item):
         self.title = None
         self.dep_expr = None
         self.visible_if_expr = None
-        self.block = None
+        self.block = [] # List of contained items
         self.config = None
         self.parent = None
 
@@ -2852,7 +2903,7 @@ class Choice(Item):
         self.prompts = []
         self.def_exprs = [] # 'default' properties
         self.help = None # Help text
-        self.block = None # List of contained items
+        self.block = [] # List of contained items
         self.config = None
         self.parent = None
 
@@ -3177,7 +3228,13 @@ def _get_visibility(sc):
             vis = sc.config._eval_max(vis, cond_expr)
 
         if isinstance(sc, Symbol) and sc.is_choice_sym:
-            vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
+            if sc.type == TRISTATE and vis == "m" and \
+               sc.parent.get_mode() == "y":
+                # Choice symbols with visibility "m" are not visible if the
+                # choice has mode "y"
+                vis = "n"
+            else:
+                vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
 
         # Promote "m" to "y" if we're dealing with a non-tristate
         if vis == "m" and sc.type != TRISTATE:
@@ -3434,7 +3491,7 @@ _get_keyword = \
    "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
    "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
    "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
-   "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION,
+   "imply" : T_IMPLY, "range": T_RANGE, "option": T_OPTION,
    "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV,
    "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
    "visible": T_VISIBLE}.get
@@ -3455,10 +3512,6 @@ _initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match
 # trailing whitespace as an optimization.
 _id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
 
-# Regular expressions for parsing .config files
-_set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match
-_unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match
-
 # Regular expression for finding $-references to symbols in strings
 _sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search
 
tools/moveconfig.py +1 -1
index 6f549a5..e311646 100755
@@ -1472,7 +1472,7 @@ def find_kconfig_rules(kconf, config, imply_config):
     """
     sym = kconf.get_symbol(imply_config)
     if sym:
-        for sel in sym.get_selected_symbols():
+        for sel in sym.get_selected_symbols() | sym.get_implied_symbols():
             if sel.get_name() == config:
                 return sym
     return None
Signature · 1 line
2.7.4
#2 lore raw

PATCH v2 1/1 kconfiglib: update with 'imply' support

Quoted 16 lines · Ulf Magnusson

Corresponds to 375506d (File writing nit) from upstream
(https://github.com/ulfalizer/Kconfiglib).

Adds proper 'imply' support and fixes a few minor issues, one of which previously triggered the following weird warning:

  configs/taurus_defconfig: /tmp/tmpisI45S:6: warning: assignment to SPL_LDSCRIPT changes mode of containing choice from "arch/$(ARCH)/cpu/u-boot-spl.lds" to "y"

The change in 8639f69 (genconfig.py: Print defconfig next to warnings) was reapplied.

tools/moveconfig.py previously depended on a hack that merged 'select's with 'imply's. It was modified to look at the union of Symbol.get_selected_symbols() and Symbol.get_implied_symbols(), which should give the same behavior.

Quoted 4 lines

tools/genboardscfg.py was verified to produce identical board.cfg's before and after the change.

Applied to u-boot/master, thanks!

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170929/3ed27f6d/attachment.sig>
#3 lore raw

PATCH v2 1/1 kconfiglib: update with 'imply' support

Quoted 21 lines · Tom Rini
Quoted 19 lines · Ulf Magnusson

Corresponds to 375506d (File writing nit) from upstream
(https://github.com/ulfalizer/Kconfiglib).

Adds proper 'imply' support and fixes a few minor issues, one of which previously triggered the following weird warning:

  configs/taurus_defconfig: /tmp/tmpisI45S:6: warning: assignment to SPL_LDSCRIPT changes mode of containing choice from "arch/$(ARCH)/cpu/u-boot-spl.lds" to "y"

The change in 8639f69 (genconfig.py: Print defconfig next to warnings) was reapplied.

tools/moveconfig.py previously depended on a hack that merged 'select's with 'imply's. It was modified to look at the union of Symbol.get_selected_symbols() and Symbol.get_implied_symbols(), which should give the same behavior.

tools/genboardscfg.py was verified to produce identical board.cfg's before and after the change.

Quoted 6 lines
Quoted 1 line

Applied to u-boot/master, thanks!

--
Tom

For future reference, Kconfiglib is now available on PyPI and can be installed via e.g. 'pip' (see the updated README at
https://github.com/ulfalizer/Kconfiglib).

Maybe the custom patch in 8639f69a (genconfig.py: Print defconfig next
to warnings) could be removed if stderr is temporarily redirected to a
string (https://stackoverflow.com/questions/1218933/can-i-redirect-the-stdout-in-python-into-some-sort-of-string-buffer).
I was thinking of adding an optional list argument to some functions
that would receive the warning strings if provided, but I'm not sure
if it's worth it. Would want them printed directly by default at
least, because otherwise people will miss them.

Cheers,
Ulf