kconfiglib: update with 'imply' support
authorUlf Magnusson <ulfalizer@gmail.com>
Tue, 19 Sep 2017 10:52:55 +0000 (12:52 +0200)
committerTom Rini <trini@konsulko.com>
Fri, 29 Sep 2017 18:07:54 +0000 (14:07 -0400)
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.

Signed-off-by: Ulf Magnusson <ulfalizer@gmail.com>
tools/buildman/kconfiglib.py
tools/moveconfig.py

index 352ad438eed2955aeb6ec5349fc437032f659c5b..68b470a3a7d9adfc5ec48647d6a6e899b63d8b5d 100644 (file)
@@ -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
 
index 6f549a51c16d0c2d81c3effbd8f62f8d255f45a5..e3116461bad0ec577b5081ff332077c5babdefe6 100755 (executable)
@@ -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