xref: /rk3399_rockchip-uboot/tools/buildman/kconfiglib.py (revision 0c69cd527fe67bb2fe6a2afc655fbc932be57de3)
1f219e013SMasahiro Yamada#
29170818aSMasahiro Yamada# SPDX-License-Identifier:	ISC
3f219e013SMasahiro Yamada#
4f219e013SMasahiro Yamada# Author: Ulf Magnusson
5f219e013SMasahiro Yamada#   https://github.com/ulfalizer/Kconfiglib
6f219e013SMasahiro Yamada
7f219e013SMasahiro Yamada# This is Kconfiglib, a Python library for scripting, debugging, and extracting
8f219e013SMasahiro Yamada# information from Kconfig-based configuration systems. To view the
9f219e013SMasahiro Yamada# documentation, run
10f219e013SMasahiro Yamada#
11f219e013SMasahiro Yamada#  $ pydoc kconfiglib
12f219e013SMasahiro Yamada#
13f219e013SMasahiro Yamada# or, if you prefer HTML,
14f219e013SMasahiro Yamada#
15f219e013SMasahiro Yamada#  $ pydoc -w kconfiglib
16f219e013SMasahiro Yamada#
17f219e013SMasahiro Yamada# The examples/ subdirectory contains examples, to be run with e.g.
18f219e013SMasahiro Yamada#
19f219e013SMasahiro Yamada#  $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
20f219e013SMasahiro Yamada#
21f219e013SMasahiro Yamada# Look in testsuite.py for the test suite.
22f219e013SMasahiro Yamada
23f219e013SMasahiro Yamada"""
24f219e013SMasahiro YamadaKconfiglib is a Python library for scripting and extracting information from
25f219e013SMasahiro YamadaKconfig-based configuration systems. Features include the following:
26f219e013SMasahiro Yamada
27f219e013SMasahiro Yamada - Symbol values and properties can be looked up and values assigned
28f219e013SMasahiro Yamada   programmatically.
29f219e013SMasahiro Yamada - .config files can be read and written.
30f219e013SMasahiro Yamada - Expressions can be evaluated in the context of a Kconfig configuration.
31f219e013SMasahiro Yamada - Relations between symbols can be quickly determined, such as finding all
32f219e013SMasahiro Yamada   symbols that reference a particular symbol.
33f219e013SMasahiro Yamada - Highly compatible with the scripts/kconfig/*conf utilities. The test suite
34f219e013SMasahiro Yamada   automatically compares outputs between Kconfiglib and the C implementation
35f219e013SMasahiro Yamada   for a large number of cases.
36f219e013SMasahiro Yamada
37f219e013SMasahiro YamadaFor the Linux kernel, scripts are run using
38f219e013SMasahiro Yamada
3990c36d8aSUlf Magnusson $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>]
40f219e013SMasahiro Yamada
4190c36d8aSUlf MagnussonUsing the 'scriptconfig' target ensures that required environment variables
4290c36d8aSUlf Magnusson(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly.
43f219e013SMasahiro Yamada
4490c36d8aSUlf MagnussonScripts receive the name of the Kconfig file to load in sys.argv[1]. As of
4590c36d8aSUlf MagnussonLinux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory.
4690c36d8aSUlf MagnussonIf an argument is provided with SCRIPT_ARG, it appears as sys.argv[2].
47f219e013SMasahiro Yamada
48f219e013SMasahiro YamadaTo get an interactive Python prompt with Kconfiglib preloaded and a Config
4990c36d8aSUlf Magnussonobject 'c' created, run
50f219e013SMasahiro Yamada
5190c36d8aSUlf Magnusson $ make iscriptconfig [ARCH=<arch>]
52f219e013SMasahiro Yamada
5390c36d8aSUlf MagnussonKconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python
5490c36d8aSUlf Magnussoninterpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy
5590c36d8aSUlf Magnussonworks well too, and might give a nice speedup for long-running jobs.
56f219e013SMasahiro Yamada
5790c36d8aSUlf MagnussonThe examples/ directory contains short example scripts, which can be run with
5890c36d8aSUlf Magnussone.g.
59f219e013SMasahiro Yamada
60f219e013SMasahiro Yamada $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
61f219e013SMasahiro Yamada
62f219e013SMasahiro Yamadaor
63f219e013SMasahiro Yamada
6490c36d8aSUlf Magnusson $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel
65f219e013SMasahiro Yamada
6690c36d8aSUlf Magnussontestsuite.py contains the test suite. See the top of the script for how to run
6790c36d8aSUlf Magnussonit.
68f219e013SMasahiro Yamada
69f219e013SMasahiro YamadaCredits: Written by Ulf "Ulfalizer" Magnusson
70f219e013SMasahiro Yamada
7190c36d8aSUlf MagnussonSend bug reports, suggestions and other feedback to ulfalizer a.t Google's
7290c36d8aSUlf Magnussonemail service. Don't wrestle with internal APIs. Tell me what you need and I
7390c36d8aSUlf Magnussonmight add it in a safe way as a client API instead."""
74f219e013SMasahiro Yamada
75f219e013SMasahiro Yamadaimport os
76f219e013SMasahiro Yamadaimport re
77f219e013SMasahiro Yamadaimport sys
78f219e013SMasahiro Yamada
7990c36d8aSUlf Magnusson# File layout:
8090c36d8aSUlf Magnusson#
8190c36d8aSUlf Magnusson# Public classes
8290c36d8aSUlf Magnusson# Public functions
8390c36d8aSUlf Magnusson# Internal classes
8490c36d8aSUlf Magnusson# Internal functions
8590c36d8aSUlf Magnusson# Internal global constants
8690c36d8aSUlf Magnusson
8790c36d8aSUlf Magnusson# Line length: 79 columns
8890c36d8aSUlf Magnusson
8990c36d8aSUlf Magnusson#
9090c36d8aSUlf Magnusson# Public classes
9190c36d8aSUlf Magnusson#
9290c36d8aSUlf Magnusson
9390c36d8aSUlf Magnussonclass Config(object):
94f219e013SMasahiro Yamada
95f219e013SMasahiro Yamada    """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the
96f219e013SMasahiro Yamada    set of symbols and other items appearing in the configuration together with
97f219e013SMasahiro Yamada    their values. Creating any number of Config objects -- including for
98f219e013SMasahiro Yamada    different architectures -- is safe; Kconfiglib has no global state."""
99f219e013SMasahiro Yamada
100f219e013SMasahiro Yamada    #
101f219e013SMasahiro Yamada    # Public interface
102f219e013SMasahiro Yamada    #
103f219e013SMasahiro Yamada
10490c36d8aSUlf Magnusson    def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True,
105f219e013SMasahiro Yamada                 print_undef_assign=False):
106f219e013SMasahiro Yamada        """Creates a new Config object, representing a Kconfig configuration.
107f219e013SMasahiro Yamada        Raises Kconfig_Syntax_Error on syntax errors.
108f219e013SMasahiro Yamada
10990c36d8aSUlf Magnusson        filename (default: "Kconfig"): The base Kconfig file of the
11090c36d8aSUlf Magnusson           configuration. For the Linux kernel, you'll probably want "Kconfig"
11190c36d8aSUlf Magnusson           from the top-level directory, as environment variables will make
11290c36d8aSUlf Magnusson           sure the right Kconfig is included from there
11390c36d8aSUlf Magnusson           (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make
11490c36d8aSUlf Magnusson           scriptconfig', the filename of the base base Kconfig file will be in
11590c36d8aSUlf Magnusson           sys.argv[1].
116f219e013SMasahiro Yamada
11790c36d8aSUlf Magnusson        base_dir (default: None): The base directory relative to which 'source'
11890c36d8aSUlf Magnusson           statements within Kconfig files will work. For the Linux kernel this
11990c36d8aSUlf Magnusson           should be the top-level directory of the kernel tree. $-references
12090c36d8aSUlf Magnusson           to existing environment variables will be expanded.
121f219e013SMasahiro Yamada
12290c36d8aSUlf Magnusson           If None (the default), the environment variable 'srctree' will be
12390c36d8aSUlf Magnusson           used if set, and the current directory otherwise. 'srctree' is set
12490c36d8aSUlf Magnusson           by the Linux makefiles to the top-level kernel directory. A default
12590c36d8aSUlf Magnusson           of "." would not work with an alternative build directory.
126f219e013SMasahiro Yamada
12790c36d8aSUlf Magnusson        print_warnings (default: True): Set to True if warnings related to this
12890c36d8aSUlf Magnusson           configuration should be printed to stderr. This can be changed later
12990c36d8aSUlf Magnusson           with Config.set_print_warnings(). It is provided as a constructor
13090c36d8aSUlf Magnusson           argument since warnings might be generated during parsing.
131f219e013SMasahiro Yamada
13290c36d8aSUlf Magnusson        print_undef_assign (default: False): Set to True if informational
13390c36d8aSUlf Magnusson           messages related to assignments to undefined symbols should be
13490c36d8aSUlf Magnusson           printed to stderr for this configuration. Can be changed later with
135f219e013SMasahiro Yamada           Config.set_print_undef_assign()."""
136f219e013SMasahiro Yamada
137f219e013SMasahiro Yamada        # The set of all symbols, indexed by name (a string)
138f219e013SMasahiro Yamada        self.syms = {}
13990c36d8aSUlf Magnusson        # Python 2/3 compatibility hack. This is the only one needed.
14090c36d8aSUlf Magnusson        if sys.version_info[0] >= 3:
14190c36d8aSUlf Magnusson            self.syms_iter = self.syms.values
14290c36d8aSUlf Magnusson        else:
14390c36d8aSUlf Magnusson            self.syms_iter = self.syms.itervalues
144f219e013SMasahiro Yamada
145f219e013SMasahiro Yamada        # The set of all defined symbols in the configuration in the order they
146f219e013SMasahiro Yamada        # appear in the Kconfig files. This excludes the special symbols n, m,
147f219e013SMasahiro Yamada        # and y as well as symbols that are referenced but never defined.
148f219e013SMasahiro Yamada        self.kconfig_syms = []
149f219e013SMasahiro Yamada
150f219e013SMasahiro Yamada        # The set of all named choices (yes, choices can have names), indexed
151f219e013SMasahiro Yamada        # by name (a string)
152f219e013SMasahiro Yamada        self.named_choices = {}
153f219e013SMasahiro Yamada
15490c36d8aSUlf Magnusson        # Lists containing all choices, menus and comments in the configuration
15590c36d8aSUlf Magnusson        self.choices = []
15690c36d8aSUlf Magnusson        self.menus = []
15790c36d8aSUlf Magnusson        self.comments = []
15890c36d8aSUlf Magnusson
15990c36d8aSUlf Magnusson        def register_special_symbol(type_, name, val):
160f219e013SMasahiro Yamada            sym = Symbol()
161f219e013SMasahiro Yamada            sym.is_special_ = True
162f219e013SMasahiro Yamada            sym.is_defined_ = True
163f219e013SMasahiro Yamada            sym.config = self
164f219e013SMasahiro Yamada            sym.name = name
16590c36d8aSUlf Magnusson            sym.type = type_
16690c36d8aSUlf Magnusson            sym.cached_val = val
167f219e013SMasahiro Yamada            self.syms[name] = sym
168f219e013SMasahiro Yamada            return sym
169f219e013SMasahiro Yamada
170f219e013SMasahiro Yamada        # The special symbols n, m and y, used as shorthand for "n", "m" and
171f219e013SMasahiro Yamada        # "y"
172f219e013SMasahiro Yamada        self.n = register_special_symbol(TRISTATE, "n", "n")
173f219e013SMasahiro Yamada        self.m = register_special_symbol(TRISTATE, "m", "m")
174f219e013SMasahiro Yamada        self.y = register_special_symbol(TRISTATE, "y", "y")
175f219e013SMasahiro Yamada        # DEFCONFIG_LIST uses this
176f219e013SMasahiro Yamada        register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2])
177f219e013SMasahiro Yamada
178f219e013SMasahiro Yamada        # The symbol with "option defconfig_list" set, containing a list of
179f219e013SMasahiro Yamada        # default .config files
180f219e013SMasahiro Yamada        self.defconfig_sym = None
181f219e013SMasahiro Yamada
182f219e013SMasahiro Yamada        # See Symbol.get_(src)arch()
183f219e013SMasahiro Yamada        self.arch = os.environ.get("ARCH")
184f219e013SMasahiro Yamada        self.srcarch = os.environ.get("SRCARCH")
185f219e013SMasahiro Yamada
186f219e013SMasahiro Yamada        # See Config.__init__(). We need this for get_defconfig_filename().
187f219e013SMasahiro Yamada        self.srctree = os.environ.get("srctree")
188f219e013SMasahiro Yamada        if self.srctree is None:
189f219e013SMasahiro Yamada            self.srctree = "."
190f219e013SMasahiro Yamada
191f219e013SMasahiro Yamada        self.filename = filename
19290c36d8aSUlf Magnusson        if base_dir is None:
19390c36d8aSUlf Magnusson            self.base_dir = self.srctree
19490c36d8aSUlf Magnusson        else:
19590c36d8aSUlf Magnusson            self.base_dir = os.path.expandvars(base_dir)
196f219e013SMasahiro Yamada
197f219e013SMasahiro Yamada        # The 'mainmenu' text
198f219e013SMasahiro Yamada        self.mainmenu_text = None
199f219e013SMasahiro Yamada
200f219e013SMasahiro Yamada        # The filename of the most recently loaded .config file
201f219e013SMasahiro Yamada        self.config_filename = None
202f219e013SMasahiro Yamada        # The textual header of the most recently loaded .config, uncommented
203f219e013SMasahiro Yamada        self.config_header = None
204f219e013SMasahiro Yamada
205f219e013SMasahiro Yamada        self.print_warnings = print_warnings
206f219e013SMasahiro Yamada        self.print_undef_assign = print_undef_assign
207*0c69cd52SSimon Glass        self._warnings = []
208f219e013SMasahiro Yamada
209f219e013SMasahiro Yamada        # For parsing routines that stop when finding a line belonging to a
210f219e013SMasahiro Yamada        # different construct, these holds that line and the tokenized version
211f219e013SMasahiro Yamada        # of that line. The purpose is to avoid having to re-tokenize the line,
212f219e013SMasahiro Yamada        # which is inefficient and causes problems when recording references to
213f219e013SMasahiro Yamada        # symbols.
214f219e013SMasahiro Yamada        self.end_line = None
215f219e013SMasahiro Yamada        self.end_line_tokens = None
216f219e013SMasahiro Yamada
217f219e013SMasahiro Yamada        # See the comment in _parse_expr().
21890c36d8aSUlf Magnusson        self._cur_item = None
21990c36d8aSUlf Magnusson        self._line = None
22090c36d8aSUlf Magnusson        self._filename = None
22190c36d8aSUlf Magnusson        self._linenr = None
22290c36d8aSUlf Magnusson        self._transform_m = None
223f219e013SMasahiro Yamada
224f219e013SMasahiro Yamada        # Parse the Kconfig files
225f219e013SMasahiro Yamada        self.top_block = self._parse_file(filename, None, None, None)
226f219e013SMasahiro Yamada
227f219e013SMasahiro Yamada        # Build Symbol.dep for all symbols
228f219e013SMasahiro Yamada        self._build_dep()
229f219e013SMasahiro Yamada
230f219e013SMasahiro Yamada    def get_arch(self):
231f219e013SMasahiro Yamada        """Returns the value the environment variable ARCH had at the time the
232f219e013SMasahiro Yamada        Config instance was created, or None if ARCH was not set. For the
233f219e013SMasahiro Yamada        kernel, this corresponds to the architecture being built for, with
234f219e013SMasahiro Yamada        values such as "i386" or "mips"."""
235f219e013SMasahiro Yamada        return self.arch
236f219e013SMasahiro Yamada
237f219e013SMasahiro Yamada    def get_srcarch(self):
238f219e013SMasahiro Yamada        """Returns the value the environment variable SRCARCH had at the time
239f219e013SMasahiro Yamada        the Config instance was created, or None if SRCARCH was not set. For
24090c36d8aSUlf Magnusson        the kernel, this corresponds to the particular arch/ subdirectory
24190c36d8aSUlf Magnusson        containing architecture-specific code."""
242f219e013SMasahiro Yamada        return self.srcarch
243f219e013SMasahiro Yamada
244f219e013SMasahiro Yamada    def get_srctree(self):
245f219e013SMasahiro Yamada        """Returns the value the environment variable srctree had at the time
246f219e013SMasahiro Yamada        the Config instance was created, or None if srctree was not defined.
247f219e013SMasahiro Yamada        This variable points to the source directory and is used when building
248f219e013SMasahiro Yamada        in a separate directory."""
249f219e013SMasahiro Yamada        return self.srctree
250f219e013SMasahiro Yamada
25190c36d8aSUlf Magnusson    def get_base_dir(self):
25290c36d8aSUlf Magnusson        """Returns the base directory relative to which 'source' statements
25390c36d8aSUlf Magnusson        will work, passed as an argument to Config.__init__()."""
25490c36d8aSUlf Magnusson        return self.base_dir
25590c36d8aSUlf Magnusson
25690c36d8aSUlf Magnusson    def get_kconfig_filename(self):
25790c36d8aSUlf Magnusson        """Returns the name of the (base) kconfig file this configuration was
25890c36d8aSUlf Magnusson        loaded from."""
25990c36d8aSUlf Magnusson        return self.filename
26090c36d8aSUlf Magnusson
261f219e013SMasahiro Yamada    def get_config_filename(self):
26290c36d8aSUlf Magnusson        """Returns the filename of the most recently loaded configuration file,
26390c36d8aSUlf Magnusson        or None if no configuration has been loaded."""
264f219e013SMasahiro Yamada        return self.config_filename
265f219e013SMasahiro Yamada
26690c36d8aSUlf Magnusson    def get_config_header(self):
26790c36d8aSUlf Magnusson        """Returns the (uncommented) textual header of the .config file most
26890c36d8aSUlf Magnusson        recently loaded with load_config(). Returns None if no .config file has
26990c36d8aSUlf Magnusson        been loaded or if the most recently loaded .config file has no header.
27090c36d8aSUlf Magnusson        The header consists of all lines up to but not including the first line
27190c36d8aSUlf Magnusson        that either
27290c36d8aSUlf Magnusson
27390c36d8aSUlf Magnusson        1. Does not start with "#"
27490c36d8aSUlf Magnusson        2. Has the form "# CONFIG_FOO is not set."
27590c36d8aSUlf Magnusson        """
27690c36d8aSUlf Magnusson        return self.config_header
27790c36d8aSUlf Magnusson
278f219e013SMasahiro Yamada    def get_mainmenu_text(self):
279f219e013SMasahiro Yamada        """Returns the text of the 'mainmenu' statement (with $-references to
280f219e013SMasahiro Yamada        symbols replaced by symbol values), or None if the configuration has no
281f219e013SMasahiro Yamada        'mainmenu' statement."""
282f219e013SMasahiro Yamada        return None if self.mainmenu_text is None else \
283f219e013SMasahiro Yamada          self._expand_sym_refs(self.mainmenu_text)
284f219e013SMasahiro Yamada
285f219e013SMasahiro Yamada    def get_defconfig_filename(self):
286f219e013SMasahiro Yamada        """Returns the name of the defconfig file, which is the first existing
287f219e013SMasahiro Yamada        file in the list given in a symbol having 'option defconfig_list' set.
288f219e013SMasahiro Yamada        $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if
289f219e013SMasahiro Yamada        FOO has the value "foo"). Returns None in case of no defconfig file.
290f219e013SMasahiro Yamada        Setting 'option defconfig_list' on multiple symbols currently results
291f219e013SMasahiro Yamada        in undefined behavior.
292f219e013SMasahiro Yamada
293f219e013SMasahiro Yamada        If the environment variable 'srctree' was set when the Config was
294f219e013SMasahiro Yamada        created, get_defconfig_filename() will first look relative to that
295f219e013SMasahiro Yamada        directory before looking in the current directory; see
2969d01b787SMasahiro Yamada        Config.__init__().
2979d01b787SMasahiro Yamada
29890c36d8aSUlf Magnusson        WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses
29990c36d8aSUlf Magnusson        the --defconfig=<defconfig> option when calling the C implementation of
30090c36d8aSUlf Magnusson        e.g. 'make defconfig'. This option overrides the 'option
30190c36d8aSUlf Magnusson        defconfig_list' symbol, meaning the result from
30290c36d8aSUlf Magnusson        get_defconfig_filename() might not match what 'make defconfig' would
30390c36d8aSUlf Magnusson        use. That probably ought to be worked around somehow, so that this
30490c36d8aSUlf Magnusson        function always gives the "expected" result."""
305f219e013SMasahiro Yamada        if self.defconfig_sym is None:
306f219e013SMasahiro Yamada            return None
30790c36d8aSUlf Magnusson        for filename, cond_expr in self.defconfig_sym.def_exprs:
308f219e013SMasahiro Yamada            if self._eval_expr(cond_expr) == "y":
309f219e013SMasahiro Yamada                filename = self._expand_sym_refs(filename)
310f219e013SMasahiro Yamada                # We first look in $srctree. os.path.join() won't work here as
311f219e013SMasahiro Yamada                # an absolute path in filename would override $srctree.
31290c36d8aSUlf Magnusson                srctree_filename = os.path.normpath(self.srctree + "/" +
31390c36d8aSUlf Magnusson                                                    filename)
314f219e013SMasahiro Yamada                if os.path.exists(srctree_filename):
315f219e013SMasahiro Yamada                    return srctree_filename
316f219e013SMasahiro Yamada                if os.path.exists(filename):
317f219e013SMasahiro Yamada                    return filename
318f219e013SMasahiro Yamada        return None
319f219e013SMasahiro Yamada
320f219e013SMasahiro Yamada    def get_symbol(self, name):
321f219e013SMasahiro Yamada        """Returns the symbol with name 'name', or None if no such symbol
322f219e013SMasahiro Yamada        appears in the configuration. An alternative shorthand is conf[name],
323f219e013SMasahiro Yamada        where conf is a Config instance, though that will instead raise
324f219e013SMasahiro Yamada        KeyError if the symbol does not exist."""
325f219e013SMasahiro Yamada        return self.syms.get(name)
326f219e013SMasahiro Yamada
32790c36d8aSUlf Magnusson    def __getitem__(self, name):
32890c36d8aSUlf Magnusson        """Returns the symbol with name 'name'. Raises KeyError if the symbol
32990c36d8aSUlf Magnusson        does not appear in the configuration."""
33090c36d8aSUlf Magnusson        return self.syms[name]
331f219e013SMasahiro Yamada
332f219e013SMasahiro Yamada    def get_symbols(self, all_symbols=True):
333f219e013SMasahiro Yamada        """Returns a list of symbols from the configuration. An alternative for
334f219e013SMasahiro Yamada        iterating over all defined symbols (in the order of definition) is
335f219e013SMasahiro Yamada
336f219e013SMasahiro Yamada        for sym in config:
337f219e013SMasahiro Yamada            ...
338f219e013SMasahiro Yamada
339f219e013SMasahiro Yamada        which relies on Config implementing __iter__() and is equivalent to
340f219e013SMasahiro Yamada
341f219e013SMasahiro Yamada        for sym in config.get_symbols(False):
342f219e013SMasahiro Yamada            ...
343f219e013SMasahiro Yamada
34490c36d8aSUlf Magnusson        all_symbols (default: True): If True, all symbols -- including special
34590c36d8aSUlf Magnusson           and undefined symbols -- will be included in the result, in an
34690c36d8aSUlf Magnusson           undefined order. If False, only symbols actually defined and not
34790c36d8aSUlf Magnusson           merely referred to in the configuration will be included in the
34890c36d8aSUlf Magnusson           result, and will appear in the order that they are defined within
34990c36d8aSUlf Magnusson           the Kconfig configuration files."""
35090c36d8aSUlf Magnusson        return list(self.syms.values()) if all_symbols else self.kconfig_syms
35190c36d8aSUlf Magnusson
35290c36d8aSUlf Magnusson    def __iter__(self):
35390c36d8aSUlf Magnusson        """Convenience function for iterating over the set of all defined
35490c36d8aSUlf Magnusson        symbols in the configuration, used like
35590c36d8aSUlf Magnusson
35690c36d8aSUlf Magnusson        for sym in conf:
35790c36d8aSUlf Magnusson            ...
35890c36d8aSUlf Magnusson
35990c36d8aSUlf Magnusson        The iteration happens in the order of definition within the Kconfig
36090c36d8aSUlf Magnusson        configuration files. Symbols only referred to but not defined will not
36190c36d8aSUlf Magnusson        be included, nor will the special symbols n, m, and y. If you want to
36290c36d8aSUlf Magnusson        include such symbols as well, see config.get_symbols()."""
36390c36d8aSUlf Magnusson        return iter(self.kconfig_syms)
364f219e013SMasahiro Yamada
365f219e013SMasahiro Yamada    def get_choices(self):
366f219e013SMasahiro Yamada        """Returns a list containing all choice statements in the
367f219e013SMasahiro Yamada        configuration, in the order they appear in the Kconfig files."""
368f219e013SMasahiro Yamada        return self.choices
369f219e013SMasahiro Yamada
370f219e013SMasahiro Yamada    def get_menus(self):
371f219e013SMasahiro Yamada        """Returns a list containing all menus in the configuration, in the
372f219e013SMasahiro Yamada        order they appear in the Kconfig files."""
373f219e013SMasahiro Yamada        return self.menus
374f219e013SMasahiro Yamada
375f219e013SMasahiro Yamada    def get_comments(self):
376f219e013SMasahiro Yamada        """Returns a list containing all comments in the configuration, in the
377f219e013SMasahiro Yamada        order they appear in the Kconfig files."""
378f219e013SMasahiro Yamada        return self.comments
379f219e013SMasahiro Yamada
38090c36d8aSUlf Magnusson    def get_top_level_items(self):
38190c36d8aSUlf Magnusson        """Returns a list containing the items (symbols, menus, choices, and
38290c36d8aSUlf Magnusson        comments) at the top level of the configuration -- that is, all items
38390c36d8aSUlf Magnusson        that do not appear within a menu or choice. The items appear in the
38490c36d8aSUlf Magnusson        same order as within the configuration."""
38590c36d8aSUlf Magnusson        return self.top_block
38690c36d8aSUlf Magnusson
38790c36d8aSUlf Magnusson    def load_config(self, filename, replace=True):
38890c36d8aSUlf Magnusson        """Loads symbol values from a file in the familiar .config format.
38990c36d8aSUlf Magnusson        Equivalent to calling Symbol.set_user_value() to set each of the
39090c36d8aSUlf Magnusson        values.
39190c36d8aSUlf Magnusson
39290c36d8aSUlf Magnusson        "# CONFIG_FOO is not set" within a .config file is treated specially
39390c36d8aSUlf Magnusson        and sets the user value of FOO to 'n'. The C implementation works the
39490c36d8aSUlf Magnusson        same way.
39590c36d8aSUlf Magnusson
39690c36d8aSUlf Magnusson        filename: The .config file to load. $-references to existing
39790c36d8aSUlf Magnusson          environment variables will be expanded. For scripts to work even when
39890c36d8aSUlf Magnusson          an alternative build directory is used with the Linux kernel, you
39990c36d8aSUlf Magnusson          need to refer to the top-level kernel directory with "$srctree".
40090c36d8aSUlf Magnusson
40190c36d8aSUlf Magnusson        replace (default: True): True if the configuration should replace the
402*0c69cd52SSimon Glass           old configuration; False if it should add to it.
40390c36d8aSUlf Magnusson
404*0c69cd52SSimon Glass        Returns a list or warnings (hopefully empty)
405*0c69cd52SSimon Glass        """
406*0c69cd52SSimon Glass
407*0c69cd52SSimon Glass        self._warnings = []
40890c36d8aSUlf Magnusson        # Put this first so that a missing file doesn't screw up our state
40990c36d8aSUlf Magnusson        filename = os.path.expandvars(filename)
41090c36d8aSUlf Magnusson        line_feeder = _FileFeed(filename)
41190c36d8aSUlf Magnusson
41290c36d8aSUlf Magnusson        self.config_filename = filename
41390c36d8aSUlf Magnusson
41490c36d8aSUlf Magnusson        #
41590c36d8aSUlf Magnusson        # Read header
41690c36d8aSUlf Magnusson        #
41790c36d8aSUlf Magnusson
41890c36d8aSUlf Magnusson        def is_header_line(line):
41990c36d8aSUlf Magnusson            return line is not None and line.startswith("#") and \
42090c36d8aSUlf Magnusson                   not _unset_re_match(line)
42190c36d8aSUlf Magnusson
42290c36d8aSUlf Magnusson        self.config_header = None
42390c36d8aSUlf Magnusson
42490c36d8aSUlf Magnusson        line = line_feeder.peek_next()
42590c36d8aSUlf Magnusson        if is_header_line(line):
42690c36d8aSUlf Magnusson            self.config_header = ""
42790c36d8aSUlf Magnusson            while is_header_line(line_feeder.peek_next()):
42890c36d8aSUlf Magnusson                self.config_header += line_feeder.get_next()[1:]
42990c36d8aSUlf Magnusson            # Remove trailing newline
43090c36d8aSUlf Magnusson            if self.config_header.endswith("\n"):
43190c36d8aSUlf Magnusson                self.config_header = self.config_header[:-1]
43290c36d8aSUlf Magnusson
43390c36d8aSUlf Magnusson        #
43490c36d8aSUlf Magnusson        # Read assignments. Hotspot for some workloads.
43590c36d8aSUlf Magnusson        #
43690c36d8aSUlf Magnusson
43790c36d8aSUlf Magnusson        def warn_override(filename, linenr, name, old_user_val, new_user_val):
43890c36d8aSUlf Magnusson            self._warn('overriding the value of {0}. '
43990c36d8aSUlf Magnusson                       'Old value: "{1}", new value: "{2}".'
44090c36d8aSUlf Magnusson                       .format(name, old_user_val, new_user_val),
44190c36d8aSUlf Magnusson                       filename, linenr)
44290c36d8aSUlf Magnusson
44390c36d8aSUlf Magnusson        # Invalidate everything to keep things simple. It might be possible to
44490c36d8aSUlf Magnusson        # improve performance for the case where multiple configurations are
44590c36d8aSUlf Magnusson        # loaded by only invalidating a symbol (and its dependent symbols) if
44690c36d8aSUlf Magnusson        # the new user value differs from the old. One complication would be
44790c36d8aSUlf Magnusson        # that symbols not mentioned in the .config must lose their user value
44890c36d8aSUlf Magnusson        # when replace = True, which is the usual case.
44990c36d8aSUlf Magnusson        if replace:
45090c36d8aSUlf Magnusson            self.unset_user_values()
45190c36d8aSUlf Magnusson        else:
45290c36d8aSUlf Magnusson            self._invalidate_all()
45390c36d8aSUlf Magnusson
45490c36d8aSUlf Magnusson        while 1:
45590c36d8aSUlf Magnusson            line = line_feeder.get_next()
45690c36d8aSUlf Magnusson            if line is None:
457*0c69cd52SSimon Glass                return self._warnings
45890c36d8aSUlf Magnusson
45990c36d8aSUlf Magnusson            line = line.rstrip()
46090c36d8aSUlf Magnusson
46190c36d8aSUlf Magnusson            set_match = _set_re_match(line)
46290c36d8aSUlf Magnusson            if set_match:
46390c36d8aSUlf Magnusson                name, val = set_match.groups()
46490c36d8aSUlf Magnusson
46590c36d8aSUlf Magnusson                if val.startswith('"'):
46690c36d8aSUlf Magnusson                    if len(val) < 2 or val[-1] != '"':
46790c36d8aSUlf Magnusson                        _parse_error(line, "malformed string literal",
46890c36d8aSUlf Magnusson                                     line_feeder.filename, line_feeder.linenr)
46990c36d8aSUlf Magnusson                    # Strip quotes and remove escapings. The unescaping
47090c36d8aSUlf Magnusson                    # procedure should be safe since " can only appear as \"
47190c36d8aSUlf Magnusson                    # inside the string.
47290c36d8aSUlf Magnusson                    val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\")
47390c36d8aSUlf Magnusson
47490c36d8aSUlf Magnusson                if name in self.syms:
47590c36d8aSUlf Magnusson                    sym = self.syms[name]
47690c36d8aSUlf Magnusson                    if sym.user_val is not None:
47790c36d8aSUlf Magnusson                        warn_override(line_feeder.filename, line_feeder.linenr,
47890c36d8aSUlf Magnusson                                      name, sym.user_val, val)
47990c36d8aSUlf Magnusson
48090c36d8aSUlf Magnusson                    if sym.is_choice_sym:
48190c36d8aSUlf Magnusson                        user_mode = sym.parent.user_mode
48290c36d8aSUlf Magnusson                        if user_mode is not None and user_mode != val:
48390c36d8aSUlf Magnusson                            self._warn("assignment to {0} changes mode of "
48490c36d8aSUlf Magnusson                                       'containing choice from "{1}" to "{2}".'
48590c36d8aSUlf Magnusson                                       .format(name, val, user_mode),
48690c36d8aSUlf Magnusson                                       line_feeder.filename,
48790c36d8aSUlf Magnusson                                       line_feeder.linenr)
48890c36d8aSUlf Magnusson
48990c36d8aSUlf Magnusson                    sym._set_user_value_no_invalidate(val, True)
49090c36d8aSUlf Magnusson                else:
49190c36d8aSUlf Magnusson                    if self.print_undef_assign:
49290c36d8aSUlf Magnusson                        _stderr_msg('note: attempt to assign the value "{0}" '
49390c36d8aSUlf Magnusson                                    "to the undefined symbol {1}."
49490c36d8aSUlf Magnusson                                    .format(val, name),
49590c36d8aSUlf Magnusson                                    line_feeder.filename, line_feeder.linenr)
49690c36d8aSUlf Magnusson            else:
49790c36d8aSUlf Magnusson                unset_match = _unset_re_match(line)
49890c36d8aSUlf Magnusson                if unset_match:
49990c36d8aSUlf Magnusson                    name = unset_match.group(1)
50090c36d8aSUlf Magnusson                    if name in self.syms:
50190c36d8aSUlf Magnusson                        sym = self.syms[name]
50290c36d8aSUlf Magnusson                        if sym.user_val is not None:
50390c36d8aSUlf Magnusson                            warn_override(line_feeder.filename,
50490c36d8aSUlf Magnusson                                          line_feeder.linenr,
50590c36d8aSUlf Magnusson                                          name, sym.user_val, "n")
50690c36d8aSUlf Magnusson
50790c36d8aSUlf Magnusson                        sym._set_user_value_no_invalidate("n", True)
50890c36d8aSUlf Magnusson
50990c36d8aSUlf Magnusson    def write_config(self, filename, header=None):
51090c36d8aSUlf Magnusson        """Writes out symbol values in the familiar .config format.
51190c36d8aSUlf Magnusson
51290c36d8aSUlf Magnusson        Kconfiglib makes sure the format matches what the C implementation
51390c36d8aSUlf Magnusson        would generate, down to whitespace. This eases testing.
51490c36d8aSUlf Magnusson
51590c36d8aSUlf Magnusson        filename: The filename under which to save the configuration.
51690c36d8aSUlf Magnusson
51790c36d8aSUlf Magnusson        header (default: None): A textual header that will appear at the
51890c36d8aSUlf Magnusson           beginning of the file, with each line commented out automatically.
51990c36d8aSUlf Magnusson           None means no header."""
52090c36d8aSUlf Magnusson
52190c36d8aSUlf Magnusson        for sym in self.syms_iter():
52290c36d8aSUlf Magnusson            sym.already_written = False
52390c36d8aSUlf Magnusson
52490c36d8aSUlf Magnusson        with open(filename, "w") as f:
52590c36d8aSUlf Magnusson            # Write header
52690c36d8aSUlf Magnusson            if header is not None:
52790c36d8aSUlf Magnusson                f.write(_comment(header))
52890c36d8aSUlf Magnusson                f.write("\n")
52990c36d8aSUlf Magnusson
53090c36d8aSUlf Magnusson            # Build and write configuration
53190c36d8aSUlf Magnusson            conf_strings = []
53290c36d8aSUlf Magnusson            _make_block_conf(self.top_block, conf_strings.append)
53390c36d8aSUlf Magnusson            f.write("\n".join(conf_strings))
53490c36d8aSUlf Magnusson            f.write("\n")
53590c36d8aSUlf Magnusson
536f219e013SMasahiro Yamada    def eval(self, s):
537f219e013SMasahiro Yamada        """Returns the value of the expression 's' -- where 's' is represented
538f219e013SMasahiro Yamada        as a string -- in the context of the configuration. Raises
539f219e013SMasahiro Yamada        Kconfig_Syntax_Error if syntax errors are detected in 's'.
540f219e013SMasahiro Yamada
541f219e013SMasahiro Yamada        For example, if FOO and BAR are tristate symbols at least one of which
542f219e013SMasahiro Yamada        has the value "y", then config.eval("y && (FOO || BAR)") => "y"
543f219e013SMasahiro Yamada
5449d01b787SMasahiro Yamada        This function always yields a tristate value. To get the value of
545f219e013SMasahiro Yamada        non-bool, non-tristate symbols, use Symbol.get_value().
546f219e013SMasahiro Yamada
547f219e013SMasahiro Yamada        The result of this function is consistent with how evaluation works for
548f219e013SMasahiro Yamada        conditional expressions in the configuration as well as in the C
549f219e013SMasahiro Yamada        implementation. "m" and m are rewritten as '"m" && MODULES' and 'm &&
550f219e013SMasahiro Yamada        MODULES', respectively, and a result of "m" will get promoted to "y" if
55190c36d8aSUlf Magnusson        we're running without modules.
55290c36d8aSUlf Magnusson
55390c36d8aSUlf Magnusson        Syntax checking is somewhat lax, partly to be compatible with lax
55490c36d8aSUlf Magnusson        parsing in the C implementation."""
555f219e013SMasahiro Yamada        return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed
55690c36d8aSUlf Magnusson                                                None, # Current symbol/choice
557f219e013SMasahiro Yamada                                                s))   # line
558f219e013SMasahiro Yamada
55990c36d8aSUlf Magnusson    def unset_user_values(self):
56090c36d8aSUlf Magnusson        """Resets the values of all symbols, as if Config.load_config() or
56190c36d8aSUlf Magnusson        Symbol.set_user_value() had never been called."""
56290c36d8aSUlf Magnusson        for sym in self.syms_iter():
56390c36d8aSUlf Magnusson            sym._unset_user_value_no_recursive_invalidate()
564f219e013SMasahiro Yamada
565f219e013SMasahiro Yamada    def set_print_warnings(self, print_warnings):
566f219e013SMasahiro Yamada        """Determines whether warnings related to this configuration (for
567f219e013SMasahiro Yamada        things like attempting to assign illegal values to symbols with
568f219e013SMasahiro Yamada        Symbol.set_user_value()) should be printed to stderr.
569f219e013SMasahiro Yamada
57090c36d8aSUlf Magnusson        print_warnings: True if warnings should be printed."""
571f219e013SMasahiro Yamada        self.print_warnings = print_warnings
572f219e013SMasahiro Yamada
573f219e013SMasahiro Yamada    def set_print_undef_assign(self, print_undef_assign):
574f219e013SMasahiro Yamada        """Determines whether informational messages related to assignments to
575f219e013SMasahiro Yamada        undefined symbols should be printed to stderr for this configuration.
576f219e013SMasahiro Yamada
57790c36d8aSUlf Magnusson        print_undef_assign: If True, such messages will be printed."""
578f219e013SMasahiro Yamada        self.print_undef_assign = print_undef_assign
579f219e013SMasahiro Yamada
580f219e013SMasahiro Yamada    def __str__(self):
581f219e013SMasahiro Yamada        """Returns a string containing various information about the Config."""
58290c36d8aSUlf Magnusson        return _lines("Configuration",
58390c36d8aSUlf Magnusson                      "File                                   : " +
58490c36d8aSUlf Magnusson                        self.filename,
58590c36d8aSUlf Magnusson                      "Base directory                         : " +
58690c36d8aSUlf Magnusson                        self.base_dir,
587f219e013SMasahiro Yamada                      "Value of $ARCH at creation time        : " +
588f219e013SMasahiro Yamada                        ("(not set)" if self.arch is None else self.arch),
589f219e013SMasahiro Yamada                      "Value of $SRCARCH at creation time     : " +
59090c36d8aSUlf Magnusson                        ("(not set)" if self.srcarch is None else
59190c36d8aSUlf Magnusson                                        self.srcarch),
592f219e013SMasahiro Yamada                      "Source tree (derived from $srctree;",
59390c36d8aSUlf Magnusson                      "defaults to '.' if $srctree isn't set) : " +
59490c36d8aSUlf Magnusson                        self.srctree,
595f219e013SMasahiro Yamada                      "Most recently loaded .config           : " +
59690c36d8aSUlf Magnusson                        ("(no .config loaded)"
59790c36d8aSUlf Magnusson                          if self.config_filename is None else
598f219e013SMasahiro Yamada                             self.config_filename),
599f219e013SMasahiro Yamada                      "Print warnings                         : " +
60090c36d8aSUlf Magnusson                        BOOL_STR[self.print_warnings],
601f219e013SMasahiro Yamada                      "Print assignments to undefined symbols : " +
60290c36d8aSUlf Magnusson                        BOOL_STR[self.print_undef_assign])
603f219e013SMasahiro Yamada
604f219e013SMasahiro Yamada    #
605f219e013SMasahiro Yamada    # Private methods
606f219e013SMasahiro Yamada    #
607f219e013SMasahiro Yamada
608f219e013SMasahiro Yamada    #
60990c36d8aSUlf Magnusson    # Kconfig parsing
610f219e013SMasahiro Yamada    #
611f219e013SMasahiro Yamada
612f219e013SMasahiro Yamada    def _parse_file(self, filename, parent, deps, visible_if_deps, res=None):
61390c36d8aSUlf Magnusson        """Parses the Kconfig file 'filename'. Returns a list with the Items in
61490c36d8aSUlf Magnusson        the file. See _parse_block() for the meaning of the parameters."""
61590c36d8aSUlf Magnusson        return self._parse_block(_FileFeed(filename), None, parent, deps,
61690c36d8aSUlf Magnusson                                 visible_if_deps, res)
617f219e013SMasahiro Yamada
618f219e013SMasahiro Yamada    def _parse_block(self, line_feeder, end_marker, parent, deps,
61990c36d8aSUlf Magnusson                     visible_if_deps, res=None):
620f219e013SMasahiro Yamada        """Parses a block, which is the contents of either a file or an if,
62190c36d8aSUlf Magnusson        menu, or choice statement. Returns a list with the Items in the block.
622f219e013SMasahiro Yamada
62390c36d8aSUlf Magnusson        line_feeder: A _FileFeed instance feeding lines from a file. The
62490c36d8aSUlf Magnusson          Kconfig language is line-based in practice.
625f219e013SMasahiro Yamada
62690c36d8aSUlf Magnusson        end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for
62790c36d8aSUlf Magnusson           ifs. None for files.
62890c36d8aSUlf Magnusson
62990c36d8aSUlf Magnusson        parent: The enclosing menu or choice, or None if we're at the top
630f219e013SMasahiro Yamada           level.
631f219e013SMasahiro Yamada
63290c36d8aSUlf Magnusson        deps: Dependencies from enclosing menus, choices and ifs.
633f219e013SMasahiro Yamada
63490c36d8aSUlf Magnusson        visible_if_deps (default: None): 'visible if' dependencies from
635f219e013SMasahiro Yamada           enclosing menus.
636f219e013SMasahiro Yamada
63790c36d8aSUlf Magnusson        res (default: None): The list to add items to. If None, a new list is
63890c36d8aSUlf Magnusson           created to hold the items."""
639f219e013SMasahiro Yamada
64090c36d8aSUlf Magnusson        block = [] if res is None else res
641f219e013SMasahiro Yamada
642f219e013SMasahiro Yamada        while 1:
643f219e013SMasahiro Yamada            # Do we already have a tokenized line that we determined wasn't
644f219e013SMasahiro Yamada            # part of whatever we were parsing earlier? See comment in
645f219e013SMasahiro Yamada            # Config.__init__().
646f219e013SMasahiro Yamada            if self.end_line is not None:
647f219e013SMasahiro Yamada                line = self.end_line
64890c36d8aSUlf Magnusson                tokens = self.end_line_tokens
64990c36d8aSUlf Magnusson                tokens.unget_all()
650f219e013SMasahiro Yamada
651f219e013SMasahiro Yamada                self.end_line = None
652f219e013SMasahiro Yamada                self.end_line_tokens = None
653f219e013SMasahiro Yamada            else:
654f219e013SMasahiro Yamada                line = line_feeder.get_next()
655f219e013SMasahiro Yamada                if line is None:
656f219e013SMasahiro Yamada                    if end_marker is not None:
65790c36d8aSUlf Magnusson                        raise Kconfig_Syntax_Error("Unexpected end of file {0}"
65890c36d8aSUlf Magnusson                                                 .format(line_feeder.filename))
659f219e013SMasahiro Yamada                    return block
660f219e013SMasahiro Yamada
66190c36d8aSUlf Magnusson                tokens = self._tokenize(line, False, line_feeder.filename,
66290c36d8aSUlf Magnusson                                        line_feeder.linenr)
663f219e013SMasahiro Yamada
664f219e013SMasahiro Yamada            t0 = tokens.get_next()
66590c36d8aSUlf Magnusson            if t0 is None:
66690c36d8aSUlf Magnusson                continue
667f219e013SMasahiro Yamada
66890c36d8aSUlf Magnusson            # Cases are ordered roughly by frequency, which speeds things up a
66990c36d8aSUlf Magnusson            # bit
670f219e013SMasahiro Yamada
671f219e013SMasahiro Yamada            if t0 == T_CONFIG or t0 == T_MENUCONFIG:
672f219e013SMasahiro Yamada                # The tokenizer will automatically allocate a new Symbol object
673f219e013SMasahiro Yamada                # for any new names it encounters, so we don't need to worry
674f219e013SMasahiro Yamada                # about that here.
675f219e013SMasahiro Yamada                sym = tokens.get_next()
676f219e013SMasahiro Yamada
677f219e013SMasahiro Yamada                # Symbols defined in multiple places get the parent of their
67890c36d8aSUlf Magnusson                # first definition. However, for symbols whose parents are
67990c36d8aSUlf Magnusson                # choice statements, the choice statement takes precedence.
680f219e013SMasahiro Yamada                if not sym.is_defined_ or isinstance(parent, Choice):
681f219e013SMasahiro Yamada                    sym.parent = parent
682f219e013SMasahiro Yamada
683f219e013SMasahiro Yamada                sym.is_defined_ = True
684f219e013SMasahiro Yamada
685f219e013SMasahiro Yamada                self.kconfig_syms.append(sym)
68690c36d8aSUlf Magnusson                block.append(sym)
687f219e013SMasahiro Yamada
688f219e013SMasahiro Yamada                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
689f219e013SMasahiro Yamada
69090c36d8aSUlf Magnusson            elif t0 == T_SOURCE:
69190c36d8aSUlf Magnusson                kconfig_file = tokens.get_next()
69290c36d8aSUlf Magnusson                exp_kconfig_file = self._expand_sym_refs(kconfig_file)
69390c36d8aSUlf Magnusson                f = os.path.join(self.base_dir, exp_kconfig_file)
69490c36d8aSUlf Magnusson                if not os.path.exists(f):
69590c36d8aSUlf Magnusson                    raise IOError('{0}:{1}: sourced file "{2}" (expands to '
69690c36d8aSUlf Magnusson                                  '"{3}") not found. Perhaps base_dir '
69790c36d8aSUlf Magnusson                                  '(argument to Config.__init__(), currently '
69890c36d8aSUlf Magnusson                                  '"{4}") is set to the wrong value.'
69990c36d8aSUlf Magnusson                                  .format(line_feeder.filename,
70090c36d8aSUlf Magnusson                                          line_feeder.linenr,
70190c36d8aSUlf Magnusson                                          kconfig_file, exp_kconfig_file,
70290c36d8aSUlf Magnusson                                          self.base_dir))
70390c36d8aSUlf Magnusson                # Add items to the same block
70490c36d8aSUlf Magnusson                self._parse_file(f, parent, deps, visible_if_deps, block)
705f219e013SMasahiro Yamada
70690c36d8aSUlf Magnusson            elif t0 == end_marker:
70790c36d8aSUlf Magnusson                # We have reached the end of the block
70890c36d8aSUlf Magnusson                return block
709f219e013SMasahiro Yamada
710f219e013SMasahiro Yamada            elif t0 == T_IF:
711f219e013SMasahiro Yamada                # If statements are treated as syntactic sugar for adding
712f219e013SMasahiro Yamada                # dependencies to enclosed items and do not have an explicit
713f219e013SMasahiro Yamada                # object representation.
714f219e013SMasahiro Yamada
71590c36d8aSUlf Magnusson                dep_expr = self._parse_expr(tokens, None, line,
71690c36d8aSUlf Magnusson                                            line_feeder.filename,
71790c36d8aSUlf Magnusson                                            line_feeder.linenr)
71890c36d8aSUlf Magnusson                # Add items to the same block
71990c36d8aSUlf Magnusson                self._parse_block(line_feeder, T_ENDIF, parent,
720f219e013SMasahiro Yamada                                  _make_and(dep_expr, deps),
72190c36d8aSUlf Magnusson                                  visible_if_deps, block)
72290c36d8aSUlf Magnusson
72390c36d8aSUlf Magnusson            elif t0 == T_COMMENT:
72490c36d8aSUlf Magnusson                comment = Comment()
72590c36d8aSUlf Magnusson
72690c36d8aSUlf Magnusson                comment.config = self
72790c36d8aSUlf Magnusson                comment.parent = parent
72890c36d8aSUlf Magnusson                comment.filename = line_feeder.filename
72990c36d8aSUlf Magnusson                comment.linenr = line_feeder.linenr
73090c36d8aSUlf Magnusson                comment.text = tokens.get_next()
73190c36d8aSUlf Magnusson
73290c36d8aSUlf Magnusson                self.comments.append(comment)
73390c36d8aSUlf Magnusson                block.append(comment)
73490c36d8aSUlf Magnusson
73590c36d8aSUlf Magnusson                self._parse_properties(line_feeder, comment, deps,
73690c36d8aSUlf Magnusson                                       visible_if_deps)
73790c36d8aSUlf Magnusson
73890c36d8aSUlf Magnusson            elif t0 == T_MENU:
73990c36d8aSUlf Magnusson                menu = Menu()
74090c36d8aSUlf Magnusson
74190c36d8aSUlf Magnusson                menu.config = self
74290c36d8aSUlf Magnusson                menu.parent = parent
74390c36d8aSUlf Magnusson                menu.filename = line_feeder.filename
74490c36d8aSUlf Magnusson                menu.linenr = line_feeder.linenr
74590c36d8aSUlf Magnusson                menu.title = tokens.get_next()
74690c36d8aSUlf Magnusson
74790c36d8aSUlf Magnusson                self.menus.append(menu)
74890c36d8aSUlf Magnusson                block.append(menu)
74990c36d8aSUlf Magnusson
75090c36d8aSUlf Magnusson                # Parse properties and contents
75190c36d8aSUlf Magnusson                self._parse_properties(line_feeder, menu, deps,
75290c36d8aSUlf Magnusson                                       visible_if_deps)
75390c36d8aSUlf Magnusson                menu.block = self._parse_block(line_feeder, T_ENDMENU, menu,
75490c36d8aSUlf Magnusson                                               menu.dep_expr,
75590c36d8aSUlf Magnusson                                               _make_and(visible_if_deps,
75690c36d8aSUlf Magnusson                                                         menu.visible_if_expr))
757f219e013SMasahiro Yamada
758f219e013SMasahiro Yamada            elif t0 == T_CHOICE:
75990c36d8aSUlf Magnusson                name = tokens.get_next()
76090c36d8aSUlf Magnusson                if name is None:
761f219e013SMasahiro Yamada                    choice = Choice()
762f219e013SMasahiro Yamada                    self.choices.append(choice)
76390c36d8aSUlf Magnusson                else:
76490c36d8aSUlf Magnusson                    # Named choice
76590c36d8aSUlf Magnusson                    choice = self.named_choices.get(name)
76690c36d8aSUlf Magnusson                    if choice is None:
76790c36d8aSUlf Magnusson                        choice = Choice()
768f219e013SMasahiro Yamada                        choice.name = name
769f219e013SMasahiro Yamada                        self.named_choices[name] = choice
77090c36d8aSUlf Magnusson                        self.choices.append(choice)
771f219e013SMasahiro Yamada
772f219e013SMasahiro Yamada                choice.config = self
773f219e013SMasahiro Yamada                choice.parent = parent
774f219e013SMasahiro Yamada
77590c36d8aSUlf Magnusson                choice.def_locations.append((line_feeder.filename,
77690c36d8aSUlf Magnusson                                             line_feeder.linenr))
777f219e013SMasahiro Yamada
778f219e013SMasahiro Yamada                # Parse properties and contents
77990c36d8aSUlf Magnusson                self._parse_properties(line_feeder, choice, deps,
780f219e013SMasahiro Yamada                                       visible_if_deps)
78190c36d8aSUlf Magnusson                choice.block = self._parse_block(line_feeder, T_ENDCHOICE,
78290c36d8aSUlf Magnusson                                                 choice, deps, visible_if_deps)
783f219e013SMasahiro Yamada
784f219e013SMasahiro Yamada                choice._determine_actual_symbols()
785f219e013SMasahiro Yamada
78690c36d8aSUlf Magnusson                # If no type is specified for the choice, its type is that of
78790c36d8aSUlf Magnusson                # the first choice item with a specified type
788f219e013SMasahiro Yamada                if choice.type == UNKNOWN:
78990c36d8aSUlf Magnusson                    for item in choice.actual_symbols:
790f219e013SMasahiro Yamada                        if item.type != UNKNOWN:
791f219e013SMasahiro Yamada                            choice.type = item.type
792f219e013SMasahiro Yamada                            break
793f219e013SMasahiro Yamada
794f219e013SMasahiro Yamada                # Each choice item of UNKNOWN type gets the type of the choice
79590c36d8aSUlf Magnusson                for item in choice.actual_symbols:
796f219e013SMasahiro Yamada                    if item.type == UNKNOWN:
797f219e013SMasahiro Yamada                        item.type = choice.type
798f219e013SMasahiro Yamada
79990c36d8aSUlf Magnusson                block.append(choice)
800f219e013SMasahiro Yamada
801f219e013SMasahiro Yamada            elif t0 == T_MAINMENU:
802f219e013SMasahiro Yamada                text = tokens.get_next()
803f219e013SMasahiro Yamada                if self.mainmenu_text is not None:
804f219e013SMasahiro Yamada                    self._warn("overriding 'mainmenu' text. "
805f219e013SMasahiro Yamada                               'Old value: "{0}", new value: "{1}".'
806f219e013SMasahiro Yamada                               .format(self.mainmenu_text, text),
80790c36d8aSUlf Magnusson                               line_feeder.filename, line_feeder.linenr)
808f219e013SMasahiro Yamada                self.mainmenu_text = text
809f219e013SMasahiro Yamada
810f219e013SMasahiro Yamada            else:
81190c36d8aSUlf Magnusson                _parse_error(line, "unrecognized construct",
81290c36d8aSUlf Magnusson                             line_feeder.filename, line_feeder.linenr)
813f219e013SMasahiro Yamada
814f219e013SMasahiro Yamada    def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps):
81590c36d8aSUlf Magnusson        """Parsing of properties for symbols, menus, choices, and comments.
81690c36d8aSUlf Magnusson        Takes care of propagating dependencies from enclosing menus and ifs."""
817f219e013SMasahiro Yamada
818f219e013SMasahiro Yamada        def parse_val_and_cond(tokens, line, filename, linenr):
819f219e013SMasahiro Yamada            """Parses '<expr1> if <expr2>' constructs, where the 'if' part is
820f219e013SMasahiro Yamada            optional. Returns a tuple containing the parsed expressions, with
821f219e013SMasahiro Yamada            None as the second element if the 'if' part is missing."""
822f219e013SMasahiro Yamada            val = self._parse_expr(tokens, stmt, line, filename, linenr, False)
823f219e013SMasahiro Yamada            if tokens.check(T_IF):
82490c36d8aSUlf Magnusson                return (val, self._parse_expr(tokens, stmt, line, filename,
82590c36d8aSUlf Magnusson                                              linenr))
826f219e013SMasahiro Yamada            return (val, None)
827f219e013SMasahiro Yamada
828f219e013SMasahiro Yamada        # In case the symbol is defined in multiple locations, we need to
829f219e013SMasahiro Yamada        # remember what prompts, defaults, and selects are new for this
830f219e013SMasahiro Yamada        # definition, as "depends on" should only apply to the local
831f219e013SMasahiro Yamada        # definition.
832f219e013SMasahiro Yamada        new_prompt = None
833f219e013SMasahiro Yamada        new_def_exprs = []
834f219e013SMasahiro Yamada        new_selects = []
835f219e013SMasahiro Yamada
836f219e013SMasahiro Yamada        # Dependencies from 'depends on' statements
837f219e013SMasahiro Yamada        depends_on_expr = None
838f219e013SMasahiro Yamada
839f219e013SMasahiro Yamada        while 1:
840f219e013SMasahiro Yamada            line = line_feeder.get_next()
841f219e013SMasahiro Yamada            if line is None:
842f219e013SMasahiro Yamada                break
843f219e013SMasahiro Yamada
84490c36d8aSUlf Magnusson            filename = line_feeder.filename
84590c36d8aSUlf Magnusson            linenr = line_feeder.linenr
846f219e013SMasahiro Yamada
847f219e013SMasahiro Yamada            tokens = self._tokenize(line, False, filename, linenr)
848f219e013SMasahiro Yamada
84990c36d8aSUlf Magnusson            t0 = tokens.get_next()
85090c36d8aSUlf Magnusson            if t0 is None:
851f219e013SMasahiro Yamada                continue
852f219e013SMasahiro Yamada
85390c36d8aSUlf Magnusson            # Cases are ordered roughly by frequency, which speeds things up a
85490c36d8aSUlf Magnusson            # bit
855f219e013SMasahiro Yamada
85690c36d8aSUlf Magnusson            if t0 == T_DEPENDS:
85790c36d8aSUlf Magnusson                if not tokens.check(T_ON):
85890c36d8aSUlf Magnusson                    _parse_error(line, 'expected "on" after "depends"',
85990c36d8aSUlf Magnusson                                 filename, linenr)
860f219e013SMasahiro Yamada
86190c36d8aSUlf Magnusson                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
86290c36d8aSUlf Magnusson                                               linenr)
863f219e013SMasahiro Yamada
86490c36d8aSUlf Magnusson                if isinstance(stmt, (Menu, Comment)):
86590c36d8aSUlf Magnusson                    stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps)
86690c36d8aSUlf Magnusson                else:
86790c36d8aSUlf Magnusson                    depends_on_expr = _make_and(depends_on_expr, parsed_deps)
86890c36d8aSUlf Magnusson
86990c36d8aSUlf Magnusson            elif t0 == T_HELP:
87090c36d8aSUlf Magnusson                # Find first non-blank (not all-space) line and get its
87190c36d8aSUlf Magnusson                # indentation
87290c36d8aSUlf Magnusson                line = line_feeder.next_nonblank()
873f219e013SMasahiro Yamada                if line is None:
874f219e013SMasahiro Yamada                    stmt.help = ""
875f219e013SMasahiro Yamada                    break
876f219e013SMasahiro Yamada                indent = _indentation(line)
87790c36d8aSUlf Magnusson                if indent == 0:
878f219e013SMasahiro Yamada                    # If the first non-empty lines has zero indent, there is no
879f219e013SMasahiro Yamada                    # help text
880f219e013SMasahiro Yamada                    stmt.help = ""
88190c36d8aSUlf Magnusson                    line_feeder.unget()
882f219e013SMasahiro Yamada                    break
883f219e013SMasahiro Yamada
884f219e013SMasahiro Yamada                # The help text goes on till the first non-empty line with less
885f219e013SMasahiro Yamada                # indent
88690c36d8aSUlf Magnusson                help_lines = [_deindent(line, indent)]
887f219e013SMasahiro Yamada                while 1:
888f219e013SMasahiro Yamada                    line = line_feeder.get_next()
88990c36d8aSUlf Magnusson                    if line is None or \
890f219e013SMasahiro Yamada                       (not line.isspace() and _indentation(line) < indent):
891f219e013SMasahiro Yamada                        stmt.help = "".join(help_lines)
892f219e013SMasahiro Yamada                        break
893f219e013SMasahiro Yamada                    help_lines.append(_deindent(line, indent))
894f219e013SMasahiro Yamada
895f219e013SMasahiro Yamada                if line is None:
896f219e013SMasahiro Yamada                    break
897f219e013SMasahiro Yamada
89890c36d8aSUlf Magnusson                line_feeder.unget()
899f219e013SMasahiro Yamada
900d036107aSTom Rini            elif t0 == T_SELECT or t0 == T_IMPLY:
901f219e013SMasahiro Yamada                target = tokens.get_next()
902f219e013SMasahiro Yamada
903f219e013SMasahiro Yamada                stmt.referenced_syms.add(target)
904f219e013SMasahiro Yamada                stmt.selected_syms.add(target)
905f219e013SMasahiro Yamada
906f219e013SMasahiro Yamada                if tokens.check(T_IF):
907f219e013SMasahiro Yamada                    new_selects.append((target,
90890c36d8aSUlf Magnusson                                        self._parse_expr(tokens, stmt, line,
90990c36d8aSUlf Magnusson                                                         filename, linenr)))
910f219e013SMasahiro Yamada                else:
911f219e013SMasahiro Yamada                    new_selects.append((target, None))
912f219e013SMasahiro Yamada
913f219e013SMasahiro Yamada            elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
91490c36d8aSUlf Magnusson                stmt.type = TOKEN_TO_TYPE[t0]
91590c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
91690c36d8aSUlf Magnusson                    new_prompt = parse_val_and_cond(tokens, line, filename,
91790c36d8aSUlf Magnusson                                                    linenr)
918f219e013SMasahiro Yamada
91990c36d8aSUlf Magnusson            elif t0 == T_DEFAULT:
92090c36d8aSUlf Magnusson                new_def_exprs.append(parse_val_and_cond(tokens, line, filename,
92190c36d8aSUlf Magnusson                                                        linenr))
922f219e013SMasahiro Yamada
923f219e013SMasahiro Yamada            elif t0 == T_DEF_BOOL:
924f219e013SMasahiro Yamada                stmt.type = BOOL
92590c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
92690c36d8aSUlf Magnusson                    new_def_exprs.append(parse_val_and_cond(tokens, line,
92790c36d8aSUlf Magnusson                                                            filename, linenr))
928f219e013SMasahiro Yamada
92990c36d8aSUlf Magnusson            elif t0 == T_PROMPT:
93090c36d8aSUlf Magnusson                # 'prompt' properties override each other within a single
93190c36d8aSUlf Magnusson                # definition of a symbol, but additional prompts can be added
93290c36d8aSUlf Magnusson                # by defining the symbol multiple times; hence 'new_prompt'
93390c36d8aSUlf Magnusson                # instead of 'prompt'.
93490c36d8aSUlf Magnusson                new_prompt = parse_val_and_cond(tokens, line, filename, linenr)
93590c36d8aSUlf Magnusson
93690c36d8aSUlf Magnusson            elif t0 == T_RANGE:
93790c36d8aSUlf Magnusson                low = tokens.get_next()
93890c36d8aSUlf Magnusson                high = tokens.get_next()
93990c36d8aSUlf Magnusson                stmt.referenced_syms.add(low)
94090c36d8aSUlf Magnusson                stmt.referenced_syms.add(high)
94190c36d8aSUlf Magnusson
94290c36d8aSUlf Magnusson                if tokens.check(T_IF):
94390c36d8aSUlf Magnusson                    stmt.ranges.append((low, high,
94490c36d8aSUlf Magnusson                                        self._parse_expr(tokens, stmt, line,
94590c36d8aSUlf Magnusson                                                         filename, linenr)))
94690c36d8aSUlf Magnusson                else:
94790c36d8aSUlf Magnusson                    stmt.ranges.append((low, high, None))
948f219e013SMasahiro Yamada
949f219e013SMasahiro Yamada            elif t0 == T_DEF_TRISTATE:
950f219e013SMasahiro Yamada                stmt.type = TRISTATE
95190c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
95290c36d8aSUlf Magnusson                    new_def_exprs.append(parse_val_and_cond(tokens, line,
95390c36d8aSUlf Magnusson                                                            filename, linenr))
954f219e013SMasahiro Yamada
955f219e013SMasahiro Yamada            elif t0 == T_OPTION:
956f219e013SMasahiro Yamada                if tokens.check(T_ENV) and tokens.check(T_EQUAL):
957f219e013SMasahiro Yamada                    env_var = tokens.get_next()
958f219e013SMasahiro Yamada
959f219e013SMasahiro Yamada                    stmt.is_special_ = True
960f219e013SMasahiro Yamada                    stmt.is_from_env = True
961f219e013SMasahiro Yamada
962f219e013SMasahiro Yamada                    if env_var not in os.environ:
96390c36d8aSUlf Magnusson                        self._warn("The symbol {0} references the "
96490c36d8aSUlf Magnusson                                   "non-existent environment variable {1} and "
96590c36d8aSUlf Magnusson                                   "will get the empty string as its value. "
96690c36d8aSUlf Magnusson                                   "If you're using Kconfiglib via "
96790c36d8aSUlf Magnusson                                   "'make (i)scriptconfig', it should have "
96890c36d8aSUlf Magnusson                                   "set up the environment correctly for you. "
96990c36d8aSUlf Magnusson                                   "If you still got this message, that "
97090c36d8aSUlf Magnusson                                   "might be an error, and you should email "
97190c36d8aSUlf Magnusson                                   "ulfalizer a.t Google's email service."""
97290c36d8aSUlf Magnusson                                   .format(stmt.name, env_var),
97390c36d8aSUlf Magnusson                                   filename, linenr)
974f219e013SMasahiro Yamada
97590c36d8aSUlf Magnusson                        stmt.cached_val = ""
976f219e013SMasahiro Yamada                    else:
97790c36d8aSUlf Magnusson                        stmt.cached_val = os.environ[env_var]
978f219e013SMasahiro Yamada
979f219e013SMasahiro Yamada                elif tokens.check(T_DEFCONFIG_LIST):
980f219e013SMasahiro Yamada                    self.defconfig_sym = stmt
981f219e013SMasahiro Yamada
982f219e013SMasahiro Yamada                elif tokens.check(T_MODULES):
98390c36d8aSUlf Magnusson                    # To reduce warning spam, only warn if 'option modules' is
98490c36d8aSUlf Magnusson                    # set on some symbol that isn't MODULES, which should be
98590c36d8aSUlf Magnusson                    # safe. I haven't run into any projects that make use
98690c36d8aSUlf Magnusson                    # modules besides the kernel yet, and there it's likely to
98790c36d8aSUlf Magnusson                    # keep being called "MODULES".
98890c36d8aSUlf Magnusson                    if stmt.name != "MODULES":
989f219e013SMasahiro Yamada                        self._warn("the 'modules' option is not supported. "
990f219e013SMasahiro Yamada                                   "Let me know if this is a problem for you; "
9919d01b787SMasahiro Yamada                                   "it shouldn't be that hard to implement. "
9929d01b787SMasahiro Yamada                                   "(Note that modules are still supported -- "
9939d01b787SMasahiro Yamada                                   "Kconfiglib just assumes the symbol name "
99490c36d8aSUlf Magnusson                                   "MODULES, like older versions of the C "
99590c36d8aSUlf Magnusson                                   "implementation did when 'option modules' "
99690c36d8aSUlf Magnusson                                   "wasn't used.)",
99790c36d8aSUlf Magnusson                                   filename, linenr)
998f219e013SMasahiro Yamada
9999d01b787SMasahiro Yamada                elif tokens.check(T_ALLNOCONFIG_Y):
10009d01b787SMasahiro Yamada                    if not isinstance(stmt, Symbol):
10019d01b787SMasahiro Yamada                        _parse_error(line,
100290c36d8aSUlf Magnusson                                     "the 'allnoconfig_y' option is only "
100390c36d8aSUlf Magnusson                                     "valid for symbols",
100490c36d8aSUlf Magnusson                                     filename, linenr)
10059d01b787SMasahiro Yamada                    stmt.allnoconfig_y = True
10069d01b787SMasahiro Yamada
1007f219e013SMasahiro Yamada                else:
100890c36d8aSUlf Magnusson                    _parse_error(line, "unrecognized option", filename, linenr)
100990c36d8aSUlf Magnusson
101090c36d8aSUlf Magnusson            elif t0 == T_VISIBLE:
101190c36d8aSUlf Magnusson                if not tokens.check(T_IF):
101290c36d8aSUlf Magnusson                    _parse_error(line, 'expected "if" after "visible"',
101390c36d8aSUlf Magnusson                                 filename, linenr)
101490c36d8aSUlf Magnusson                if not isinstance(stmt, Menu):
101590c36d8aSUlf Magnusson                    _parse_error(line,
101690c36d8aSUlf Magnusson                                 "'visible if' is only valid for menus",
101790c36d8aSUlf Magnusson                                 filename, linenr)
101890c36d8aSUlf Magnusson
101990c36d8aSUlf Magnusson                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
102090c36d8aSUlf Magnusson                                               linenr)
102190c36d8aSUlf Magnusson                stmt.visible_if_expr = _make_and(stmt.visible_if_expr,
102290c36d8aSUlf Magnusson                                                 parsed_deps)
102390c36d8aSUlf Magnusson
102490c36d8aSUlf Magnusson            elif t0 == T_OPTIONAL:
102590c36d8aSUlf Magnusson                if not isinstance(stmt, Choice):
102690c36d8aSUlf Magnusson                    _parse_error(line,
102790c36d8aSUlf Magnusson                                 '"optional" is only valid for choices',
102890c36d8aSUlf Magnusson                                 filename,
102990c36d8aSUlf Magnusson                                 linenr)
103090c36d8aSUlf Magnusson                stmt.optional = True
1031f219e013SMasahiro Yamada
1032f219e013SMasahiro Yamada            else:
1033f219e013SMasahiro Yamada                # See comment in Config.__init__()
1034f219e013SMasahiro Yamada                self.end_line = line
1035f219e013SMasahiro Yamada                self.end_line_tokens = tokens
1036f219e013SMasahiro Yamada                break
1037f219e013SMasahiro Yamada
103890c36d8aSUlf Magnusson        # Done parsing properties. Now propagate 'depends on' and enclosing
103990c36d8aSUlf Magnusson        # menu/if dependencies to expressions.
1040f219e013SMasahiro Yamada
104190c36d8aSUlf Magnusson        # The set of symbols referenced directly by the statement plus all
104290c36d8aSUlf Magnusson        # symbols referenced by enclosing menus and ifs
104390c36d8aSUlf Magnusson        stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps)
104490c36d8aSUlf Magnusson
104590c36d8aSUlf Magnusson        # Save original dependencies from enclosing menus and ifs
1046f219e013SMasahiro Yamada        stmt.deps_from_containing = deps
1047f219e013SMasahiro Yamada
104890c36d8aSUlf Magnusson        if isinstance(stmt, (Menu, Comment)):
104990c36d8aSUlf Magnusson            stmt.dep_expr = _make_and(stmt.orig_deps, deps)
1050f219e013SMasahiro Yamada        else:
105190c36d8aSUlf Magnusson            # Symbol or Choice
1052f219e013SMasahiro Yamada
1053f219e013SMasahiro Yamada            # See comment for 'menu_dep'
1054f219e013SMasahiro Yamada            stmt.menu_dep = depends_on_expr
1055f219e013SMasahiro Yamada
105690c36d8aSUlf Magnusson            # Propagate dependencies to prompts
1057f219e013SMasahiro Yamada
1058f219e013SMasahiro Yamada            if new_prompt is not None:
105990c36d8aSUlf Magnusson                # Propagate 'visible if' dependencies from enclosing menus
1060f219e013SMasahiro Yamada                prompt, cond_expr = new_prompt
1061f219e013SMasahiro Yamada                cond_expr = _make_and(cond_expr, visible_if_deps)
106290c36d8aSUlf Magnusson                # Propagate 'depends on' dependencies
1063f219e013SMasahiro Yamada                new_prompt = (prompt, _make_and(cond_expr, depends_on_expr))
106490c36d8aSUlf Magnusson                # Save original
1065f219e013SMasahiro Yamada                stmt.orig_prompts.append(new_prompt)
106690c36d8aSUlf Magnusson                # Finalize with dependencies from enclosing menus and ifs
106790c36d8aSUlf Magnusson                stmt.prompts.append((new_prompt[0],
106890c36d8aSUlf Magnusson                                     _make_and(new_prompt[1], deps)))
106990c36d8aSUlf Magnusson
107090c36d8aSUlf Magnusson            # Propagate dependencies to defaults
107190c36d8aSUlf Magnusson
107290c36d8aSUlf Magnusson            # Propagate 'depends on' dependencies
107390c36d8aSUlf Magnusson            new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr))
107490c36d8aSUlf Magnusson                             for val_expr, cond_expr in new_def_exprs]
107590c36d8aSUlf Magnusson            # Save original
107690c36d8aSUlf Magnusson            stmt.orig_def_exprs.extend(new_def_exprs)
107790c36d8aSUlf Magnusson            # Finalize with dependencies from enclosing menus and ifs
107890c36d8aSUlf Magnusson            stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
107990c36d8aSUlf Magnusson                                   for val_expr, cond_expr in new_def_exprs])
108090c36d8aSUlf Magnusson
108190c36d8aSUlf Magnusson            # Propagate dependencies to selects
1082f219e013SMasahiro Yamada
1083f219e013SMasahiro Yamada            # Only symbols can select
1084f219e013SMasahiro Yamada            if isinstance(stmt, Symbol):
108590c36d8aSUlf Magnusson                # Propagate 'depends on' dependencies
108690c36d8aSUlf Magnusson                new_selects = [(target, _make_and(cond_expr, depends_on_expr))
108790c36d8aSUlf Magnusson                               for target, cond_expr in new_selects]
108890c36d8aSUlf Magnusson                # Save original
1089f219e013SMasahiro Yamada                stmt.orig_selects.extend(new_selects)
109090c36d8aSUlf Magnusson                # Finalize with dependencies from enclosing menus and ifs
109190c36d8aSUlf Magnusson                for target, cond in new_selects:
1092f219e013SMasahiro Yamada                    target.rev_dep = _make_or(target.rev_dep,
1093f219e013SMasahiro Yamada                                              _make_and(stmt,
1094f219e013SMasahiro Yamada                                                        _make_and(cond, deps)))
1095f219e013SMasahiro Yamada
109690c36d8aSUlf Magnusson    def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
109790c36d8aSUlf Magnusson                    transform_m=True):
109890c36d8aSUlf Magnusson        """Parses an expression from the tokens in 'feed' using a simple
109990c36d8aSUlf Magnusson        top-down approach. The result has the form
110090c36d8aSUlf Magnusson        '(<operator>, [<parsed operands>])', where <operator> is e.g.
110190c36d8aSUlf Magnusson        kconfiglib.AND. If there is only one operand (i.e., no && or ||), then
110290c36d8aSUlf Magnusson        the operand is returned directly. This also goes for subexpressions.
1103f219e013SMasahiro Yamada
110490c36d8aSUlf Magnusson        feed: _Feed instance containing the tokens for the expression.
1105f219e013SMasahiro Yamada
110690c36d8aSUlf Magnusson        cur_item: The item (Symbol, Choice, Menu, or Comment) currently being
110790c36d8aSUlf Magnusson           parsed, or None if we're not parsing an item. Used for recording
110890c36d8aSUlf Magnusson           references to symbols.
110990c36d8aSUlf Magnusson
111090c36d8aSUlf Magnusson        line: The line containing the expression being parsed.
111190c36d8aSUlf Magnusson
111290c36d8aSUlf Magnusson        filename (default: None): The file containing the expression.
111390c36d8aSUlf Magnusson
111490c36d8aSUlf Magnusson        linenr (default: None): The line number containing the expression.
111590c36d8aSUlf Magnusson
111690c36d8aSUlf Magnusson        transform_m (default: False): Determines if 'm' should be rewritten to
111790c36d8aSUlf Magnusson           'm && MODULES' -- see parse_val_and_cond().
111890c36d8aSUlf Magnusson
111990c36d8aSUlf Magnusson        Expression grammar, in decreasing order of precedence:
112090c36d8aSUlf Magnusson
112190c36d8aSUlf Magnusson        <expr> -> <symbol>
112290c36d8aSUlf Magnusson                  <symbol> '=' <symbol>
112390c36d8aSUlf Magnusson                  <symbol> '!=' <symbol>
112490c36d8aSUlf Magnusson                  '(' <expr> ')'
112590c36d8aSUlf Magnusson                  '!' <expr>
112690c36d8aSUlf Magnusson                  <expr> '&&' <expr>
112790c36d8aSUlf Magnusson                  <expr> '||' <expr>"""
112890c36d8aSUlf Magnusson
112990c36d8aSUlf Magnusson        # Use instance variables to avoid having to pass these as arguments
113090c36d8aSUlf Magnusson        # through the top-down parser in _parse_expr_rec(), which is tedious
113190c36d8aSUlf Magnusson        # and obfuscates the code. A profiler run shows no noticeable
113290c36d8aSUlf Magnusson        # performance difference.
113390c36d8aSUlf Magnusson        self._cur_item = cur_item
113490c36d8aSUlf Magnusson        self._transform_m = transform_m
113590c36d8aSUlf Magnusson        self._line = line
113690c36d8aSUlf Magnusson        self._filename = filename
113790c36d8aSUlf Magnusson        self._linenr = linenr
113890c36d8aSUlf Magnusson
113990c36d8aSUlf Magnusson        return self._parse_expr_rec(feed)
114090c36d8aSUlf Magnusson
114190c36d8aSUlf Magnusson    def _parse_expr_rec(self, feed):
114290c36d8aSUlf Magnusson        or_term = self._parse_or_term(feed)
114390c36d8aSUlf Magnusson        if not feed.check(T_OR):
114490c36d8aSUlf Magnusson            # Common case -- no need for an OR node since it's just a single
114590c36d8aSUlf Magnusson            # operand
114690c36d8aSUlf Magnusson            return or_term
114790c36d8aSUlf Magnusson        or_terms = [or_term, self._parse_or_term(feed)]
114890c36d8aSUlf Magnusson        while feed.check(T_OR):
114990c36d8aSUlf Magnusson            or_terms.append(self._parse_or_term(feed))
115090c36d8aSUlf Magnusson        return (OR, or_terms)
115190c36d8aSUlf Magnusson
115290c36d8aSUlf Magnusson    def _parse_or_term(self, feed):
115390c36d8aSUlf Magnusson        and_term = self._parse_factor(feed)
115490c36d8aSUlf Magnusson        if not feed.check(T_AND):
115590c36d8aSUlf Magnusson            # Common case -- no need for an AND node since it's just a single
115690c36d8aSUlf Magnusson            # operand
115790c36d8aSUlf Magnusson            return and_term
115890c36d8aSUlf Magnusson        and_terms = [and_term, self._parse_factor(feed)]
115990c36d8aSUlf Magnusson        while feed.check(T_AND):
116090c36d8aSUlf Magnusson            and_terms.append(self._parse_factor(feed))
116190c36d8aSUlf Magnusson        return (AND, and_terms)
116290c36d8aSUlf Magnusson
116390c36d8aSUlf Magnusson    def _parse_factor(self, feed):
116490c36d8aSUlf Magnusson        token = feed.get_next()
116590c36d8aSUlf Magnusson
116690c36d8aSUlf Magnusson        if isinstance(token, (Symbol, str)):
116790c36d8aSUlf Magnusson            if self._cur_item is not None and isinstance(token, Symbol):
116890c36d8aSUlf Magnusson                self._cur_item.referenced_syms.add(token)
116990c36d8aSUlf Magnusson
117090c36d8aSUlf Magnusson            next_token = feed.peek_next()
117190c36d8aSUlf Magnusson            # For conditional expressions ('depends on <expr>',
117290c36d8aSUlf Magnusson            # '... if <expr>', # etc.), "m" and m are rewritten to
117390c36d8aSUlf Magnusson            # "m" && MODULES.
117490c36d8aSUlf Magnusson            if next_token != T_EQUAL and next_token != T_UNEQUAL:
117590c36d8aSUlf Magnusson                if self._transform_m and (token is self.m or token == "m"):
117690c36d8aSUlf Magnusson                    return (AND, ["m", self._sym_lookup("MODULES")])
117790c36d8aSUlf Magnusson                return token
117890c36d8aSUlf Magnusson
117990c36d8aSUlf Magnusson            relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL
118090c36d8aSUlf Magnusson            token_2 = feed.get_next()
118190c36d8aSUlf Magnusson            if self._cur_item is not None and isinstance(token_2, Symbol):
118290c36d8aSUlf Magnusson                self._cur_item.referenced_syms.add(token_2)
118390c36d8aSUlf Magnusson            return (relation, token, token_2)
118490c36d8aSUlf Magnusson
118590c36d8aSUlf Magnusson        if token == T_NOT:
118690c36d8aSUlf Magnusson            return (NOT, self._parse_factor(feed))
118790c36d8aSUlf Magnusson
118890c36d8aSUlf Magnusson        if token == T_OPEN_PAREN:
118990c36d8aSUlf Magnusson            expr_parse = self._parse_expr_rec(feed)
119090c36d8aSUlf Magnusson            if not feed.check(T_CLOSE_PAREN):
119190c36d8aSUlf Magnusson                _parse_error(self._line, "missing end parenthesis",
119290c36d8aSUlf Magnusson                             self._filename, self._linenr)
119390c36d8aSUlf Magnusson            return expr_parse
119490c36d8aSUlf Magnusson
119590c36d8aSUlf Magnusson        _parse_error(self._line, "malformed expression", self._filename,
119690c36d8aSUlf Magnusson                     self._linenr)
119790c36d8aSUlf Magnusson
119890c36d8aSUlf Magnusson    def _tokenize(self, s, for_eval, filename=None, linenr=None):
119990c36d8aSUlf Magnusson        """Returns a _Feed instance containing tokens derived from the string
120090c36d8aSUlf Magnusson        's'. Registers any new symbols encountered (via _sym_lookup()).
120190c36d8aSUlf Magnusson
120290c36d8aSUlf Magnusson        (I experimented with a pure regular expression implementation, but it
120390c36d8aSUlf Magnusson        came out slower, less readable, and wouldn't have been as flexible.)
120490c36d8aSUlf Magnusson
120590c36d8aSUlf Magnusson        for_eval: True when parsing an expression for a call to Config.eval(),
120690c36d8aSUlf Magnusson           in which case we should not treat the first token specially nor
120790c36d8aSUlf Magnusson           register new symbols."""
120890c36d8aSUlf Magnusson
120990c36d8aSUlf Magnusson        s = s.strip()
121090c36d8aSUlf Magnusson        if s == "" or s[0] == "#":
121190c36d8aSUlf Magnusson            return _Feed([])
121290c36d8aSUlf Magnusson
121390c36d8aSUlf Magnusson        if for_eval:
121490c36d8aSUlf Magnusson            previous = None # The previous token seen
121590c36d8aSUlf Magnusson            tokens = []
121690c36d8aSUlf Magnusson            i = 0 # The current index in the string being tokenized
121790c36d8aSUlf Magnusson
121890c36d8aSUlf Magnusson        else:
121990c36d8aSUlf Magnusson            # The initial word on a line is parsed specially. Let
122090c36d8aSUlf Magnusson            # command_chars = [A-Za-z0-9_]. Then
122190c36d8aSUlf Magnusson            #  - leading non-command_chars characters are ignored, and
122290c36d8aSUlf Magnusson            #  - the first token consists the following one or more
122390c36d8aSUlf Magnusson            #    command_chars characters.
122490c36d8aSUlf Magnusson            # This is why things like "----help--" are accepted.
122590c36d8aSUlf Magnusson            initial_token_match = _initial_token_re_match(s)
122690c36d8aSUlf Magnusson            if initial_token_match is None:
122790c36d8aSUlf Magnusson                return _Feed([])
122890c36d8aSUlf Magnusson            keyword = _get_keyword(initial_token_match.group(1))
122990c36d8aSUlf Magnusson            if keyword == T_HELP:
123090c36d8aSUlf Magnusson                # Avoid junk after "help", e.g. "---", being registered as a
123190c36d8aSUlf Magnusson                # symbol
123290c36d8aSUlf Magnusson                return _Feed([T_HELP])
123390c36d8aSUlf Magnusson            if keyword is None:
123490c36d8aSUlf Magnusson                # We expect a keyword as the first token
123590c36d8aSUlf Magnusson                _tokenization_error(s, filename, linenr)
123690c36d8aSUlf Magnusson
123790c36d8aSUlf Magnusson            previous = keyword
123890c36d8aSUlf Magnusson            tokens = [keyword]
123990c36d8aSUlf Magnusson            # The current index in the string being tokenized
124090c36d8aSUlf Magnusson            i = initial_token_match.end()
124190c36d8aSUlf Magnusson
124290c36d8aSUlf Magnusson        # _tokenize() is a hotspot during parsing, and this speeds things up a
124390c36d8aSUlf Magnusson        # bit
124490c36d8aSUlf Magnusson        strlen = len(s)
124590c36d8aSUlf Magnusson        append = tokens.append
124690c36d8aSUlf Magnusson
124790c36d8aSUlf Magnusson        # Main tokenization loop. (Handles tokens past the first one.)
124890c36d8aSUlf Magnusson        while i < strlen:
124990c36d8aSUlf Magnusson            # Test for an identifier/keyword preceded by whitespace first; this
125090c36d8aSUlf Magnusson            # is the most common case.
125190c36d8aSUlf Magnusson            id_keyword_match = _id_keyword_re_match(s, i)
125290c36d8aSUlf Magnusson            if id_keyword_match:
125390c36d8aSUlf Magnusson                # We have an identifier or keyword. The above also stripped any
125490c36d8aSUlf Magnusson                # whitespace for us.
125590c36d8aSUlf Magnusson                name = id_keyword_match.group(1)
125690c36d8aSUlf Magnusson                # Jump past it
125790c36d8aSUlf Magnusson                i = id_keyword_match.end()
125890c36d8aSUlf Magnusson
125990c36d8aSUlf Magnusson                keyword = _get_keyword(name)
126090c36d8aSUlf Magnusson                if keyword is not None:
126190c36d8aSUlf Magnusson                    # It's a keyword
126290c36d8aSUlf Magnusson                    append(keyword)
126390c36d8aSUlf Magnusson                elif previous in STRING_LEX:
126490c36d8aSUlf Magnusson                    # What would ordinarily be considered an identifier is
126590c36d8aSUlf Magnusson                    # treated as a string after certain tokens
126690c36d8aSUlf Magnusson                    append(name)
126790c36d8aSUlf Magnusson                else:
126890c36d8aSUlf Magnusson                    # It's a symbol name. _sym_lookup() will take care of
126990c36d8aSUlf Magnusson                    # allocating a new Symbol instance if it's the first time
127090c36d8aSUlf Magnusson                    # we see it.
127190c36d8aSUlf Magnusson                    sym = self._sym_lookup(name, for_eval)
127290c36d8aSUlf Magnusson
127390c36d8aSUlf Magnusson                    if previous == T_CONFIG or previous == T_MENUCONFIG:
127490c36d8aSUlf Magnusson                        # If the previous token is T_(MENU)CONFIG
127590c36d8aSUlf Magnusson                        # ("(menu)config"), we're tokenizing the first line of
127690c36d8aSUlf Magnusson                        # a symbol definition, and should remember this as a
127790c36d8aSUlf Magnusson                        # location where the symbol is defined
127890c36d8aSUlf Magnusson                        sym.def_locations.append((filename, linenr))
127990c36d8aSUlf Magnusson                    else:
128090c36d8aSUlf Magnusson                        # Otherwise, it's a reference to the symbol
128190c36d8aSUlf Magnusson                        sym.ref_locations.append((filename, linenr))
128290c36d8aSUlf Magnusson
128390c36d8aSUlf Magnusson                    append(sym)
128490c36d8aSUlf Magnusson
128590c36d8aSUlf Magnusson            else:
128690c36d8aSUlf Magnusson                # Not an identifier/keyword
128790c36d8aSUlf Magnusson
128890c36d8aSUlf Magnusson                while i < strlen and s[i].isspace():
128990c36d8aSUlf Magnusson                    i += 1
129090c36d8aSUlf Magnusson                if i == strlen:
129190c36d8aSUlf Magnusson                    break
129290c36d8aSUlf Magnusson                c = s[i]
129390c36d8aSUlf Magnusson                i += 1
129490c36d8aSUlf Magnusson
129590c36d8aSUlf Magnusson                # String literal (constant symbol)
129690c36d8aSUlf Magnusson                if c == '"' or c == "'":
129790c36d8aSUlf Magnusson                    if "\\" in s:
129890c36d8aSUlf Magnusson                        # Slow path: This could probably be sped up, but it's a
129990c36d8aSUlf Magnusson                        # very unusual case anyway.
130090c36d8aSUlf Magnusson                        quote = c
130190c36d8aSUlf Magnusson                        val = ""
130290c36d8aSUlf Magnusson                        while 1:
130390c36d8aSUlf Magnusson                            if i >= len(s):
130490c36d8aSUlf Magnusson                                _tokenization_error(s, filename, linenr)
130590c36d8aSUlf Magnusson                            c = s[i]
130690c36d8aSUlf Magnusson                            if c == quote:
130790c36d8aSUlf Magnusson                                break
130890c36d8aSUlf Magnusson                            if c == "\\":
130990c36d8aSUlf Magnusson                                if i + 1 >= len(s):
131090c36d8aSUlf Magnusson                                    _tokenization_error(s, filename, linenr)
131190c36d8aSUlf Magnusson                                val += s[i + 1]
131290c36d8aSUlf Magnusson                                i += 2
131390c36d8aSUlf Magnusson                            else:
131490c36d8aSUlf Magnusson                                val += c
131590c36d8aSUlf Magnusson                                i += 1
131690c36d8aSUlf Magnusson                        i += 1
131790c36d8aSUlf Magnusson                        append(val)
131890c36d8aSUlf Magnusson                    else:
131990c36d8aSUlf Magnusson                        # Fast path: If the string contains no backslashes
132090c36d8aSUlf Magnusson                        # (almost always) we can simply look for the matching
132190c36d8aSUlf Magnusson                        # quote.
132290c36d8aSUlf Magnusson                        end = s.find(c, i)
132390c36d8aSUlf Magnusson                        if end == -1:
132490c36d8aSUlf Magnusson                            _tokenization_error(s, filename, linenr)
132590c36d8aSUlf Magnusson                        append(s[i:end])
132690c36d8aSUlf Magnusson                        i = end + 1
132790c36d8aSUlf Magnusson
132890c36d8aSUlf Magnusson                elif c == "&":
132990c36d8aSUlf Magnusson                    # Invalid characters are ignored
133090c36d8aSUlf Magnusson                    if i >= len(s) or s[i] != "&": continue
133190c36d8aSUlf Magnusson                    append(T_AND)
133290c36d8aSUlf Magnusson                    i += 1
133390c36d8aSUlf Magnusson
133490c36d8aSUlf Magnusson                elif c == "|":
133590c36d8aSUlf Magnusson                    # Invalid characters are ignored
133690c36d8aSUlf Magnusson                    if i >= len(s) or s[i] != "|": continue
133790c36d8aSUlf Magnusson                    append(T_OR)
133890c36d8aSUlf Magnusson                    i += 1
133990c36d8aSUlf Magnusson
134090c36d8aSUlf Magnusson                elif c == "!":
134190c36d8aSUlf Magnusson                    if i < len(s) and s[i] == "=":
134290c36d8aSUlf Magnusson                        append(T_UNEQUAL)
134390c36d8aSUlf Magnusson                        i += 1
134490c36d8aSUlf Magnusson                    else:
134590c36d8aSUlf Magnusson                        append(T_NOT)
134690c36d8aSUlf Magnusson
134790c36d8aSUlf Magnusson                elif c == "=": append(T_EQUAL)
134890c36d8aSUlf Magnusson                elif c == "(": append(T_OPEN_PAREN)
134990c36d8aSUlf Magnusson                elif c == ")": append(T_CLOSE_PAREN)
135090c36d8aSUlf Magnusson                elif c == "#": break # Comment
135190c36d8aSUlf Magnusson
135290c36d8aSUlf Magnusson                else: continue # Invalid characters are ignored
135390c36d8aSUlf Magnusson
135490c36d8aSUlf Magnusson            previous = tokens[-1]
135590c36d8aSUlf Magnusson
135690c36d8aSUlf Magnusson        return _Feed(tokens)
135790c36d8aSUlf Magnusson
135890c36d8aSUlf Magnusson    def _sym_lookup(self, name, for_eval=False):
135990c36d8aSUlf Magnusson        """Fetches the symbol 'name' from the symbol table, creating and
136090c36d8aSUlf Magnusson        registering it if it does not exist. If 'for_eval' is True, the symbol
136190c36d8aSUlf Magnusson        won't be added to the symbol table if it does not exist -- this is for
136290c36d8aSUlf Magnusson        Config.eval()."""
1363f219e013SMasahiro Yamada        if name in self.syms:
1364f219e013SMasahiro Yamada            return self.syms[name]
1365f219e013SMasahiro Yamada
1366f219e013SMasahiro Yamada        new_sym = Symbol()
1367f219e013SMasahiro Yamada        new_sym.config = self
1368f219e013SMasahiro Yamada        new_sym.name = name
136990c36d8aSUlf Magnusson        if for_eval:
1370f219e013SMasahiro Yamada            self._warn("no symbol {0} in configuration".format(name))
137190c36d8aSUlf Magnusson        else:
137290c36d8aSUlf Magnusson            self.syms[name] = new_sym
1373f219e013SMasahiro Yamada        return new_sym
1374f219e013SMasahiro Yamada
1375f219e013SMasahiro Yamada    #
137690c36d8aSUlf Magnusson    # Expression evaluation
1377f219e013SMasahiro Yamada    #
1378f219e013SMasahiro Yamada
1379f219e013SMasahiro Yamada    def _eval_expr(self, expr):
138090c36d8aSUlf Magnusson        """Evaluates an expression to "n", "m", or "y"."""
1381f219e013SMasahiro Yamada
138290c36d8aSUlf Magnusson        # Handles e.g. an "x if y" condition where the "if y" part is missing.
1383f219e013SMasahiro Yamada        if expr is None:
1384f219e013SMasahiro Yamada            return "y"
1385f219e013SMasahiro Yamada
138690c36d8aSUlf Magnusson        res = self._eval_expr_rec(expr)
138790c36d8aSUlf Magnusson        if res == "m":
138890c36d8aSUlf Magnusson            # Promote "m" to "y" if we're running without modules.
138990c36d8aSUlf Magnusson            #
139090c36d8aSUlf Magnusson            # Internally, "m" is often rewritten to "m" && MODULES by both the
139190c36d8aSUlf Magnusson            # C implementation and Kconfiglib, which takes care of cases where
139290c36d8aSUlf Magnusson            # "m" should be demoted to "n" instead.
139390c36d8aSUlf Magnusson            modules_sym = self.syms.get("MODULES")
139490c36d8aSUlf Magnusson            if modules_sym is None or modules_sym.get_value() != "y":
139590c36d8aSUlf Magnusson                return "y"
139690c36d8aSUlf Magnusson        return res
139790c36d8aSUlf Magnusson
139890c36d8aSUlf Magnusson    def _eval_expr_rec(self, expr):
1399f219e013SMasahiro Yamada        if isinstance(expr, Symbol):
1400f219e013SMasahiro Yamada            # Non-bool/tristate symbols are always "n" in a tristate sense,
1401f219e013SMasahiro Yamada            # regardless of their value
1402f219e013SMasahiro Yamada            if expr.type != BOOL and expr.type != TRISTATE:
1403f219e013SMasahiro Yamada                return "n"
1404f219e013SMasahiro Yamada            return expr.get_value()
1405f219e013SMasahiro Yamada
1406f219e013SMasahiro Yamada        if isinstance(expr, str):
1407f219e013SMasahiro Yamada            return expr if (expr == "y" or expr == "m") else "n"
1408f219e013SMasahiro Yamada
140990c36d8aSUlf Magnusson        # Ordered by frequency
1410f219e013SMasahiro Yamada
141190c36d8aSUlf Magnusson        if expr[0] == AND:
1412f219e013SMasahiro Yamada            res = "y"
1413f219e013SMasahiro Yamada            for subexpr in expr[1]:
141490c36d8aSUlf Magnusson                ev = self._eval_expr_rec(subexpr)
1415f219e013SMasahiro Yamada                # Return immediately upon discovering an "n" term
1416f219e013SMasahiro Yamada                if ev == "n":
1417f219e013SMasahiro Yamada                    return "n"
1418f219e013SMasahiro Yamada                if ev == "m":
1419f219e013SMasahiro Yamada                    res = "m"
1420f219e013SMasahiro Yamada            # 'res' is either "m" or "y" here; we already handled the
1421f219e013SMasahiro Yamada            # short-circuiting "n" case in the loop.
1422f219e013SMasahiro Yamada            return res
1423f219e013SMasahiro Yamada
142490c36d8aSUlf Magnusson        if expr[0] == NOT:
142590c36d8aSUlf Magnusson            ev = self._eval_expr_rec(expr[1])
1426f219e013SMasahiro Yamada            if ev == "y":
1427f219e013SMasahiro Yamada                return "n"
1428f219e013SMasahiro Yamada            return "y" if (ev == "n") else "m"
1429f219e013SMasahiro Yamada
143090c36d8aSUlf Magnusson        if expr[0] == OR:
143190c36d8aSUlf Magnusson            res = "n"
143290c36d8aSUlf Magnusson            for subexpr in expr[1]:
143390c36d8aSUlf Magnusson                ev = self._eval_expr_rec(subexpr)
143490c36d8aSUlf Magnusson                # Return immediately upon discovering a "y" term
143590c36d8aSUlf Magnusson                if ev == "y":
143690c36d8aSUlf Magnusson                    return "y"
143790c36d8aSUlf Magnusson                if ev == "m":
143890c36d8aSUlf Magnusson                    res = "m"
143990c36d8aSUlf Magnusson            # 'res' is either "n" or "m" here; we already handled the
144090c36d8aSUlf Magnusson            # short-circuiting "y" case in the loop.
144190c36d8aSUlf Magnusson            return res
1442f219e013SMasahiro Yamada
144390c36d8aSUlf Magnusson        if expr[0] == EQUAL:
144490c36d8aSUlf Magnusson            return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n"
144590c36d8aSUlf Magnusson
144690c36d8aSUlf Magnusson        if expr[0] == UNEQUAL:
144790c36d8aSUlf Magnusson            return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n"
1448f219e013SMasahiro Yamada
1449f219e013SMasahiro Yamada        _internal_error("Internal error while evaluating expression: "
145090c36d8aSUlf Magnusson                        "unknown operation {0}.".format(expr[0]))
1451f219e013SMasahiro Yamada
1452f219e013SMasahiro Yamada    def _eval_min(self, e1, e2):
145390c36d8aSUlf Magnusson        """Returns the minimum value of the two expressions. Equates None with
145490c36d8aSUlf Magnusson        'y'."""
1455f219e013SMasahiro Yamada        e1_eval = self._eval_expr(e1)
1456f219e013SMasahiro Yamada        e2_eval = self._eval_expr(e2)
1457f219e013SMasahiro Yamada        return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval
1458f219e013SMasahiro Yamada
1459f219e013SMasahiro Yamada    def _eval_max(self, e1, e2):
146090c36d8aSUlf Magnusson        """Returns the maximum value of the two expressions. Equates None with
146190c36d8aSUlf Magnusson        'y'."""
1462f219e013SMasahiro Yamada        e1_eval = self._eval_expr(e1)
1463f219e013SMasahiro Yamada        e2_eval = self._eval_expr(e2)
1464f219e013SMasahiro Yamada        return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval
1465f219e013SMasahiro Yamada
1466f219e013SMasahiro Yamada    #
146790c36d8aSUlf Magnusson    # Dependency tracking (for caching and invalidation)
1468f219e013SMasahiro Yamada    #
1469f219e013SMasahiro Yamada
1470f219e013SMasahiro Yamada    def _build_dep(self):
1471f219e013SMasahiro Yamada        """Populates the Symbol.dep sets, linking the symbol to the symbols
1472f219e013SMasahiro Yamada        that immediately depend on it in the sense that changing the value of
1473f219e013SMasahiro Yamada        the symbol might affect the values of those other symbols. This is used
1474f219e013SMasahiro Yamada        for caching/invalidation purposes. The calculated sets might be larger
1475f219e013SMasahiro Yamada        than necessary as we don't do any complicated analysis of the
1476f219e013SMasahiro Yamada        expressions."""
1477f219e013SMasahiro Yamada
1478f219e013SMasahiro Yamada        # Adds 'sym' as a directly dependent symbol to all symbols that appear
1479f219e013SMasahiro Yamada        # in the expression 'e'
1480f219e013SMasahiro Yamada        def add_expr_deps(e, sym):
1481f219e013SMasahiro Yamada            for s in _get_expr_syms(e):
1482f219e013SMasahiro Yamada                s.dep.add(sym)
1483f219e013SMasahiro Yamada
1484f219e013SMasahiro Yamada        # The directly dependent symbols of a symbol are:
1485f219e013SMasahiro Yamada        #  - Any symbols whose prompts, default values, rev_dep (select
1486f219e013SMasahiro Yamada        #    condition), or ranges depend on the symbol
1487f219e013SMasahiro Yamada        #  - Any symbols that belong to the same choice statement as the symbol
1488f219e013SMasahiro Yamada        #    (these won't be included in 'dep' as that makes the dependency
1489f219e013SMasahiro Yamada        #    graph unwieldy, but Symbol._get_dependent() will include them)
1490f219e013SMasahiro Yamada        #  - Any symbols in a choice statement that depends on the symbol
149190c36d8aSUlf Magnusson        for sym in self.syms_iter():
149290c36d8aSUlf Magnusson            for _, e in sym.prompts:
1493f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1494f219e013SMasahiro Yamada
149590c36d8aSUlf Magnusson            for v, e in sym.def_exprs:
1496f219e013SMasahiro Yamada                add_expr_deps(v, sym)
1497f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1498f219e013SMasahiro Yamada
1499f219e013SMasahiro Yamada            add_expr_deps(sym.rev_dep, sym)
1500f219e013SMasahiro Yamada
150190c36d8aSUlf Magnusson            for l, u, e in sym.ranges:
1502f219e013SMasahiro Yamada                add_expr_deps(l, sym)
1503f219e013SMasahiro Yamada                add_expr_deps(u, sym)
1504f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1505f219e013SMasahiro Yamada
150690c36d8aSUlf Magnusson            if sym.is_choice_sym:
1507f219e013SMasahiro Yamada                choice = sym.parent
150890c36d8aSUlf Magnusson                for _, e in choice.prompts:
150990c36d8aSUlf Magnusson                    add_expr_deps(e, sym)
151090c36d8aSUlf Magnusson                for _, e in choice.def_exprs:
1511f219e013SMasahiro Yamada                    add_expr_deps(e, sym)
1512f219e013SMasahiro Yamada
151390c36d8aSUlf Magnusson    def _eq_to_sym(self, eq):
151490c36d8aSUlf Magnusson        """_expr_depends_on() helper. For (in)equalities of the form sym = y/m
151590c36d8aSUlf Magnusson        or sym != n, returns sym. For other (in)equalities, returns None."""
151690c36d8aSUlf Magnusson        relation, left, right = eq
1517f219e013SMasahiro Yamada
151890c36d8aSUlf Magnusson        def transform_y_m_n(item):
151990c36d8aSUlf Magnusson            if item is self.y: return "y"
152090c36d8aSUlf Magnusson            if item is self.m: return "m"
152190c36d8aSUlf Magnusson            if item is self.n: return "n"
152290c36d8aSUlf Magnusson            return item
152390c36d8aSUlf Magnusson
152490c36d8aSUlf Magnusson        left = transform_y_m_n(left)
152590c36d8aSUlf Magnusson        right = transform_y_m_n(right)
152690c36d8aSUlf Magnusson
152790c36d8aSUlf Magnusson        # Make sure the symbol (if any) appears to the left
152890c36d8aSUlf Magnusson        if not isinstance(left, Symbol):
152990c36d8aSUlf Magnusson            left, right = right, left
153090c36d8aSUlf Magnusson        if not isinstance(left, Symbol):
153190c36d8aSUlf Magnusson            return None
153290c36d8aSUlf Magnusson        if (relation == EQUAL and (right == "y" or right == "m")) or \
153390c36d8aSUlf Magnusson           (relation == UNEQUAL and right == "n"):
153490c36d8aSUlf Magnusson            return left
153590c36d8aSUlf Magnusson        return None
153690c36d8aSUlf Magnusson
153790c36d8aSUlf Magnusson    def _expr_depends_on(self, expr, sym):
153890c36d8aSUlf Magnusson        """Reimplementation of expr_depends_symbol() from mconf.c. Used to
153990c36d8aSUlf Magnusson        determine if a submenu should be implicitly created, which influences
154090c36d8aSUlf Magnusson        what items inside choice statements are considered choice items."""
154190c36d8aSUlf Magnusson        if expr is None:
154290c36d8aSUlf Magnusson            return False
154390c36d8aSUlf Magnusson
154490c36d8aSUlf Magnusson        def rec(expr):
154590c36d8aSUlf Magnusson            if isinstance(expr, str):
154690c36d8aSUlf Magnusson                return False
154790c36d8aSUlf Magnusson            if isinstance(expr, Symbol):
154890c36d8aSUlf Magnusson                return expr is sym
154990c36d8aSUlf Magnusson
155090c36d8aSUlf Magnusson            if expr[0] in (EQUAL, UNEQUAL):
155190c36d8aSUlf Magnusson                return self._eq_to_sym(expr) is sym
155290c36d8aSUlf Magnusson            if expr[0] == AND:
155390c36d8aSUlf Magnusson                for and_expr in expr[1]:
155490c36d8aSUlf Magnusson                    if rec(and_expr):
155590c36d8aSUlf Magnusson                        return True
155690c36d8aSUlf Magnusson            return False
155790c36d8aSUlf Magnusson
155890c36d8aSUlf Magnusson        return rec(expr)
155990c36d8aSUlf Magnusson
156090c36d8aSUlf Magnusson    def _invalidate_all(self):
156190c36d8aSUlf Magnusson        for sym in self.syms_iter():
156290c36d8aSUlf Magnusson            sym._invalidate()
156390c36d8aSUlf Magnusson
156490c36d8aSUlf Magnusson    #
156590c36d8aSUlf Magnusson    # Printing and misc.
156690c36d8aSUlf Magnusson    #
156790c36d8aSUlf Magnusson
156890c36d8aSUlf Magnusson    def _expand_sym_refs(self, s):
156990c36d8aSUlf Magnusson        """Expands $-references to symbols in 's' to symbol values, or to the
157090c36d8aSUlf Magnusson        empty string for undefined symbols."""
157190c36d8aSUlf Magnusson
157290c36d8aSUlf Magnusson        while 1:
157390c36d8aSUlf Magnusson            sym_ref_match = _sym_ref_re_search(s)
157490c36d8aSUlf Magnusson            if sym_ref_match is None:
157590c36d8aSUlf Magnusson                return s
157690c36d8aSUlf Magnusson
157790c36d8aSUlf Magnusson            sym_name = sym_ref_match.group(0)[1:]
157890c36d8aSUlf Magnusson            sym = self.syms.get(sym_name)
157990c36d8aSUlf Magnusson            expansion = "" if sym is None else sym.get_value()
158090c36d8aSUlf Magnusson
158190c36d8aSUlf Magnusson            s = s[:sym_ref_match.start()] + \
158290c36d8aSUlf Magnusson                expansion + \
158390c36d8aSUlf Magnusson                s[sym_ref_match.end():]
158490c36d8aSUlf Magnusson
158590c36d8aSUlf Magnusson    def _expr_val_str(self, expr, no_value_str="(none)",
158690c36d8aSUlf Magnusson                      get_val_instead_of_eval=False):
158790c36d8aSUlf Magnusson        """Printing helper. Returns a string with 'expr' and its value.
158890c36d8aSUlf Magnusson
158990c36d8aSUlf Magnusson        no_value_str: String to return when 'expr' is missing (None).
159090c36d8aSUlf Magnusson
159190c36d8aSUlf Magnusson        get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant
159290c36d8aSUlf Magnusson          symbol) and get its value directly instead of evaluating it to a
159390c36d8aSUlf Magnusson          tristate value."""
1594f219e013SMasahiro Yamada
1595f219e013SMasahiro Yamada        if expr is None:
1596f219e013SMasahiro Yamada            return no_value_str
1597f219e013SMasahiro Yamada
1598f219e013SMasahiro Yamada        if get_val_instead_of_eval:
1599f219e013SMasahiro Yamada            if isinstance(expr, str):
1600f219e013SMasahiro Yamada                return _expr_to_str(expr)
1601f219e013SMasahiro Yamada            val = expr.get_value()
1602f219e013SMasahiro Yamada        else:
1603f219e013SMasahiro Yamada            val = self._eval_expr(expr)
1604f219e013SMasahiro Yamada
1605f219e013SMasahiro Yamada        return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val))
1606f219e013SMasahiro Yamada
1607f219e013SMasahiro Yamada    def _get_sym_or_choice_str(self, sc):
1608f219e013SMasahiro Yamada        """Symbols and choices have many properties in common, so we factor out
1609f219e013SMasahiro Yamada        common __str__() stuff here. "sc" is short for "symbol or choice"."""
1610f219e013SMasahiro Yamada
1611f219e013SMasahiro Yamada        # As we deal a lot with string representations here, use some
1612f219e013SMasahiro Yamada        # convenient shorthand:
1613f219e013SMasahiro Yamada        s = _expr_to_str
1614f219e013SMasahiro Yamada
1615f219e013SMasahiro Yamada        #
1616f219e013SMasahiro Yamada        # Common symbol/choice properties
1617f219e013SMasahiro Yamada        #
1618f219e013SMasahiro Yamada
161990c36d8aSUlf Magnusson        user_val_str = "(no user value)" if sc.user_val is None else \
162090c36d8aSUlf Magnusson                       s(sc.user_val)
1621f219e013SMasahiro Yamada
1622f219e013SMasahiro Yamada        # Build prompts string
162390c36d8aSUlf Magnusson        if not sc.prompts:
1624f219e013SMasahiro Yamada            prompts_str = " (no prompts)"
1625f219e013SMasahiro Yamada        else:
1626f219e013SMasahiro Yamada            prompts_str_rows = []
162790c36d8aSUlf Magnusson            for prompt, cond_expr in sc.orig_prompts:
1628f219e013SMasahiro Yamada                if cond_expr is None:
1629f219e013SMasahiro Yamada                    prompts_str_rows.append(' "{0}"'.format(prompt))
1630f219e013SMasahiro Yamada                else:
163190c36d8aSUlf Magnusson                    prompts_str_rows.append(
163290c36d8aSUlf Magnusson                      ' "{0}" if {1}'.format(prompt,
163390c36d8aSUlf Magnusson                                             self._expr_val_str(cond_expr)))
1634f219e013SMasahiro Yamada            prompts_str = "\n".join(prompts_str_rows)
1635f219e013SMasahiro Yamada
1636f219e013SMasahiro Yamada        # Build locations string
163790c36d8aSUlf Magnusson        if not sc.def_locations:
1638f219e013SMasahiro Yamada            locations_str = "(no locations)"
1639f219e013SMasahiro Yamada        else:
1640f219e013SMasahiro Yamada            locations_str = " ".join(["{0}:{1}".format(filename, linenr) for
1641f219e013SMasahiro Yamada                                      (filename, linenr) in sc.def_locations])
1642f219e013SMasahiro Yamada
164390c36d8aSUlf Magnusson        # Build additional-dependencies-from-menus-and-ifs string
164490c36d8aSUlf Magnusson        additional_deps_str = " " + \
164590c36d8aSUlf Magnusson          self._expr_val_str(sc.deps_from_containing,
1646f219e013SMasahiro Yamada                             "(no additional dependencies)")
1647f219e013SMasahiro Yamada
1648f219e013SMasahiro Yamada        #
1649f219e013SMasahiro Yamada        # Symbol-specific stuff
1650f219e013SMasahiro Yamada        #
1651f219e013SMasahiro Yamada
1652f219e013SMasahiro Yamada        if isinstance(sc, Symbol):
1653f219e013SMasahiro Yamada            # Build ranges string
1654f219e013SMasahiro Yamada            if isinstance(sc, Symbol):
165590c36d8aSUlf Magnusson                if not sc.ranges:
1656f219e013SMasahiro Yamada                    ranges_str = " (no ranges)"
1657f219e013SMasahiro Yamada                else:
1658f219e013SMasahiro Yamada                    ranges_str_rows = []
165990c36d8aSUlf Magnusson                    for l, u, cond_expr in sc.ranges:
1660f219e013SMasahiro Yamada                        if cond_expr is None:
166190c36d8aSUlf Magnusson                            ranges_str_rows.append(" [{0}, {1}]".format(s(l),
166290c36d8aSUlf Magnusson                                                                        s(u)))
1663f219e013SMasahiro Yamada                        else:
1664f219e013SMasahiro Yamada                            ranges_str_rows.append(" [{0}, {1}] if {2}"
166590c36d8aSUlf Magnusson                              .format(s(l), s(u),
166690c36d8aSUlf Magnusson                                      self._expr_val_str(cond_expr)))
1667f219e013SMasahiro Yamada                    ranges_str = "\n".join(ranges_str_rows)
1668f219e013SMasahiro Yamada
1669f219e013SMasahiro Yamada            # Build default values string
167090c36d8aSUlf Magnusson            if not sc.def_exprs:
1671f219e013SMasahiro Yamada                defaults_str = " (no default values)"
1672f219e013SMasahiro Yamada            else:
1673f219e013SMasahiro Yamada                defaults_str_rows = []
167490c36d8aSUlf Magnusson                for val_expr, cond_expr in sc.orig_def_exprs:
167590c36d8aSUlf Magnusson                    row_str = " " + self._expr_val_str(val_expr, "(none)",
167690c36d8aSUlf Magnusson                                                       sc.type == STRING)
1677f219e013SMasahiro Yamada                    defaults_str_rows.append(row_str)
167890c36d8aSUlf Magnusson                    defaults_str_rows.append("  Condition: " +
167990c36d8aSUlf Magnusson                                               self._expr_val_str(cond_expr))
1680f219e013SMasahiro Yamada                defaults_str = "\n".join(defaults_str_rows)
1681f219e013SMasahiro Yamada
1682f219e013SMasahiro Yamada            # Build selects string
168390c36d8aSUlf Magnusson            if not sc.orig_selects:
1684f219e013SMasahiro Yamada                selects_str = " (no selects)"
1685f219e013SMasahiro Yamada            else:
1686f219e013SMasahiro Yamada                selects_str_rows = []
168790c36d8aSUlf Magnusson                for target, cond_expr in sc.orig_selects:
1688f219e013SMasahiro Yamada                    if cond_expr is None:
1689f219e013SMasahiro Yamada                        selects_str_rows.append(" {0}".format(target.name))
1690f219e013SMasahiro Yamada                    else:
169190c36d8aSUlf Magnusson                        selects_str_rows.append(
169290c36d8aSUlf Magnusson                          " {0} if {1}".format(target.name,
169390c36d8aSUlf Magnusson                                               self._expr_val_str(cond_expr)))
1694f219e013SMasahiro Yamada                selects_str = "\n".join(selects_str_rows)
1695f219e013SMasahiro Yamada
169690c36d8aSUlf Magnusson            res = _lines("Symbol " +
169790c36d8aSUlf Magnusson                           ("(no name)" if sc.name is None else sc.name),
169890c36d8aSUlf Magnusson                         "Type           : " + TYPENAME[sc.type],
169990c36d8aSUlf Magnusson                         "Value          : " + s(sc.get_value()),
170090c36d8aSUlf Magnusson                         "User value     : " + user_val_str,
170190c36d8aSUlf Magnusson                         "Visibility     : " + s(_get_visibility(sc)),
170290c36d8aSUlf Magnusson                         "Is choice item : " + BOOL_STR[sc.is_choice_sym],
170390c36d8aSUlf Magnusson                         "Is defined     : " + BOOL_STR[sc.is_defined_],
170490c36d8aSUlf Magnusson                         "Is from env.   : " + BOOL_STR[sc.is_from_env],
170590c36d8aSUlf Magnusson                         "Is special     : " + BOOL_STR[sc.is_special_] + "\n")
170690c36d8aSUlf Magnusson            if sc.ranges:
170790c36d8aSUlf Magnusson                res += _lines("Ranges:", ranges_str + "\n")
170890c36d8aSUlf Magnusson            res += _lines("Prompts:",
1709f219e013SMasahiro Yamada                          prompts_str,
1710f219e013SMasahiro Yamada                          "Default values:",
1711f219e013SMasahiro Yamada                          defaults_str,
1712f219e013SMasahiro Yamada                          "Selects:",
1713f219e013SMasahiro Yamada                          selects_str,
171490c36d8aSUlf Magnusson                          "Reverse (select-related) dependencies:",
171590c36d8aSUlf Magnusson                          " (no reverse dependencies)" if sc.rev_dep == "n"
171690c36d8aSUlf Magnusson                            else " " + self._expr_val_str(sc.rev_dep),
171790c36d8aSUlf Magnusson                          "Additional dependencies from enclosing menus "
171890c36d8aSUlf Magnusson                            "and ifs:",
1719f219e013SMasahiro Yamada                          additional_deps_str,
1720f219e013SMasahiro Yamada                          "Locations: " + locations_str)
1721f219e013SMasahiro Yamada
1722f219e013SMasahiro Yamada            return res
1723f219e013SMasahiro Yamada
1724f219e013SMasahiro Yamada        #
1725f219e013SMasahiro Yamada        # Choice-specific stuff
1726f219e013SMasahiro Yamada        #
1727f219e013SMasahiro Yamada
1728f219e013SMasahiro Yamada        # Build selected symbol string
1729f219e013SMasahiro Yamada        sel = sc.get_selection()
173090c36d8aSUlf Magnusson        sel_str = "(no selection)" if sel is None else sel.name
1731f219e013SMasahiro Yamada
1732f219e013SMasahiro Yamada        # Build default values string
173390c36d8aSUlf Magnusson        if not sc.def_exprs:
1734f219e013SMasahiro Yamada            defaults_str = " (no default values)"
1735f219e013SMasahiro Yamada        else:
1736f219e013SMasahiro Yamada            defaults_str_rows = []
173790c36d8aSUlf Magnusson            for sym, cond_expr in sc.orig_def_exprs:
1738f219e013SMasahiro Yamada                if cond_expr is None:
1739f219e013SMasahiro Yamada                    defaults_str_rows.append(" {0}".format(sym.name))
1740f219e013SMasahiro Yamada                else:
174190c36d8aSUlf Magnusson                    defaults_str_rows.append(" {0} if {1}".format(sym.name,
174290c36d8aSUlf Magnusson                                                self._expr_val_str(cond_expr)))
1743f219e013SMasahiro Yamada            defaults_str = "\n".join(defaults_str_rows)
1744f219e013SMasahiro Yamada
1745f219e013SMasahiro Yamada        # Build contained symbols string
174690c36d8aSUlf Magnusson        names = [sym.name for sym in sc.actual_symbols]
174790c36d8aSUlf Magnusson        syms_string = " ".join(names) if names else "(empty)"
1748f219e013SMasahiro Yamada
174990c36d8aSUlf Magnusson        return _lines("Choice",
175090c36d8aSUlf Magnusson                      "Name (for named choices): " +
175190c36d8aSUlf Magnusson                        ("(no name)" if sc.name is None else sc.name),
175290c36d8aSUlf Magnusson                      "Type            : " + TYPENAME[sc.type],
1753f219e013SMasahiro Yamada                      "Selected symbol : " + sel_str,
175490c36d8aSUlf Magnusson                      "User value      : " + user_val_str,
175590c36d8aSUlf Magnusson                      "Mode            : " + s(sc.get_mode()),
175690c36d8aSUlf Magnusson                      "Visibility      : " + s(_get_visibility(sc)),
175790c36d8aSUlf Magnusson                      "Optional        : " + BOOL_STR[sc.optional],
1758f219e013SMasahiro Yamada                      "Prompts:",
1759f219e013SMasahiro Yamada                      prompts_str,
1760f219e013SMasahiro Yamada                      "Defaults:",
1761f219e013SMasahiro Yamada                      defaults_str,
1762f219e013SMasahiro Yamada                      "Choice symbols:",
1763f219e013SMasahiro Yamada                      " " + syms_string,
176490c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
176590c36d8aSUlf Magnusson                        "ifs:",
1766f219e013SMasahiro Yamada                      additional_deps_str,
1767f219e013SMasahiro Yamada                      "Locations: " + locations_str)
1768f219e013SMasahiro Yamada
1769f219e013SMasahiro Yamada    def _warn(self, msg, filename=None, linenr=None):
1770f219e013SMasahiro Yamada        """For printing warnings to stderr."""
1771*0c69cd52SSimon Glass        msg = _build_msg("warning: " + msg, filename, linenr)
1772f219e013SMasahiro Yamada        if self.print_warnings:
1773*0c69cd52SSimon Glass            sys.stderr.write(msg + "\n")
1774*0c69cd52SSimon Glass        self._warnings.append(msg)
1775f219e013SMasahiro Yamada
177690c36d8aSUlf Magnussonclass Item(object):
1777f219e013SMasahiro Yamada
1778f219e013SMasahiro Yamada    """Base class for symbols and other Kconfig constructs. Subclasses are
1779f219e013SMasahiro Yamada    Symbol, Choice, Menu, and Comment."""
1780f219e013SMasahiro Yamada
1781f219e013SMasahiro Yamada    def is_symbol(self):
178290c36d8aSUlf Magnusson        """Returns True if the item is a symbol. Short for
1783f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Symbol)."""
1784f219e013SMasahiro Yamada        return isinstance(self, Symbol)
1785f219e013SMasahiro Yamada
1786f219e013SMasahiro Yamada    def is_choice(self):
178790c36d8aSUlf Magnusson        """Returns True if the item is a choice. Short for
1788f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Choice)."""
1789f219e013SMasahiro Yamada        return isinstance(self, Choice)
1790f219e013SMasahiro Yamada
1791f219e013SMasahiro Yamada    def is_menu(self):
179290c36d8aSUlf Magnusson        """Returns True if the item is a menu. Short for
1793f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Menu)."""
1794f219e013SMasahiro Yamada        return isinstance(self, Menu)
1795f219e013SMasahiro Yamada
1796f219e013SMasahiro Yamada    def is_comment(self):
179790c36d8aSUlf Magnusson        """Returns True if the item is a comment. Short for
1798f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Comment)."""
1799f219e013SMasahiro Yamada        return isinstance(self, Comment)
1800f219e013SMasahiro Yamada
180190c36d8aSUlf Magnussonclass Symbol(Item):
1802f219e013SMasahiro Yamada
1803f219e013SMasahiro Yamada    """Represents a configuration symbol - e.g. FOO for
1804f219e013SMasahiro Yamada
1805f219e013SMasahiro Yamada    config FOO
1806f219e013SMasahiro Yamada        ..."""
1807f219e013SMasahiro Yamada
1808f219e013SMasahiro Yamada    #
1809f219e013SMasahiro Yamada    # Public interface
1810f219e013SMasahiro Yamada    #
1811f219e013SMasahiro Yamada
181290c36d8aSUlf Magnusson    def get_config(self):
181390c36d8aSUlf Magnusson        """Returns the Config instance this symbol is from."""
181490c36d8aSUlf Magnusson        return self.config
181590c36d8aSUlf Magnusson
181690c36d8aSUlf Magnusson    def get_name(self):
181790c36d8aSUlf Magnusson        """Returns the name of the symbol."""
181890c36d8aSUlf Magnusson        return self.name
181990c36d8aSUlf Magnusson
182090c36d8aSUlf Magnusson    def get_type(self):
182190c36d8aSUlf Magnusson        """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE,
182290c36d8aSUlf Magnusson        STRING, HEX, or INT. These are defined at the top level of the module,
182390c36d8aSUlf Magnusson        so you'd do something like
182490c36d8aSUlf Magnusson
182590c36d8aSUlf Magnusson        if sym.get_type() == kconfiglib.STRING:
182690c36d8aSUlf Magnusson            ..."""
182790c36d8aSUlf Magnusson        return self.type
182890c36d8aSUlf Magnusson
182990c36d8aSUlf Magnusson    def get_prompts(self):
183090c36d8aSUlf Magnusson        """Returns a list of prompts defined for the symbol, in the order they
183190c36d8aSUlf Magnusson        appear in the configuration files. Returns the empty list for symbols
183290c36d8aSUlf Magnusson        with no prompt.
183390c36d8aSUlf Magnusson
183490c36d8aSUlf Magnusson        This list will have a single entry for the vast majority of symbols
183590c36d8aSUlf Magnusson        having prompts, but having multiple prompts for a single symbol is
183690c36d8aSUlf Magnusson        possible through having multiple 'config' entries for it."""
183790c36d8aSUlf Magnusson        return [prompt for prompt, _ in self.orig_prompts]
183890c36d8aSUlf Magnusson
183990c36d8aSUlf Magnusson    def get_help(self):
184090c36d8aSUlf Magnusson        """Returns the help text of the symbol, or None if the symbol has no
184190c36d8aSUlf Magnusson        help text."""
184290c36d8aSUlf Magnusson        return self.help
184390c36d8aSUlf Magnusson
184490c36d8aSUlf Magnusson    def get_parent(self):
184590c36d8aSUlf Magnusson        """Returns the menu or choice statement that contains the symbol, or
184690c36d8aSUlf Magnusson        None if the symbol is at the top level. Note that if statements are
184790c36d8aSUlf Magnusson        treated as syntactic and do not have an explicit class
184890c36d8aSUlf Magnusson        representation."""
184990c36d8aSUlf Magnusson        return self.parent
185090c36d8aSUlf Magnusson
185190c36d8aSUlf Magnusson    def get_def_locations(self):
185290c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
185390c36d8aSUlf Magnusson        and linenr (int) represent a location where the symbol is defined. For
185490c36d8aSUlf Magnusson        the vast majority of symbols this list will only contain one element.
185590c36d8aSUlf Magnusson        For the following Kconfig, FOO would get two entries: the lines marked
185690c36d8aSUlf Magnusson        with *.
185790c36d8aSUlf Magnusson
185890c36d8aSUlf Magnusson        config FOO *
185990c36d8aSUlf Magnusson            bool "foo prompt 1"
186090c36d8aSUlf Magnusson
186190c36d8aSUlf Magnusson        config FOO *
186290c36d8aSUlf Magnusson            bool "foo prompt 2"
186390c36d8aSUlf Magnusson        """
186490c36d8aSUlf Magnusson        return self.def_locations
186590c36d8aSUlf Magnusson
186690c36d8aSUlf Magnusson    def get_ref_locations(self):
186790c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
186890c36d8aSUlf Magnusson        and linenr (int) represent a location where the symbol is referenced in
186990c36d8aSUlf Magnusson        the configuration. For example, the lines marked by * would be included
187090c36d8aSUlf Magnusson        for FOO below:
187190c36d8aSUlf Magnusson
187290c36d8aSUlf Magnusson        config A
187390c36d8aSUlf Magnusson            bool
187490c36d8aSUlf Magnusson            default BAR || FOO *
187590c36d8aSUlf Magnusson
187690c36d8aSUlf Magnusson        config B
187790c36d8aSUlf Magnusson            tristate
187890c36d8aSUlf Magnusson            depends on FOO *
187990c36d8aSUlf Magnusson            default m if FOO *
188090c36d8aSUlf Magnusson
188190c36d8aSUlf Magnusson        if FOO *
188290c36d8aSUlf Magnusson            config A
188390c36d8aSUlf Magnusson                bool "A"
188490c36d8aSUlf Magnusson        endif
188590c36d8aSUlf Magnusson
188690c36d8aSUlf Magnusson        config FOO (definition not included)
188790c36d8aSUlf Magnusson            bool
188890c36d8aSUlf Magnusson        """
188990c36d8aSUlf Magnusson        return self.ref_locations
189090c36d8aSUlf Magnusson
1891f219e013SMasahiro Yamada    def get_value(self):
1892f219e013SMasahiro Yamada        """Calculate and return the value of the symbol. See also
1893f219e013SMasahiro Yamada        Symbol.set_user_value()."""
1894f219e013SMasahiro Yamada
189590c36d8aSUlf Magnusson        if self.cached_val is not None:
189690c36d8aSUlf Magnusson            return self.cached_val
1897f219e013SMasahiro Yamada
1898f219e013SMasahiro Yamada        # As a quirk of Kconfig, undefined symbols get their name as their
1899f219e013SMasahiro Yamada        # value. This is why things like "FOO = bar" work for seeing if FOO has
1900f219e013SMasahiro Yamada        # the value "bar".
1901f219e013SMasahiro Yamada        if self.type == UNKNOWN:
190290c36d8aSUlf Magnusson            self.cached_val = self.name
1903f219e013SMasahiro Yamada            return self.name
1904f219e013SMasahiro Yamada
190590c36d8aSUlf Magnusson        new_val = DEFAULT_VALUE[self.type]
190690c36d8aSUlf Magnusson        vis = _get_visibility(self)
1907f219e013SMasahiro Yamada
190890c36d8aSUlf Magnusson        # This is easiest to calculate together with the value
190990c36d8aSUlf Magnusson        self.write_to_conf = False
1910f219e013SMasahiro Yamada
1911f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
1912f219e013SMasahiro Yamada            # The visibility and mode (modules-only or single-selection) of
191390c36d8aSUlf Magnusson            # choice items will be taken into account in _get_visibility()
191490c36d8aSUlf Magnusson            if self.is_choice_sym:
1915f219e013SMasahiro Yamada                if vis != "n":
1916f219e013SMasahiro Yamada                    choice = self.parent
1917f219e013SMasahiro Yamada                    mode = choice.get_mode()
1918f219e013SMasahiro Yamada
1919f219e013SMasahiro Yamada                    self.write_to_conf = (mode != "n")
1920f219e013SMasahiro Yamada
1921f219e013SMasahiro Yamada                    if mode == "y":
192290c36d8aSUlf Magnusson                        if choice.get_selection() is self:
192390c36d8aSUlf Magnusson                            new_val = "y"
192490c36d8aSUlf Magnusson                        else:
192590c36d8aSUlf Magnusson                            new_val = "n"
1926f219e013SMasahiro Yamada                    elif mode == "m":
1927f219e013SMasahiro Yamada                        if self.user_val == "m" or self.user_val == "y":
1928f219e013SMasahiro Yamada                            new_val = "m"
1929f219e013SMasahiro Yamada
1930f219e013SMasahiro Yamada            else:
193190c36d8aSUlf Magnusson                # If the symbol is visible and has a user value, use that.
193290c36d8aSUlf Magnusson                # Otherwise, look at defaults.
1933f219e013SMasahiro Yamada                use_defaults = True
1934f219e013SMasahiro Yamada
1935f219e013SMasahiro Yamada                if vis != "n":
1936f219e013SMasahiro Yamada                    self.write_to_conf = True
1937f219e013SMasahiro Yamada                    if self.user_val is not None:
1938f219e013SMasahiro Yamada                        new_val = self.config._eval_min(self.user_val, vis)
1939f219e013SMasahiro Yamada                        use_defaults = False
1940f219e013SMasahiro Yamada
1941f219e013SMasahiro Yamada                if use_defaults:
194290c36d8aSUlf Magnusson                    for val_expr, cond_expr in self.def_exprs:
1943f219e013SMasahiro Yamada                        cond_eval = self.config._eval_expr(cond_expr)
1944f219e013SMasahiro Yamada                        if cond_eval != "n":
1945f219e013SMasahiro Yamada                            self.write_to_conf = True
194690c36d8aSUlf Magnusson                            new_val = self.config._eval_min(val_expr,
194790c36d8aSUlf Magnusson                                                            cond_eval)
1948f219e013SMasahiro Yamada                            break
1949f219e013SMasahiro Yamada
195090c36d8aSUlf Magnusson                # Reverse (select-related) dependencies take precedence
1951f219e013SMasahiro Yamada                rev_dep_val = self.config._eval_expr(self.rev_dep)
1952f219e013SMasahiro Yamada                if rev_dep_val != "n":
1953f219e013SMasahiro Yamada                    self.write_to_conf = True
1954f219e013SMasahiro Yamada                    new_val = self.config._eval_max(new_val, rev_dep_val)
1955f219e013SMasahiro Yamada
1956f219e013SMasahiro Yamada            # Promote "m" to "y" for booleans
1957f219e013SMasahiro Yamada            if new_val == "m" and self.type == BOOL:
1958f219e013SMasahiro Yamada                new_val = "y"
1959f219e013SMasahiro Yamada
196090c36d8aSUlf Magnusson        elif self.type == INT or self.type == HEX:
1961f219e013SMasahiro Yamada            has_active_range = False
1962f219e013SMasahiro Yamada            low = None
1963f219e013SMasahiro Yamada            high = None
1964f219e013SMasahiro Yamada            use_defaults = True
1965f219e013SMasahiro Yamada
1966f219e013SMasahiro Yamada            base = 16 if self.type == HEX else 10
1967f219e013SMasahiro Yamada
196890c36d8aSUlf Magnusson            for l, h, cond_expr in self.ranges:
1969f219e013SMasahiro Yamada                if self.config._eval_expr(cond_expr) != "n":
1970f219e013SMasahiro Yamada                    has_active_range = True
1971f219e013SMasahiro Yamada
197290c36d8aSUlf Magnusson                    low_str = _str_val(l)
197390c36d8aSUlf Magnusson                    high_str = _str_val(h)
1974f219e013SMasahiro Yamada                    low = int(low_str, base) if \
1975f219e013SMasahiro Yamada                      _is_base_n(low_str, base) else 0
1976f219e013SMasahiro Yamada                    high = int(high_str, base) if \
1977f219e013SMasahiro Yamada                      _is_base_n(high_str, base) else 0
1978f219e013SMasahiro Yamada
1979f219e013SMasahiro Yamada                    break
1980f219e013SMasahiro Yamada
1981f219e013SMasahiro Yamada            if vis != "n":
1982f219e013SMasahiro Yamada                self.write_to_conf = True
1983f219e013SMasahiro Yamada
1984f219e013SMasahiro Yamada                if self.user_val is not None and \
1985f219e013SMasahiro Yamada                   _is_base_n(self.user_val, base) and \
1986f219e013SMasahiro Yamada                   (not has_active_range or
1987f219e013SMasahiro Yamada                    low <= int(self.user_val, base) <= high):
1988f219e013SMasahiro Yamada
1989f219e013SMasahiro Yamada                    # If the user value is OK, it is stored in exactly the same
1990f219e013SMasahiro Yamada                    # form as specified in the assignment (with or without
1991f219e013SMasahiro Yamada                    # "0x", etc).
1992f219e013SMasahiro Yamada
1993f219e013SMasahiro Yamada                    use_defaults = False
1994f219e013SMasahiro Yamada                    new_val = self.user_val
1995f219e013SMasahiro Yamada
1996f219e013SMasahiro Yamada            if use_defaults:
199790c36d8aSUlf Magnusson                for val_expr, cond_expr in self.def_exprs:
1998f219e013SMasahiro Yamada                    if self.config._eval_expr(cond_expr) != "n":
1999f219e013SMasahiro Yamada                        self.write_to_conf = True
2000f219e013SMasahiro Yamada
2001f219e013SMasahiro Yamada                        # If the default value is OK, it is stored in exactly
2002f219e013SMasahiro Yamada                        # the same form as specified. Otherwise, it is clamped
2003f219e013SMasahiro Yamada                        # to the range, and the output has "0x" as appropriate
2004f219e013SMasahiro Yamada                        # for the type.
2005f219e013SMasahiro Yamada
200690c36d8aSUlf Magnusson                        new_val = _str_val(val_expr)
2007f219e013SMasahiro Yamada
2008f219e013SMasahiro Yamada                        if _is_base_n(new_val, base):
2009f219e013SMasahiro Yamada                            new_val_num = int(new_val, base)
2010f219e013SMasahiro Yamada                            if has_active_range:
2011f219e013SMasahiro Yamada                                clamped_val = None
2012f219e013SMasahiro Yamada
2013f219e013SMasahiro Yamada                                if new_val_num < low:
2014f219e013SMasahiro Yamada                                    clamped_val = low
2015f219e013SMasahiro Yamada                                elif new_val_num > high:
2016f219e013SMasahiro Yamada                                    clamped_val = high
2017f219e013SMasahiro Yamada
2018f219e013SMasahiro Yamada                                if clamped_val is not None:
2019f219e013SMasahiro Yamada                                    new_val = (hex(clamped_val) if \
2020f219e013SMasahiro Yamada                                      self.type == HEX else str(clamped_val))
2021f219e013SMasahiro Yamada
2022f219e013SMasahiro Yamada                            break
2023f219e013SMasahiro Yamada                else: # For the for loop
2024f219e013SMasahiro Yamada                    # If no user value or default kicks in but the hex/int has
2025f219e013SMasahiro Yamada                    # an active range, then the low end of the range is used,
2026f219e013SMasahiro Yamada                    # provided it's > 0, with "0x" prepended as appropriate.
2027f219e013SMasahiro Yamada                    if has_active_range and low > 0:
2028f219e013SMasahiro Yamada                        new_val = (hex(low) if self.type == HEX else str(low))
2029f219e013SMasahiro Yamada
203090c36d8aSUlf Magnusson        elif self.type == STRING:
203190c36d8aSUlf Magnusson            use_defaults = True
203290c36d8aSUlf Magnusson
203390c36d8aSUlf Magnusson            if vis != "n":
203490c36d8aSUlf Magnusson                self.write_to_conf = True
203590c36d8aSUlf Magnusson                if self.user_val is not None:
203690c36d8aSUlf Magnusson                    new_val = self.user_val
203790c36d8aSUlf Magnusson                    use_defaults = False
203890c36d8aSUlf Magnusson
203990c36d8aSUlf Magnusson            if use_defaults:
204090c36d8aSUlf Magnusson                for val_expr, cond_expr in self.def_exprs:
204190c36d8aSUlf Magnusson                    if self.config._eval_expr(cond_expr) != "n":
204290c36d8aSUlf Magnusson                        self.write_to_conf = True
204390c36d8aSUlf Magnusson                        new_val = _str_val(val_expr)
204490c36d8aSUlf Magnusson                        break
204590c36d8aSUlf Magnusson
204690c36d8aSUlf Magnusson        self.cached_val = new_val
2047f219e013SMasahiro Yamada        return new_val
2048f219e013SMasahiro Yamada
2049f219e013SMasahiro Yamada    def get_user_value(self):
2050f219e013SMasahiro Yamada        """Returns the value assigned to the symbol in a .config or via
205190c36d8aSUlf Magnusson        Symbol.set_user_value() (provided the value was valid for the type of
205290c36d8aSUlf Magnusson        the symbol). Returns None in case of no user value."""
2053f219e013SMasahiro Yamada        return self.user_val
2054f219e013SMasahiro Yamada
2055f219e013SMasahiro Yamada    def get_upper_bound(self):
2056f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2057f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns None.
2058f219e013SMasahiro Yamada
2059f219e013SMasahiro Yamada        Otherwise, returns the highest value the symbol can be set to with
206090c36d8aSUlf Magnusson        Symbol.set_user_value() (that will not be truncated): one of "m" or
206190c36d8aSUlf Magnusson        "y", arranged from lowest to highest. This corresponds to the highest
206290c36d8aSUlf Magnusson        value the symbol could be given in e.g. the 'make menuconfig'
206390c36d8aSUlf Magnusson        interface.
2064f219e013SMasahiro Yamada
2065f219e013SMasahiro Yamada        See also the tri_less*() and tri_greater*() functions, which could come
2066f219e013SMasahiro Yamada        in handy."""
2067f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2068f219e013SMasahiro Yamada            return None
2069f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
207090c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
207190c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
207290c36d8aSUlf Magnusson            return None
207390c36d8aSUlf Magnusson        vis = _get_visibility(self)
207490c36d8aSUlf Magnusson        if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]:
2075f219e013SMasahiro Yamada            return vis
2076f219e013SMasahiro Yamada        return None
2077f219e013SMasahiro Yamada
2078f219e013SMasahiro Yamada    def get_lower_bound(self):
2079f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2080f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns None.
2081f219e013SMasahiro Yamada
2082f219e013SMasahiro Yamada        Otherwise, returns the lowest value the symbol can be set to with
208390c36d8aSUlf Magnusson        Symbol.set_user_value() (that will not be truncated): one of "n" or
208490c36d8aSUlf Magnusson        "m", arranged from lowest to highest. This corresponds to the lowest
208590c36d8aSUlf Magnusson        value the symbol could be given in e.g. the 'make menuconfig'
208690c36d8aSUlf Magnusson        interface.
2087f219e013SMasahiro Yamada
2088f219e013SMasahiro Yamada        See also the tri_less*() and tri_greater*() functions, which could come
2089f219e013SMasahiro Yamada        in handy."""
2090f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2091f219e013SMasahiro Yamada            return None
2092f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
209390c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
209490c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
209590c36d8aSUlf Magnusson            return None
209690c36d8aSUlf Magnusson        if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]:
2097f219e013SMasahiro Yamada            return rev_dep
2098f219e013SMasahiro Yamada        return None
2099f219e013SMasahiro Yamada
2100f219e013SMasahiro Yamada    def get_assignable_values(self):
2101f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2102f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns the empty list.
2103f219e013SMasahiro Yamada
2104f219e013SMasahiro Yamada        Otherwise, returns a list containing the user values that can be
2105f219e013SMasahiro Yamada        assigned to the symbol (that won't be truncated). Usage example:
2106f219e013SMasahiro Yamada
2107f219e013SMasahiro Yamada        if "m" in sym.get_assignable_values():
2108f219e013SMasahiro Yamada            sym.set_user_value("m")
2109f219e013SMasahiro Yamada
2110f219e013SMasahiro Yamada        This is basically a more convenient interface to
2111f219e013SMasahiro Yamada        get_lower/upper_bound() when wanting to test if a particular tristate
2112f219e013SMasahiro Yamada        value can be assigned."""
2113f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2114f219e013SMasahiro Yamada            return []
2115f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
211690c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
211790c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
211890c36d8aSUlf Magnusson            return []
211990c36d8aSUlf Magnusson        res = ["n", "m", "y"][TRI_TO_INT[rev_dep] :
212090c36d8aSUlf Magnusson                              TRI_TO_INT[_get_visibility(self)] + 1]
2121f219e013SMasahiro Yamada        return res if len(res) > 1 else []
2122f219e013SMasahiro Yamada
2123f219e013SMasahiro Yamada    def get_visibility(self):
2124f219e013SMasahiro Yamada        """Returns the visibility of the symbol: one of "n", "m" or "y". For
2125f219e013SMasahiro Yamada        bool and tristate symbols, this is an upper bound on the value users
2126f219e013SMasahiro Yamada        can set for the symbol. For other types of symbols, a visibility of "n"
2127f219e013SMasahiro Yamada        means the user value will be ignored. A visibility of "n" corresponds
2128f219e013SMasahiro Yamada        to not being visible in the 'make *config' interfaces.
2129f219e013SMasahiro Yamada
2130f219e013SMasahiro Yamada        Example (assuming we're running with modules enabled -- i.e., MODULES
2131f219e013SMasahiro Yamada        set to 'y'):
2132f219e013SMasahiro Yamada
2133f219e013SMasahiro Yamada        # Assume this has been assigned 'n'
2134f219e013SMasahiro Yamada        config N_SYM
2135f219e013SMasahiro Yamada            tristate "N_SYM"
2136f219e013SMasahiro Yamada
2137f219e013SMasahiro Yamada        # Assume this has been assigned 'm'
2138f219e013SMasahiro Yamada        config M_SYM
2139f219e013SMasahiro Yamada            tristate "M_SYM"
2140f219e013SMasahiro Yamada
2141f219e013SMasahiro Yamada        # Has visibility 'n'
2142f219e013SMasahiro Yamada        config A
2143f219e013SMasahiro Yamada            tristate "A"
2144f219e013SMasahiro Yamada            depends on N_SYM
2145f219e013SMasahiro Yamada
2146f219e013SMasahiro Yamada        # Has visibility 'm'
2147f219e013SMasahiro Yamada        config B
2148f219e013SMasahiro Yamada            tristate "B"
2149f219e013SMasahiro Yamada            depends on M_SYM
2150f219e013SMasahiro Yamada
2151f219e013SMasahiro Yamada        # Has visibility 'y'
2152f219e013SMasahiro Yamada        config C
2153f219e013SMasahiro Yamada            tristate "C"
2154f219e013SMasahiro Yamada
2155f219e013SMasahiro Yamada        # Has no prompt, and hence visibility 'n'
2156f219e013SMasahiro Yamada        config D
2157f219e013SMasahiro Yamada            tristate
2158f219e013SMasahiro Yamada
2159f219e013SMasahiro Yamada        Having visibility be tri-valued ensures that e.g. a symbol cannot be
2160f219e013SMasahiro Yamada        set to "y" by the user if it depends on a symbol with value "m", which
2161f219e013SMasahiro Yamada        wouldn't be safe.
2162f219e013SMasahiro Yamada
2163f219e013SMasahiro Yamada        You should probably look at get_lower/upper_bound(),
2164f219e013SMasahiro Yamada        get_assignable_values() and is_modifiable() before using this."""
216590c36d8aSUlf Magnusson        return _get_visibility(self)
2166f219e013SMasahiro Yamada
2167f219e013SMasahiro Yamada    def get_referenced_symbols(self, refs_from_enclosing=False):
2168f219e013SMasahiro Yamada        """Returns the set() of all symbols referenced by this symbol. For
2169f219e013SMasahiro Yamada        example, the symbol defined by
2170f219e013SMasahiro Yamada
2171f219e013SMasahiro Yamada        config FOO
2172f219e013SMasahiro Yamada            bool
2173f219e013SMasahiro Yamada            prompt "foo" if A && B
2174f219e013SMasahiro Yamada            default C if D
2175f219e013SMasahiro Yamada            depends on E
2176f219e013SMasahiro Yamada            select F if G
2177f219e013SMasahiro Yamada
2178f219e013SMasahiro Yamada        references the symbols A through G.
2179f219e013SMasahiro Yamada
218090c36d8aSUlf Magnusson        refs_from_enclosing (default: False): If True, the symbols referenced
218190c36d8aSUlf Magnusson           by enclosing menus and ifs will be included in the result."""
218290c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
218390c36d8aSUlf Magnusson               self.referenced_syms
2184f219e013SMasahiro Yamada
2185f219e013SMasahiro Yamada    def get_selected_symbols(self):
2186f219e013SMasahiro Yamada        """Returns the set() of all symbols X for which this symbol has a
2187f219e013SMasahiro Yamada        'select X' or 'select X if Y' (regardless of whether Y is satisfied or
2188f219e013SMasahiro Yamada        not). This is a subset of the symbols returned by
2189f219e013SMasahiro Yamada        get_referenced_symbols()."""
2190f219e013SMasahiro Yamada        return self.selected_syms
2191f219e013SMasahiro Yamada
219290c36d8aSUlf Magnusson    def set_user_value(self, v):
219390c36d8aSUlf Magnusson        """Sets the user value of the symbol.
2194f219e013SMasahiro Yamada
219590c36d8aSUlf Magnusson        Equal in effect to assigning the value to the symbol within a .config
219690c36d8aSUlf Magnusson        file. Use get_lower/upper_bound() or get_assignable_values() to find
219790c36d8aSUlf Magnusson        the range of currently assignable values for bool and tristate symbols;
219890c36d8aSUlf Magnusson        setting values outside this range will cause the user value to differ
219990c36d8aSUlf Magnusson        from the result of Symbol.get_value() (be truncated). Values that are
220090c36d8aSUlf Magnusson        invalid for the type (such as a_bool.set_user_value("foo")) are
220190c36d8aSUlf Magnusson        ignored, and a warning is emitted if an attempt is made to assign such
220290c36d8aSUlf Magnusson        a value.
2203f219e013SMasahiro Yamada
220490c36d8aSUlf Magnusson        For any type of symbol, is_modifiable() can be used to check if a user
220590c36d8aSUlf Magnusson        value will currently have any effect on the symbol, as determined by
220690c36d8aSUlf Magnusson        its visibility and range of assignable values. Any value that is valid
220790c36d8aSUlf Magnusson        for the type (bool, tristate, etc.) will end up being reflected in
220890c36d8aSUlf Magnusson        get_user_value() though, and might have an effect later if conditions
220990c36d8aSUlf Magnusson        change. To get rid of the user value, use unset_user_value().
2210f219e013SMasahiro Yamada
221190c36d8aSUlf Magnusson        Any symbols dependent on the symbol are (recursively) invalidated, so
221290c36d8aSUlf Magnusson        things will just work with regards to dependencies.
2213f219e013SMasahiro Yamada
221490c36d8aSUlf Magnusson        v: The user value to give to the symbol."""
221590c36d8aSUlf Magnusson        self._set_user_value_no_invalidate(v, False)
2216f219e013SMasahiro Yamada
221790c36d8aSUlf Magnusson        # There might be something more efficient you could do here, but play
221890c36d8aSUlf Magnusson        # it safe.
221990c36d8aSUlf Magnusson        if self.name == "MODULES":
222090c36d8aSUlf Magnusson            self.config._invalidate_all()
222190c36d8aSUlf Magnusson            return
2222f219e013SMasahiro Yamada
222390c36d8aSUlf Magnusson        self._invalidate()
222490c36d8aSUlf Magnusson        self._invalidate_dependent()
2225f219e013SMasahiro Yamada
222690c36d8aSUlf Magnusson    def unset_user_value(self):
222790c36d8aSUlf Magnusson        """Resets the user value of the symbol, as if the symbol had never
222890c36d8aSUlf Magnusson        gotten a user value via Config.load_config() or
222990c36d8aSUlf Magnusson        Symbol.set_user_value()."""
223090c36d8aSUlf Magnusson        self._unset_user_value_no_recursive_invalidate()
223190c36d8aSUlf Magnusson        self._invalidate_dependent()
2232f219e013SMasahiro Yamada
2233f219e013SMasahiro Yamada    def is_modifiable(self):
2234f219e013SMasahiro Yamada        """Returns True if the value of the symbol could be modified by calling
223590c36d8aSUlf Magnusson        Symbol.set_user_value().
2236f219e013SMasahiro Yamada
2237f219e013SMasahiro Yamada        For bools and tristates, this corresponds to the symbol being visible
2238f219e013SMasahiro Yamada        in the 'make menuconfig' interface and not already being pinned to a
2239f219e013SMasahiro Yamada        specific value (e.g. because it is selected by another symbol).
2240f219e013SMasahiro Yamada
2241f219e013SMasahiro Yamada        For strings and numbers, this corresponds to just being visible. (See
2242f219e013SMasahiro Yamada        Symbol.get_visibility().)"""
2243f219e013SMasahiro Yamada        if self.is_special_:
2244f219e013SMasahiro Yamada            return False
2245f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
2246f219e013SMasahiro Yamada            rev_dep = self.config._eval_expr(self.rev_dep)
224790c36d8aSUlf Magnusson            # A bool selected to "m" gets promoted to "y", pinning it
224890c36d8aSUlf Magnusson            if rev_dep == "m" and self.type == BOOL:
224990c36d8aSUlf Magnusson                return False
225090c36d8aSUlf Magnusson            return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]
225190c36d8aSUlf Magnusson        return _get_visibility(self) != "n"
2252f219e013SMasahiro Yamada
2253f219e013SMasahiro Yamada    def is_defined(self):
2254f219e013SMasahiro Yamada        """Returns False if the symbol is referred to in the Kconfig but never
225590c36d8aSUlf Magnusson        actually defined."""
2256f219e013SMasahiro Yamada        return self.is_defined_
2257f219e013SMasahiro Yamada
2258f219e013SMasahiro Yamada    def is_special(self):
2259f219e013SMasahiro Yamada        """Returns True if the symbol is one of the special symbols n, m, y, or
226090c36d8aSUlf Magnusson        UNAME_RELEASE, or gets its value from the environment."""
2261f219e013SMasahiro Yamada        return self.is_special_
2262f219e013SMasahiro Yamada
2263f219e013SMasahiro Yamada    def is_from_environment(self):
226490c36d8aSUlf Magnusson        """Returns True if the symbol gets its value from the environment."""
2265f219e013SMasahiro Yamada        return self.is_from_env
2266f219e013SMasahiro Yamada
2267f219e013SMasahiro Yamada    def has_ranges(self):
2268f219e013SMasahiro Yamada        """Returns True if the symbol is of type INT or HEX and has ranges that
226990c36d8aSUlf Magnusson        limit what values it can take on."""
227090c36d8aSUlf Magnusson        return bool(self.ranges)
2271f219e013SMasahiro Yamada
2272f219e013SMasahiro Yamada    def is_choice_symbol(self):
2273f219e013SMasahiro Yamada        """Returns True if the symbol is in a choice statement and is an actual
227490c36d8aSUlf Magnusson        choice symbol (see Choice.get_symbols())."""
227590c36d8aSUlf Magnusson        return self.is_choice_sym
2276f219e013SMasahiro Yamada
2277f219e013SMasahiro Yamada    def is_choice_selection(self):
2278f219e013SMasahiro Yamada        """Returns True if the symbol is contained in a choice statement and is
227990c36d8aSUlf Magnusson        the selected item. Equivalent to
228090c36d8aSUlf Magnusson
228190c36d8aSUlf Magnusson        sym.is_choice_symbol() and sym.get_parent().get_selection() is sym"""
228290c36d8aSUlf Magnusson        return self.is_choice_sym and self.parent.get_selection() is self
2283f219e013SMasahiro Yamada
22849d01b787SMasahiro Yamada    def is_allnoconfig_y(self):
228590c36d8aSUlf Magnusson        """Returns True if the symbol has the 'allnoconfig_y' option set."""
22869d01b787SMasahiro Yamada        return self.allnoconfig_y
22879d01b787SMasahiro Yamada
2288f219e013SMasahiro Yamada    def __str__(self):
2289f219e013SMasahiro Yamada        """Returns a string containing various information about the symbol."""
2290f219e013SMasahiro Yamada        return self.config._get_sym_or_choice_str(self)
2291f219e013SMasahiro Yamada
2292f219e013SMasahiro Yamada    #
2293f219e013SMasahiro Yamada    # Private methods
2294f219e013SMasahiro Yamada    #
2295f219e013SMasahiro Yamada
2296f219e013SMasahiro Yamada    def __init__(self):
2297f219e013SMasahiro Yamada        """Symbol constructor -- not intended to be called directly by
229890c36d8aSUlf Magnusson        Kconfiglib clients."""
2299f219e013SMasahiro Yamada
2300f219e013SMasahiro Yamada        self.name = None
2301f219e013SMasahiro Yamada        self.type = UNKNOWN
230290c36d8aSUlf Magnusson        self.prompts = []
230390c36d8aSUlf Magnusson        self.def_exprs = [] # 'default' properties
230490c36d8aSUlf Magnusson        self.ranges = [] # 'range' properties (for int and hex)
230590c36d8aSUlf Magnusson        self.help = None # Help text
230690c36d8aSUlf Magnusson        self.rev_dep = "n" # Reverse (select-related) dependencies
230790c36d8aSUlf Magnusson        self.config = None
230890c36d8aSUlf Magnusson        self.parent = None
2309f219e013SMasahiro Yamada
231090c36d8aSUlf Magnusson        self.user_val = None # Value set by user
2311f219e013SMasahiro Yamada
2312f219e013SMasahiro Yamada        # The prompt, default value and select conditions without any
231390c36d8aSUlf Magnusson        # dependencies from menus and ifs propagated to them
2314f219e013SMasahiro Yamada        self.orig_prompts = []
2315f219e013SMasahiro Yamada        self.orig_def_exprs = []
2316f219e013SMasahiro Yamada        self.orig_selects = []
2317f219e013SMasahiro Yamada
231890c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2319f219e013SMasahiro Yamada        self.deps_from_containing = None
2320f219e013SMasahiro Yamada        # The set of symbols referenced by this symbol (see
2321f219e013SMasahiro Yamada        # get_referenced_symbols())
2322f219e013SMasahiro Yamada        self.referenced_syms = set()
2323f219e013SMasahiro Yamada        # The set of symbols selected by this symbol (see
2324f219e013SMasahiro Yamada        # get_selected_symbols())
2325f219e013SMasahiro Yamada        self.selected_syms = set()
2326f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
232790c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2328f219e013SMasahiro Yamada        self.all_referenced_syms = set()
2329f219e013SMasahiro Yamada
2330f219e013SMasahiro Yamada        # This records only dependencies specified with 'depends on'. Needed
2331f219e013SMasahiro Yamada        # when determining actual choice items (hrrrr...). See also
2332f219e013SMasahiro Yamada        # Choice._determine_actual_symbols().
2333f219e013SMasahiro Yamada        self.menu_dep = None
2334f219e013SMasahiro Yamada
2335f219e013SMasahiro Yamada        # See Symbol.get_ref/def_locations().
2336f219e013SMasahiro Yamada        self.def_locations = []
2337f219e013SMasahiro Yamada        self.ref_locations = []
2338f219e013SMasahiro Yamada
233990c36d8aSUlf Magnusson        # Populated in Config._build_dep() after parsing. Links the symbol to
234090c36d8aSUlf Magnusson        # the symbols that immediately depend on it (in a caching/invalidation
234190c36d8aSUlf Magnusson        # sense). The total set of dependent symbols for the symbol (the
234290c36d8aSUlf Magnusson        # transitive closure) is calculated on an as-needed basis in
234390c36d8aSUlf Magnusson        # _get_dependent().
234490c36d8aSUlf Magnusson        self.dep = set()
2345f219e013SMasahiro Yamada
234690c36d8aSUlf Magnusson        # Cached values
2347f219e013SMasahiro Yamada
2348f219e013SMasahiro Yamada        # Caches the calculated value
234990c36d8aSUlf Magnusson        self.cached_val = None
235090c36d8aSUlf Magnusson        # Caches the visibility, which acts as an upper bound on the value
235190c36d8aSUlf Magnusson        self.cached_visibility = None
2352f219e013SMasahiro Yamada        # Caches the total list of dependent symbols. Calculated in
2353f219e013SMasahiro Yamada        # _get_dependent().
2354f219e013SMasahiro Yamada        self.cached_deps = None
2355f219e013SMasahiro Yamada
235690c36d8aSUlf Magnusson        # Flags
235790c36d8aSUlf Magnusson
2358f219e013SMasahiro Yamada        # Does the symbol have an entry in the Kconfig file? The trailing
2359f219e013SMasahiro Yamada        # underscore avoids a collision with is_defined().
2360f219e013SMasahiro Yamada        self.is_defined_ = False
236190c36d8aSUlf Magnusson        # Should the symbol get an entry in .config?
236290c36d8aSUlf Magnusson        self.write_to_conf = False
236390c36d8aSUlf Magnusson        # Set to true when _make_conf() is called on a symbol, so that symbols
236490c36d8aSUlf Magnusson        # defined in multiple locations only get one .config entry. We need to
236590c36d8aSUlf Magnusson        # reset it prior to writing out a new .config.
236690c36d8aSUlf Magnusson        self.already_written = False
236790c36d8aSUlf Magnusson        # This is set to True for "actual" choice symbols; see
236890c36d8aSUlf Magnusson        # Choice._determine_actual_symbols().
236990c36d8aSUlf Magnusson        self.is_choice_sym = False
2370f219e013SMasahiro Yamada        # Does the symbol get its value in some special way, e.g. from the
2371f219e013SMasahiro Yamada        # environment or by being one of the special symbols n, m, and y? If
237290c36d8aSUlf Magnusson        # so, the value is stored in self.cached_val, which is never
2373f219e013SMasahiro Yamada        # invalidated. The trailing underscore avoids a collision with
2374f219e013SMasahiro Yamada        # is_special().
2375f219e013SMasahiro Yamada        self.is_special_ = False
2376f219e013SMasahiro Yamada        # Does the symbol get its value from the environment?
2377f219e013SMasahiro Yamada        self.is_from_env = False
23789d01b787SMasahiro Yamada        # Does the symbol have the 'allnoconfig_y' option set?
23799d01b787SMasahiro Yamada        self.allnoconfig_y = False
23809d01b787SMasahiro Yamada
2381f219e013SMasahiro Yamada    def _invalidate(self):
2382f219e013SMasahiro Yamada        if self.is_special_:
2383f219e013SMasahiro Yamada            return
2384f219e013SMasahiro Yamada
238590c36d8aSUlf Magnusson        if self.is_choice_sym:
2386f219e013SMasahiro Yamada            self.parent._invalidate()
2387f219e013SMasahiro Yamada
238890c36d8aSUlf Magnusson        self.cached_val = None
238990c36d8aSUlf Magnusson        self.cached_visibility = None
2390f219e013SMasahiro Yamada
2391f219e013SMasahiro Yamada    def _invalidate_dependent(self):
2392f219e013SMasahiro Yamada        for sym in self._get_dependent():
2393f219e013SMasahiro Yamada            sym._invalidate()
2394f219e013SMasahiro Yamada
2395f219e013SMasahiro Yamada    def _set_user_value_no_invalidate(self, v, suppress_load_warnings):
2396f219e013SMasahiro Yamada        """Like set_user_value(), but does not invalidate any symbols.
2397f219e013SMasahiro Yamada
239890c36d8aSUlf Magnusson        suppress_load_warnings: some warnings are annoying when loading a
239990c36d8aSUlf Magnusson           .config that can be helpful when manually invoking set_user_value().
240090c36d8aSUlf Magnusson           This flag is set to True to suppress such warnings.
2401f219e013SMasahiro Yamada
2402f219e013SMasahiro Yamada           Perhaps this could be made optional for load_config() instead."""
2403f219e013SMasahiro Yamada
2404f219e013SMasahiro Yamada        if self.is_special_:
2405f219e013SMasahiro Yamada            if self.is_from_env:
2406f219e013SMasahiro Yamada                self.config._warn('attempt to assign the value "{0}" to the '
2407f219e013SMasahiro Yamada                                  'symbol {1}, which gets its value from the '
2408f219e013SMasahiro Yamada                                  'environment. Assignment ignored.'
2409f219e013SMasahiro Yamada                                  .format(v, self.name))
2410f219e013SMasahiro Yamada            else:
2411f219e013SMasahiro Yamada                self.config._warn('attempt to assign the value "{0}" to the '
2412f219e013SMasahiro Yamada                                  'special symbol {1}. Assignment ignored.'
2413f219e013SMasahiro Yamada                                  .format(v, self.name))
2414f219e013SMasahiro Yamada            return
2415f219e013SMasahiro Yamada
2416f219e013SMasahiro Yamada        if not self.is_defined_:
2417f219e013SMasahiro Yamada            filename, linenr = self.ref_locations[0]
241890c36d8aSUlf Magnusson            if self.config.print_undef_assign:
241990c36d8aSUlf Magnusson                _stderr_msg('note: attempt to assign the value "{0}" to {1}, '
2420f219e013SMasahiro Yamada                            "which is referenced at {2}:{3} but never "
2421f219e013SMasahiro Yamada                            "defined. Assignment ignored."
2422f219e013SMasahiro Yamada                            .format(v, self.name, filename, linenr))
2423f219e013SMasahiro Yamada            return
2424f219e013SMasahiro Yamada
2425f219e013SMasahiro Yamada        # Check if the value is valid for our type
242690c36d8aSUlf Magnusson        if not ((self.type == BOOL     and (v == "y" or v == "n")   ) or
242790c36d8aSUlf Magnusson                (self.type == TRISTATE and (v == "y" or v == "m" or
242890c36d8aSUlf Magnusson                                            v == "n")               ) or
2429f219e013SMasahiro Yamada                (self.type == STRING                                ) or
2430f219e013SMasahiro Yamada                (self.type == INT      and _is_base_n(v, 10)        ) or
2431f219e013SMasahiro Yamada                (self.type == HEX      and _is_base_n(v, 16)        )):
243290c36d8aSUlf Magnusson            self.config._warn('the value "{0}" is invalid for {1}, which has '
243390c36d8aSUlf Magnusson                              "type {2}. Assignment ignored."
243490c36d8aSUlf Magnusson                              .format(v, self.name, TYPENAME[self.type]))
2435f219e013SMasahiro Yamada            return
2436f219e013SMasahiro Yamada
243790c36d8aSUlf Magnusson        if not self.prompts and not suppress_load_warnings:
2438f219e013SMasahiro Yamada            self.config._warn('assigning "{0}" to the symbol {1} which '
2439f219e013SMasahiro Yamada                              'lacks prompts and thus has visibility "n". '
2440f219e013SMasahiro Yamada                              'The assignment will have no effect.'
2441f219e013SMasahiro Yamada                              .format(v, self.name))
2442f219e013SMasahiro Yamada
2443f219e013SMasahiro Yamada        self.user_val = v
2444f219e013SMasahiro Yamada
244590c36d8aSUlf Magnusson        if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE):
2446f219e013SMasahiro Yamada            choice = self.parent
2447f219e013SMasahiro Yamada            if v == "y":
2448f219e013SMasahiro Yamada                choice.user_val = self
2449f219e013SMasahiro Yamada                choice.user_mode = "y"
2450f219e013SMasahiro Yamada            elif v == "m":
2451f219e013SMasahiro Yamada                choice.user_val = None
2452f219e013SMasahiro Yamada                choice.user_mode = "m"
2453f219e013SMasahiro Yamada
2454f219e013SMasahiro Yamada    def _unset_user_value_no_recursive_invalidate(self):
2455f219e013SMasahiro Yamada        self._invalidate()
2456f219e013SMasahiro Yamada        self.user_val = None
2457f219e013SMasahiro Yamada
245890c36d8aSUlf Magnusson        if self.is_choice_sym:
2459f219e013SMasahiro Yamada            self.parent._unset_user_value()
2460f219e013SMasahiro Yamada
246190c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2462f219e013SMasahiro Yamada        if self.already_written:
246390c36d8aSUlf Magnusson            return
2464f219e013SMasahiro Yamada
2465f219e013SMasahiro Yamada        self.already_written = True
2466f219e013SMasahiro Yamada
2467f219e013SMasahiro Yamada        # Note: write_to_conf is determined in get_value()
2468f219e013SMasahiro Yamada        val = self.get_value()
2469f219e013SMasahiro Yamada        if not self.write_to_conf:
247090c36d8aSUlf Magnusson            return
2471f219e013SMasahiro Yamada
2472f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
247390c36d8aSUlf Magnusson            if val == "y" or val == "m":
247490c36d8aSUlf Magnusson                append_fn("CONFIG_{0}={1}".format(self.name, val))
247590c36d8aSUlf Magnusson            else:
247690c36d8aSUlf Magnusson                append_fn("# CONFIG_{0} is not set".format(self.name))
247790c36d8aSUlf Magnusson
247890c36d8aSUlf Magnusson        elif self.type == INT or self.type == HEX:
247990c36d8aSUlf Magnusson            append_fn("CONFIG_{0}={1}".format(self.name, val))
2480f219e013SMasahiro Yamada
2481f219e013SMasahiro Yamada        elif self.type == STRING:
2482f219e013SMasahiro Yamada            # Escape \ and "
248390c36d8aSUlf Magnusson            append_fn('CONFIG_{0}="{1}"'
2484f219e013SMasahiro Yamada                      .format(self.name,
248590c36d8aSUlf Magnusson                              val.replace("\\", "\\\\").replace('"', '\\"')))
2486f219e013SMasahiro Yamada
2487f219e013SMasahiro Yamada        else:
248890c36d8aSUlf Magnusson            _internal_error("Internal error while creating .config: unknown "
248990c36d8aSUlf Magnusson                            'type "{0}".'.format(self.type))
2490f219e013SMasahiro Yamada
2491f219e013SMasahiro Yamada    def _get_dependent(self):
2492f219e013SMasahiro Yamada        """Returns the set of symbols that should be invalidated if the value
2493f219e013SMasahiro Yamada        of the symbol changes, because they might be affected by the change.
2494f219e013SMasahiro Yamada        Note that this is an internal API -- it's probably of limited
2495f219e013SMasahiro Yamada        usefulness to clients."""
2496f219e013SMasahiro Yamada        if self.cached_deps is not None:
2497f219e013SMasahiro Yamada            return self.cached_deps
2498f219e013SMasahiro Yamada
249990c36d8aSUlf Magnusson        res = set(self.dep)
250090c36d8aSUlf Magnusson        for s in self.dep:
250190c36d8aSUlf Magnusson            res |= s._get_dependent()
2502f219e013SMasahiro Yamada
250390c36d8aSUlf Magnusson        if self.is_choice_sym:
250490c36d8aSUlf Magnusson            # Choice symbols also depend (recursively) on their siblings. The
250590c36d8aSUlf Magnusson            # siblings are not included in 'dep' to avoid dependency loops.
250690c36d8aSUlf Magnusson            for sibling in self.parent.actual_symbols:
250790c36d8aSUlf Magnusson                if sibling is not self:
250890c36d8aSUlf Magnusson                    res.add(sibling)
250990c36d8aSUlf Magnusson                    res |= sibling.dep
251090c36d8aSUlf Magnusson                    for s in sibling.dep:
251190c36d8aSUlf Magnusson                        res |= s._get_dependent()
2512f219e013SMasahiro Yamada
2513f219e013SMasahiro Yamada        self.cached_deps = res
2514f219e013SMasahiro Yamada        return res
2515f219e013SMasahiro Yamada
2516f219e013SMasahiro Yamada    def _has_auto_menu_dep_on(self, on):
2517f219e013SMasahiro Yamada        """See Choice._determine_actual_symbols()."""
2518f219e013SMasahiro Yamada        if not isinstance(self.parent, Choice):
251990c36d8aSUlf Magnusson            _internal_error("Attempt to determine auto menu dependency for "
252090c36d8aSUlf Magnusson                            "symbol ouside of choice.")
2521f219e013SMasahiro Yamada
252290c36d8aSUlf Magnusson        if not self.prompts:
2523f219e013SMasahiro Yamada            # If we have no prompt, use the menu dependencies instead (what was
2524f219e013SMasahiro Yamada            # specified with 'depends on')
2525f219e013SMasahiro Yamada            return self.menu_dep is not None and \
2526f219e013SMasahiro Yamada                   self.config._expr_depends_on(self.menu_dep, on)
2527f219e013SMasahiro Yamada
252890c36d8aSUlf Magnusson        for _, cond_expr in self.prompts:
2529f219e013SMasahiro Yamada            if self.config._expr_depends_on(cond_expr, on):
2530f219e013SMasahiro Yamada                return True
2531f219e013SMasahiro Yamada
2532f219e013SMasahiro Yamada        return False
2533f219e013SMasahiro Yamada
2534f219e013SMasahiro Yamadaclass Menu(Item):
2535f219e013SMasahiro Yamada
2536f219e013SMasahiro Yamada    """Represents a menu statement."""
2537f219e013SMasahiro Yamada
2538f219e013SMasahiro Yamada    #
2539f219e013SMasahiro Yamada    # Public interface
2540f219e013SMasahiro Yamada    #
2541f219e013SMasahiro Yamada
2542f219e013SMasahiro Yamada    def get_config(self):
2543f219e013SMasahiro Yamada        """Return the Config instance this menu is from."""
2544f219e013SMasahiro Yamada        return self.config
2545f219e013SMasahiro Yamada
2546f219e013SMasahiro Yamada    def get_title(self):
2547f219e013SMasahiro Yamada        """Returns the title text of the menu."""
2548f219e013SMasahiro Yamada        return self.title
2549f219e013SMasahiro Yamada
2550f219e013SMasahiro Yamada    def get_parent(self):
2551f219e013SMasahiro Yamada        """Returns the menu or choice statement that contains the menu, or
2552f219e013SMasahiro Yamada        None if the menu is at the top level. Note that if statements are
2553f219e013SMasahiro Yamada        treated as syntactic sugar and do not have an explicit class
2554f219e013SMasahiro Yamada        representation."""
2555f219e013SMasahiro Yamada        return self.parent
2556f219e013SMasahiro Yamada
2557f219e013SMasahiro Yamada    def get_location(self):
2558f219e013SMasahiro Yamada        """Returns the location of the menu as a (filename, linenr) tuple,
2559f219e013SMasahiro Yamada        where filename is a string and linenr an int."""
2560f219e013SMasahiro Yamada        return (self.filename, self.linenr)
2561f219e013SMasahiro Yamada
256290c36d8aSUlf Magnusson    def get_items(self, recursive=False):
256390c36d8aSUlf Magnusson        """Returns a list containing the items (symbols, menus, choice
256490c36d8aSUlf Magnusson        statements and comments) in in the menu, in the same order that the
256590c36d8aSUlf Magnusson        items appear within the menu.
256690c36d8aSUlf Magnusson
256790c36d8aSUlf Magnusson        recursive (default: False): True if items contained in items within the
256890c36d8aSUlf Magnusson           menu should be included recursively (preorder)."""
256990c36d8aSUlf Magnusson
257090c36d8aSUlf Magnusson        if not recursive:
257190c36d8aSUlf Magnusson            return self.block
257290c36d8aSUlf Magnusson
257390c36d8aSUlf Magnusson        res = []
257490c36d8aSUlf Magnusson        for item in self.block:
257590c36d8aSUlf Magnusson            res.append(item)
257690c36d8aSUlf Magnusson            if isinstance(item, Menu):
257790c36d8aSUlf Magnusson                res.extend(item.get_items(True))
257890c36d8aSUlf Magnusson            elif isinstance(item, Choice):
257990c36d8aSUlf Magnusson                res.extend(item.get_items())
258090c36d8aSUlf Magnusson        return res
258190c36d8aSUlf Magnusson
258290c36d8aSUlf Magnusson    def get_symbols(self, recursive=False):
258390c36d8aSUlf Magnusson        """Returns a list containing the symbols in the menu, in the same order
258490c36d8aSUlf Magnusson        that they appear within the menu.
258590c36d8aSUlf Magnusson
258690c36d8aSUlf Magnusson        recursive (default: False): True if symbols contained in items within
258790c36d8aSUlf Magnusson           the menu should be included recursively."""
258890c36d8aSUlf Magnusson
258990c36d8aSUlf Magnusson        return [item for item in self.get_items(recursive) if
259090c36d8aSUlf Magnusson                isinstance(item, Symbol)]
259190c36d8aSUlf Magnusson
259290c36d8aSUlf Magnusson    def get_visibility(self):
259390c36d8aSUlf Magnusson        """Returns the visibility of the menu. This also affects the visibility
259490c36d8aSUlf Magnusson        of subitems. See also Symbol.get_visibility()."""
259590c36d8aSUlf Magnusson        return self.config._eval_expr(self.dep_expr)
259690c36d8aSUlf Magnusson
259790c36d8aSUlf Magnusson    def get_visible_if_visibility(self):
259890c36d8aSUlf Magnusson        """Returns the visibility the menu gets from its 'visible if'
259990c36d8aSUlf Magnusson        condition. "y" if the menu has no 'visible if' condition."""
260090c36d8aSUlf Magnusson        return self.config._eval_expr(self.visible_if_expr)
260190c36d8aSUlf Magnusson
260290c36d8aSUlf Magnusson    def get_referenced_symbols(self, refs_from_enclosing=False):
260390c36d8aSUlf Magnusson        """See Symbol.get_referenced_symbols()."""
260490c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
260590c36d8aSUlf Magnusson               self.referenced_syms
260690c36d8aSUlf Magnusson
2607f219e013SMasahiro Yamada    def __str__(self):
2608f219e013SMasahiro Yamada        """Returns a string containing various information about the menu."""
2609f219e013SMasahiro Yamada        depends_on_str = self.config._expr_val_str(self.orig_deps,
2610f219e013SMasahiro Yamada                                                   "(no dependencies)")
2611f219e013SMasahiro Yamada        visible_if_str = self.config._expr_val_str(self.visible_if_expr,
2612f219e013SMasahiro Yamada                                                   "(no dependencies)")
2613f219e013SMasahiro Yamada
261490c36d8aSUlf Magnusson        additional_deps_str = " " + \
261590c36d8aSUlf Magnusson          self.config._expr_val_str(self.deps_from_containing,
2616f219e013SMasahiro Yamada                                    "(no additional dependencies)")
2617f219e013SMasahiro Yamada
261890c36d8aSUlf Magnusson        return _lines("Menu",
2619f219e013SMasahiro Yamada                      "Title                     : " + self.title,
2620f219e013SMasahiro Yamada                      "'depends on' dependencies : " + depends_on_str,
2621f219e013SMasahiro Yamada                      "'visible if' dependencies : " + visible_if_str,
262290c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
262390c36d8aSUlf Magnusson                        "ifs:",
2624f219e013SMasahiro Yamada                      additional_deps_str,
2625f219e013SMasahiro Yamada                      "Location: {0}:{1}".format(self.filename, self.linenr))
2626f219e013SMasahiro Yamada
2627f219e013SMasahiro Yamada    #
2628f219e013SMasahiro Yamada    # Private methods
2629f219e013SMasahiro Yamada    #
2630f219e013SMasahiro Yamada
2631f219e013SMasahiro Yamada    def __init__(self):
2632f219e013SMasahiro Yamada        """Menu constructor -- not intended to be called directly by
263390c36d8aSUlf Magnusson        Kconfiglib clients."""
2634f219e013SMasahiro Yamada
2635f219e013SMasahiro Yamada        self.title = None
2636f219e013SMasahiro Yamada        self.dep_expr = None
263790c36d8aSUlf Magnusson        self.visible_if_expr = None
263890c36d8aSUlf Magnusson        self.block = None
263990c36d8aSUlf Magnusson        self.config = None
264090c36d8aSUlf Magnusson        self.parent = None
2641f219e013SMasahiro Yamada
2642f219e013SMasahiro Yamada        # Dependency expression without dependencies from enclosing menus and
264390c36d8aSUlf Magnusson        # ifs propagated
2644f219e013SMasahiro Yamada        self.orig_deps = None
2645f219e013SMasahiro Yamada
264690c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2647f219e013SMasahiro Yamada        self.deps_from_containing = None
2648f219e013SMasahiro Yamada        # The set of symbols referenced by this menu (see
2649f219e013SMasahiro Yamada        # get_referenced_symbols())
2650f219e013SMasahiro Yamada        self.referenced_syms = set()
2651f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
265290c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2653f219e013SMasahiro Yamada        self.all_referenced_syms = None
2654f219e013SMasahiro Yamada
2655f219e013SMasahiro Yamada        self.filename = None
2656f219e013SMasahiro Yamada        self.linenr = None
2657f219e013SMasahiro Yamada
265890c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2659f219e013SMasahiro Yamada        if self.config._eval_expr(self.dep_expr) != "n" and \
2660f219e013SMasahiro Yamada           self.config._eval_expr(self.visible_if_expr) != "n":
266190c36d8aSUlf Magnusson            append_fn("\n#\n# {0}\n#".format(self.title))
266290c36d8aSUlf Magnusson        _make_block_conf(self.block, append_fn)
2663f219e013SMasahiro Yamada
266490c36d8aSUlf Magnussonclass Choice(Item):
2665f219e013SMasahiro Yamada
2666f219e013SMasahiro Yamada    """Represents a choice statement. A choice can be in one of three modes:
2667f219e013SMasahiro Yamada
2668f219e013SMasahiro Yamada    "n" - The choice is not visible and no symbols can be selected.
2669f219e013SMasahiro Yamada
2670f219e013SMasahiro Yamada    "m" - Any number of symbols can be set to "m". The rest will be "n". This
2671f219e013SMasahiro Yamada          is safe since potentially conflicting options don't actually get
2672f219e013SMasahiro Yamada          compiled into the kernel simultaneously with "m".
2673f219e013SMasahiro Yamada
2674f219e013SMasahiro Yamada    "y" - One symbol will be "y" while the rest are "n".
2675f219e013SMasahiro Yamada
2676f219e013SMasahiro Yamada    Only tristate choices can be in "m" mode, and the visibility of the choice
2677f219e013SMasahiro Yamada    is an upper bound on the mode, so that e.g. a choice that depends on a
2678f219e013SMasahiro Yamada    symbol with value "m" will be in "m" mode.
2679f219e013SMasahiro Yamada
2680f219e013SMasahiro Yamada    The mode changes automatically when a value is assigned to a symbol within
2681f219e013SMasahiro Yamada    the choice.
2682f219e013SMasahiro Yamada
2683f219e013SMasahiro Yamada    See Symbol.get_visibility() too."""
2684f219e013SMasahiro Yamada
2685f219e013SMasahiro Yamada    #
2686f219e013SMasahiro Yamada    # Public interface
2687f219e013SMasahiro Yamada    #
2688f219e013SMasahiro Yamada
2689f219e013SMasahiro Yamada    def get_config(self):
2690f219e013SMasahiro Yamada        """Returns the Config instance this choice is from."""
2691f219e013SMasahiro Yamada        return self.config
2692f219e013SMasahiro Yamada
2693f219e013SMasahiro Yamada    def get_name(self):
2694f219e013SMasahiro Yamada        """For named choices, returns the name. Returns None for unnamed
2695f219e013SMasahiro Yamada        choices. No named choices appear anywhere in the kernel Kconfig files
2696f219e013SMasahiro Yamada        as of Linux 3.7.0-rc8."""
2697f219e013SMasahiro Yamada        return self.name
2698f219e013SMasahiro Yamada
269990c36d8aSUlf Magnusson    def get_type(self):
270090c36d8aSUlf Magnusson        """Returns the type of the choice. See Symbol.get_type()."""
270190c36d8aSUlf Magnusson        return self.type
270290c36d8aSUlf Magnusson
2703f219e013SMasahiro Yamada    def get_prompts(self):
2704f219e013SMasahiro Yamada        """Returns a list of prompts defined for the choice, in the order they
2705f219e013SMasahiro Yamada        appear in the configuration files. Returns the empty list for choices
2706f219e013SMasahiro Yamada        with no prompt.
2707f219e013SMasahiro Yamada
2708f219e013SMasahiro Yamada        This list will have a single entry for the vast majority of choices
2709f219e013SMasahiro Yamada        having prompts, but having multiple prompts for a single choice is
2710f219e013SMasahiro Yamada        possible through having multiple 'choice' entries for it (though I'm
2711f219e013SMasahiro Yamada        not sure if that ever happens in practice)."""
2712f219e013SMasahiro Yamada        return [prompt for prompt, _ in self.orig_prompts]
2713f219e013SMasahiro Yamada
2714f219e013SMasahiro Yamada    def get_help(self):
2715f219e013SMasahiro Yamada        """Returns the help text of the choice, or None if the choice has no
2716f219e013SMasahiro Yamada        help text."""
2717f219e013SMasahiro Yamada        return self.help
2718f219e013SMasahiro Yamada
271990c36d8aSUlf Magnusson    def get_parent(self):
272090c36d8aSUlf Magnusson        """Returns the menu or choice statement that contains the choice, or
272190c36d8aSUlf Magnusson        None if the choice is at the top level. Note that if statements are
272290c36d8aSUlf Magnusson        treated as syntactic sugar and do not have an explicit class
272390c36d8aSUlf Magnusson        representation."""
272490c36d8aSUlf Magnusson        return self.parent
272590c36d8aSUlf Magnusson
272690c36d8aSUlf Magnusson    def get_def_locations(self):
272790c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
272890c36d8aSUlf Magnusson        and linenr (int) represent a location where the choice is defined. For
272990c36d8aSUlf Magnusson        the vast majority of choices (all of them as of Linux 3.7.0-rc8) this
273090c36d8aSUlf Magnusson        list will only contain one element, but its possible for named choices
273190c36d8aSUlf Magnusson        to be defined in multiple locations."""
273290c36d8aSUlf Magnusson        return self.def_locations
273390c36d8aSUlf Magnusson
273490c36d8aSUlf Magnusson    def get_selection(self):
273590c36d8aSUlf Magnusson        """Returns the symbol selected (either by the user or through
273690c36d8aSUlf Magnusson        defaults), or None if either no symbol is selected or the mode is not
273790c36d8aSUlf Magnusson        "y"."""
273890c36d8aSUlf Magnusson        if self.cached_selection is not None:
273990c36d8aSUlf Magnusson            if self.cached_selection == NO_SELECTION:
274090c36d8aSUlf Magnusson                return None
274190c36d8aSUlf Magnusson            return self.cached_selection
274290c36d8aSUlf Magnusson
274390c36d8aSUlf Magnusson        if self.get_mode() != "y":
274490c36d8aSUlf Magnusson            return self._cache_ret(None)
274590c36d8aSUlf Magnusson
274690c36d8aSUlf Magnusson        # User choice available?
274790c36d8aSUlf Magnusson        if self.user_val is not None and _get_visibility(self.user_val) == "y":
274890c36d8aSUlf Magnusson            return self._cache_ret(self.user_val)
274990c36d8aSUlf Magnusson
275090c36d8aSUlf Magnusson        if self.optional:
275190c36d8aSUlf Magnusson            return self._cache_ret(None)
275290c36d8aSUlf Magnusson
275390c36d8aSUlf Magnusson        return self._cache_ret(self.get_selection_from_defaults())
275490c36d8aSUlf Magnusson
275590c36d8aSUlf Magnusson    def get_selection_from_defaults(self):
275690c36d8aSUlf Magnusson        """Like Choice.get_selection(), but acts as if no symbol has been
275790c36d8aSUlf Magnusson        selected by the user and no 'optional' flag is in effect."""
275890c36d8aSUlf Magnusson
275990c36d8aSUlf Magnusson        if not self.actual_symbols:
276090c36d8aSUlf Magnusson            return None
276190c36d8aSUlf Magnusson
276290c36d8aSUlf Magnusson        for symbol, cond_expr in self.def_exprs:
276390c36d8aSUlf Magnusson            if self.config._eval_expr(cond_expr) != "n":
276490c36d8aSUlf Magnusson                chosen_symbol = symbol
276590c36d8aSUlf Magnusson                break
276690c36d8aSUlf Magnusson        else:
276790c36d8aSUlf Magnusson            chosen_symbol = self.actual_symbols[0]
276890c36d8aSUlf Magnusson
276990c36d8aSUlf Magnusson        # Is the chosen symbol visible?
277090c36d8aSUlf Magnusson        if _get_visibility(chosen_symbol) != "n":
277190c36d8aSUlf Magnusson            return chosen_symbol
277290c36d8aSUlf Magnusson        # Otherwise, pick the first visible symbol
277390c36d8aSUlf Magnusson        for sym in self.actual_symbols:
277490c36d8aSUlf Magnusson            if _get_visibility(sym) != "n":
277590c36d8aSUlf Magnusson                return sym
277690c36d8aSUlf Magnusson        return None
277790c36d8aSUlf Magnusson
277890c36d8aSUlf Magnusson    def get_user_selection(self):
277990c36d8aSUlf Magnusson        """If the choice is in "y" mode and has a user-selected symbol, returns
278090c36d8aSUlf Magnusson        that symbol. Otherwise, returns None."""
278190c36d8aSUlf Magnusson        return self.user_val
2782f219e013SMasahiro Yamada
2783f219e013SMasahiro Yamada    def get_items(self):
2784f219e013SMasahiro Yamada        """Gets all items contained in the choice in the same order as within
2785f219e013SMasahiro Yamada        the configuration ("items" instead of "symbols" since choices and
2786f219e013SMasahiro Yamada        comments might appear within choices. This only happens in one place as
2787f219e013SMasahiro Yamada        of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig)."""
278890c36d8aSUlf Magnusson        return self.block
2789f219e013SMasahiro Yamada
2790f219e013SMasahiro Yamada    def get_symbols(self):
2791f219e013SMasahiro Yamada        """Returns a list containing the choice's symbols.
2792f219e013SMasahiro Yamada
2793f219e013SMasahiro Yamada        A quirk (perhaps a bug) of Kconfig is that you can put items within a
2794f219e013SMasahiro Yamada        choice that will not be considered members of the choice insofar as
2795f219e013SMasahiro Yamada        selection is concerned. This happens for example if one symbol within a
2796f219e013SMasahiro Yamada        choice 'depends on' the symbol preceding it, or if you put non-symbol
2797f219e013SMasahiro Yamada        items within choices.
2798f219e013SMasahiro Yamada
2799f219e013SMasahiro Yamada        As of Linux 3.7.0-rc8, this seems to be used intentionally in one
2800f219e013SMasahiro Yamada        place: drivers/usb/gadget/Kconfig.
2801f219e013SMasahiro Yamada
2802f219e013SMasahiro Yamada        This function returns the "proper" symbols of the choice in the order
2803f219e013SMasahiro Yamada        they appear in the choice, excluding such items. If you want all items
2804f219e013SMasahiro Yamada        in the choice, use get_items()."""
2805f219e013SMasahiro Yamada        return self.actual_symbols
2806f219e013SMasahiro Yamada
2807f219e013SMasahiro Yamada    def get_referenced_symbols(self, refs_from_enclosing=False):
2808f219e013SMasahiro Yamada        """See Symbol.get_referenced_symbols()."""
280990c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
281090c36d8aSUlf Magnusson               self.referenced_syms
2811f219e013SMasahiro Yamada
2812f219e013SMasahiro Yamada    def get_visibility(self):
2813f219e013SMasahiro Yamada        """Returns the visibility of the choice statement: one of "n", "m" or
2814f219e013SMasahiro Yamada        "y". This acts as an upper limit on the mode of the choice (though bool
2815f219e013SMasahiro Yamada        choices can only have the mode "y"). See the class documentation for an
2816f219e013SMasahiro Yamada        explanation of modes."""
281790c36d8aSUlf Magnusson        return _get_visibility(self)
2818f219e013SMasahiro Yamada
2819f219e013SMasahiro Yamada    def get_mode(self):
2820f219e013SMasahiro Yamada        """Returns the mode of the choice. See the class documentation for
2821f219e013SMasahiro Yamada        an explanation of modes."""
2822f219e013SMasahiro Yamada        minimum_mode = "n" if self.optional else "m"
2823f219e013SMasahiro Yamada        mode = self.user_mode if self.user_mode is not None else minimum_mode
282490c36d8aSUlf Magnusson        mode = self.config._eval_min(mode, _get_visibility(self))
2825f219e013SMasahiro Yamada
2826f219e013SMasahiro Yamada        # Promote "m" to "y" for boolean choices
2827f219e013SMasahiro Yamada        if mode == "m" and self.type == BOOL:
2828f219e013SMasahiro Yamada            return "y"
2829f219e013SMasahiro Yamada
2830f219e013SMasahiro Yamada        return mode
2831f219e013SMasahiro Yamada
2832f219e013SMasahiro Yamada    def is_optional(self):
283390c36d8aSUlf Magnusson        """Returns True if the choice has the 'optional' flag set (and so will
283490c36d8aSUlf Magnusson        default to "n" mode)."""
2835f219e013SMasahiro Yamada        return self.optional
2836f219e013SMasahiro Yamada
2837f219e013SMasahiro Yamada    def __str__(self):
2838f219e013SMasahiro Yamada        """Returns a string containing various information about the choice
2839f219e013SMasahiro Yamada        statement."""
2840f219e013SMasahiro Yamada        return self.config._get_sym_or_choice_str(self)
2841f219e013SMasahiro Yamada
2842f219e013SMasahiro Yamada    #
2843f219e013SMasahiro Yamada    # Private methods
2844f219e013SMasahiro Yamada    #
2845f219e013SMasahiro Yamada
2846f219e013SMasahiro Yamada    def __init__(self):
2847f219e013SMasahiro Yamada        """Choice constructor -- not intended to be called directly by
284890c36d8aSUlf Magnusson        Kconfiglib clients."""
2849f219e013SMasahiro Yamada
2850f219e013SMasahiro Yamada        self.name = None # Yes, choices can be named
2851f219e013SMasahiro Yamada        self.type = UNKNOWN
285290c36d8aSUlf Magnusson        self.prompts = []
285390c36d8aSUlf Magnusson        self.def_exprs = [] # 'default' properties
285490c36d8aSUlf Magnusson        self.help = None # Help text
285590c36d8aSUlf Magnusson        self.block = None # List of contained items
285690c36d8aSUlf Magnusson        self.config = None
285790c36d8aSUlf Magnusson        self.parent = None
2858f219e013SMasahiro Yamada
285990c36d8aSUlf Magnusson        self.user_val = None
286090c36d8aSUlf Magnusson        self.user_mode = None
2861f219e013SMasahiro Yamada
2862f219e013SMasahiro Yamada        # We need to filter out symbols that appear within the choice block but
2863f219e013SMasahiro Yamada        # are not considered choice items (see
286490c36d8aSUlf Magnusson        # Choice._determine_actual_symbols()) This list holds the "actual"
286590c36d8aSUlf Magnusson        # choice items.
2866f219e013SMasahiro Yamada        self.actual_symbols = []
2867f219e013SMasahiro Yamada
286890c36d8aSUlf Magnusson        # The prompts and default values without any dependencies from
286990c36d8aSUlf Magnusson        # enclosing menus and ifs propagated
287090c36d8aSUlf Magnusson        self.orig_prompts = []
287190c36d8aSUlf Magnusson        self.orig_def_exprs = []
287290c36d8aSUlf Magnusson
287390c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
287490c36d8aSUlf Magnusson        self.deps_from_containing = None
2875f219e013SMasahiro Yamada        # The set of symbols referenced by this choice (see
2876f219e013SMasahiro Yamada        # get_referenced_symbols())
2877f219e013SMasahiro Yamada        self.referenced_syms = set()
2878f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
287990c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2880f219e013SMasahiro Yamada        self.all_referenced_syms = set()
2881f219e013SMasahiro Yamada
2882f219e013SMasahiro Yamada        # See Choice.get_def_locations()
2883f219e013SMasahiro Yamada        self.def_locations = []
2884f219e013SMasahiro Yamada
288590c36d8aSUlf Magnusson        # Cached values
2886f219e013SMasahiro Yamada        self.cached_selection = None
288790c36d8aSUlf Magnusson        self.cached_visibility = None
288890c36d8aSUlf Magnusson
288990c36d8aSUlf Magnusson        self.optional = False
2890f219e013SMasahiro Yamada
2891f219e013SMasahiro Yamada    def _determine_actual_symbols(self):
2892f219e013SMasahiro Yamada        """If a symbol's visibility depends on the preceding symbol within a
289390c36d8aSUlf Magnusson        choice, it is no longer viewed as a choice item. (This is quite
289490c36d8aSUlf Magnusson        possibly a bug, but some things consciously use it... ugh. It stems
289590c36d8aSUlf Magnusson        from automatic submenu creation.) In addition, it's possible to have
289690c36d8aSUlf Magnusson        choices and comments within choices, and those shouldn't be considered
289790c36d8aSUlf Magnusson        choice items either. Only drivers/usb/gadget/Kconfig seems to depend on
289890c36d8aSUlf Magnusson        any of this. This method computes the "actual" items in the choice and
289990c36d8aSUlf Magnusson        sets the is_choice_sym flag on them (retrieved via is_choice_symbol()).
2900f219e013SMasahiro Yamada
2901f219e013SMasahiro Yamada        Don't let this scare you: an earlier version simply checked for a
2902f219e013SMasahiro Yamada        sequence of symbols where all symbols after the first appeared in the
2903f219e013SMasahiro Yamada        'depends on' expression of the first, and that worked fine.  The added
2904f219e013SMasahiro Yamada        complexity is to be future-proof in the event that
2905f219e013SMasahiro Yamada        drivers/usb/gadget/Kconfig turns even more sinister. It might very well
2906f219e013SMasahiro Yamada        be overkilling things (especially if that file is refactored ;)."""
2907f219e013SMasahiro Yamada
2908f219e013SMasahiro Yamada        # Items might depend on each other in a tree structure, so we need a
2909f219e013SMasahiro Yamada        # stack to keep track of the current tentative parent
2910f219e013SMasahiro Yamada        stack = []
2911f219e013SMasahiro Yamada
291290c36d8aSUlf Magnusson        for item in self.block:
2913f219e013SMasahiro Yamada            if not isinstance(item, Symbol):
2914f219e013SMasahiro Yamada                stack = []
2915f219e013SMasahiro Yamada                continue
2916f219e013SMasahiro Yamada
291790c36d8aSUlf Magnusson            while stack:
2918f219e013SMasahiro Yamada                if item._has_auto_menu_dep_on(stack[-1]):
2919f219e013SMasahiro Yamada                    # The item should not be viewed as a choice item, so don't
292090c36d8aSUlf Magnusson                    # set item.is_choice_sym
2921f219e013SMasahiro Yamada                    stack.append(item)
2922f219e013SMasahiro Yamada                    break
2923f219e013SMasahiro Yamada                else:
2924f219e013SMasahiro Yamada                    stack.pop()
2925f219e013SMasahiro Yamada            else:
292690c36d8aSUlf Magnusson                item.is_choice_sym = True
2927f219e013SMasahiro Yamada                self.actual_symbols.append(item)
2928f219e013SMasahiro Yamada                stack.append(item)
2929f219e013SMasahiro Yamada
2930f219e013SMasahiro Yamada    def _cache_ret(self, selection):
2931f219e013SMasahiro Yamada        # As None is used to indicate the lack of a cached value we can't use
2932f219e013SMasahiro Yamada        # that to cache the fact that the choice has no selection. Instead, we
2933f219e013SMasahiro Yamada        # use the symbolic constant NO_SELECTION.
2934f219e013SMasahiro Yamada        if selection is None:
2935f219e013SMasahiro Yamada            self.cached_selection = NO_SELECTION
2936f219e013SMasahiro Yamada        else:
2937f219e013SMasahiro Yamada            self.cached_selection = selection
2938f219e013SMasahiro Yamada
2939f219e013SMasahiro Yamada        return selection
2940f219e013SMasahiro Yamada
2941f219e013SMasahiro Yamada    def _invalidate(self):
2942f219e013SMasahiro Yamada        self.cached_selection = None
294390c36d8aSUlf Magnusson        self.cached_visibility = None
2944f219e013SMasahiro Yamada
2945f219e013SMasahiro Yamada    def _unset_user_value(self):
2946f219e013SMasahiro Yamada        self._invalidate()
2947f219e013SMasahiro Yamada        self.user_val = None
2948f219e013SMasahiro Yamada        self.user_mode = None
2949f219e013SMasahiro Yamada
295090c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
295190c36d8aSUlf Magnusson        _make_block_conf(self.block, append_fn)
2952f219e013SMasahiro Yamada
2953f219e013SMasahiro Yamadaclass Comment(Item):
2954f219e013SMasahiro Yamada
2955f219e013SMasahiro Yamada    """Represents a comment statement."""
2956f219e013SMasahiro Yamada
2957f219e013SMasahiro Yamada    #
2958f219e013SMasahiro Yamada    # Public interface
2959f219e013SMasahiro Yamada    #
2960f219e013SMasahiro Yamada
2961f219e013SMasahiro Yamada    def get_config(self):
2962f219e013SMasahiro Yamada        """Returns the Config instance this comment is from."""
2963f219e013SMasahiro Yamada        return self.config
2964f219e013SMasahiro Yamada
2965f219e013SMasahiro Yamada    def get_text(self):
2966f219e013SMasahiro Yamada        """Returns the text of the comment."""
2967f219e013SMasahiro Yamada        return self.text
2968f219e013SMasahiro Yamada
2969f219e013SMasahiro Yamada    def get_parent(self):
2970f219e013SMasahiro Yamada        """Returns the menu or choice statement that contains the comment, or
2971f219e013SMasahiro Yamada        None if the comment is at the top level. Note that if statements are
2972f219e013SMasahiro Yamada        treated as syntactic sugar and do not have an explicit class
2973f219e013SMasahiro Yamada        representation."""
2974f219e013SMasahiro Yamada        return self.parent
2975f219e013SMasahiro Yamada
2976f219e013SMasahiro Yamada    def get_location(self):
2977f219e013SMasahiro Yamada        """Returns the location of the comment as a (filename, linenr) tuple,
2978f219e013SMasahiro Yamada        where filename is a string and linenr an int."""
2979f219e013SMasahiro Yamada        return (self.filename, self.linenr)
2980f219e013SMasahiro Yamada
298190c36d8aSUlf Magnusson    def get_visibility(self):
298290c36d8aSUlf Magnusson        """Returns the visibility of the comment. See also
298390c36d8aSUlf Magnusson        Symbol.get_visibility()."""
298490c36d8aSUlf Magnusson        return self.config._eval_expr(self.dep_expr)
2985f219e013SMasahiro Yamada
298690c36d8aSUlf Magnusson    def get_referenced_symbols(self, refs_from_enclosing=False):
298790c36d8aSUlf Magnusson        """See Symbol.get_referenced_symbols()."""
298890c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
298990c36d8aSUlf Magnusson               self.referenced_syms
299090c36d8aSUlf Magnusson
299190c36d8aSUlf Magnusson    def __str__(self):
299290c36d8aSUlf Magnusson        """Returns a string containing various information about the
299390c36d8aSUlf Magnusson        comment."""
299490c36d8aSUlf Magnusson        dep_str = self.config._expr_val_str(self.orig_deps,
299590c36d8aSUlf Magnusson                                            "(no dependencies)")
299690c36d8aSUlf Magnusson
299790c36d8aSUlf Magnusson        additional_deps_str = " " + \
299890c36d8aSUlf Magnusson          self.config._expr_val_str(self.deps_from_containing,
2999f219e013SMasahiro Yamada                                    "(no additional dependencies)")
3000f219e013SMasahiro Yamada
300190c36d8aSUlf Magnusson        return _lines("Comment",
3002f219e013SMasahiro Yamada                      "Text: "         + str(self.text),
3003f219e013SMasahiro Yamada                      "Dependencies: " + dep_str,
300490c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
300590c36d8aSUlf Magnusson                        "ifs:",
3006f219e013SMasahiro Yamada                      additional_deps_str,
3007f219e013SMasahiro Yamada                      "Location: {0}:{1}".format(self.filename, self.linenr))
3008f219e013SMasahiro Yamada
3009f219e013SMasahiro Yamada    #
3010f219e013SMasahiro Yamada    # Private methods
3011f219e013SMasahiro Yamada    #
3012f219e013SMasahiro Yamada
3013f219e013SMasahiro Yamada    def __init__(self):
3014f219e013SMasahiro Yamada        """Comment constructor -- not intended to be called directly by
301590c36d8aSUlf Magnusson        Kconfiglib clients."""
3016f219e013SMasahiro Yamada
3017f219e013SMasahiro Yamada        self.text = None
3018f219e013SMasahiro Yamada        self.dep_expr = None
301990c36d8aSUlf Magnusson        self.config = None
302090c36d8aSUlf Magnusson        self.parent = None
3021f219e013SMasahiro Yamada
3022f219e013SMasahiro Yamada        # Dependency expression without dependencies from enclosing menus and
302390c36d8aSUlf Magnusson        # ifs propagated
3024f219e013SMasahiro Yamada        self.orig_deps = None
3025f219e013SMasahiro Yamada
302690c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
3027f219e013SMasahiro Yamada        self.deps_from_containing = None
3028f219e013SMasahiro Yamada        # The set of symbols referenced by this comment (see
3029f219e013SMasahiro Yamada        # get_referenced_symbols())
3030f219e013SMasahiro Yamada        self.referenced_syms = set()
3031f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
303290c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
3033f219e013SMasahiro Yamada        self.all_referenced_syms = None
3034f219e013SMasahiro Yamada
3035f219e013SMasahiro Yamada        self.filename = None
3036f219e013SMasahiro Yamada        self.linenr = None
3037f219e013SMasahiro Yamada
303890c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
3039f219e013SMasahiro Yamada        if self.config._eval_expr(self.dep_expr) != "n":
304090c36d8aSUlf Magnusson            append_fn("\n#\n# {0}\n#".format(self.text))
3041f219e013SMasahiro Yamada
304290c36d8aSUlf Magnussonclass Kconfig_Syntax_Error(Exception):
304390c36d8aSUlf Magnusson    """Exception raised for syntax errors."""
304490c36d8aSUlf Magnusson    pass
3045f219e013SMasahiro Yamada
304690c36d8aSUlf Magnussonclass Internal_Error(Exception):
304790c36d8aSUlf Magnusson    """Exception raised for internal errors."""
304890c36d8aSUlf Magnusson    pass
304990c36d8aSUlf Magnusson
305090c36d8aSUlf Magnusson#
305190c36d8aSUlf Magnusson# Public functions
305290c36d8aSUlf Magnusson#
305390c36d8aSUlf Magnusson
305490c36d8aSUlf Magnussondef tri_less(v1, v2):
305590c36d8aSUlf Magnusson    """Returns True if the tristate v1 is less than the tristate v2, where "n",
305690c36d8aSUlf Magnusson    "m" and "y" are ordered from lowest to highest."""
305790c36d8aSUlf Magnusson    return TRI_TO_INT[v1] < TRI_TO_INT[v2]
305890c36d8aSUlf Magnusson
305990c36d8aSUlf Magnussondef tri_less_eq(v1, v2):
306090c36d8aSUlf Magnusson    """Returns True if the tristate v1 is less than or equal to the tristate
306190c36d8aSUlf Magnusson    v2, where "n", "m" and "y" are ordered from lowest to highest."""
306290c36d8aSUlf Magnusson    return TRI_TO_INT[v1] <= TRI_TO_INT[v2]
306390c36d8aSUlf Magnusson
306490c36d8aSUlf Magnussondef tri_greater(v1, v2):
306590c36d8aSUlf Magnusson    """Returns True if the tristate v1 is greater than the tristate v2, where
306690c36d8aSUlf Magnusson    "n", "m" and "y" are ordered from lowest to highest."""
306790c36d8aSUlf Magnusson    return TRI_TO_INT[v1] > TRI_TO_INT[v2]
306890c36d8aSUlf Magnusson
306990c36d8aSUlf Magnussondef tri_greater_eq(v1, v2):
307090c36d8aSUlf Magnusson    """Returns True if the tristate v1 is greater than or equal to the tristate
307190c36d8aSUlf Magnusson    v2, where "n", "m" and "y" are ordered from lowest to highest."""
307290c36d8aSUlf Magnusson    return TRI_TO_INT[v1] >= TRI_TO_INT[v2]
307390c36d8aSUlf Magnusson
307490c36d8aSUlf Magnusson#
307590c36d8aSUlf Magnusson# Internal classes
307690c36d8aSUlf Magnusson#
307790c36d8aSUlf Magnusson
307890c36d8aSUlf Magnussonclass _Feed(object):
307990c36d8aSUlf Magnusson
308090c36d8aSUlf Magnusson    """Class for working with sequences in a stream-like fashion; handy for
308190c36d8aSUlf Magnusson    tokens."""
308290c36d8aSUlf Magnusson
308390c36d8aSUlf Magnusson    # This would be more helpful on the item classes, but would remove some
308490c36d8aSUlf Magnusson    # flexibility
308590c36d8aSUlf Magnusson    __slots__ = ['items', 'length', 'i']
3086f219e013SMasahiro Yamada
3087f219e013SMasahiro Yamada    def __init__(self, items):
3088f219e013SMasahiro Yamada        self.items = items
3089f219e013SMasahiro Yamada        self.length = len(self.items)
3090f219e013SMasahiro Yamada        self.i = 0
3091f219e013SMasahiro Yamada
3092f219e013SMasahiro Yamada    def get_next(self):
3093f219e013SMasahiro Yamada        if self.i >= self.length:
3094f219e013SMasahiro Yamada            return None
3095f219e013SMasahiro Yamada        item = self.items[self.i]
3096f219e013SMasahiro Yamada        self.i += 1
3097f219e013SMasahiro Yamada        return item
3098f219e013SMasahiro Yamada
3099f219e013SMasahiro Yamada    def peek_next(self):
3100f219e013SMasahiro Yamada        return None if self.i >= self.length else self.items[self.i]
3101f219e013SMasahiro Yamada
3102f219e013SMasahiro Yamada    def check(self, token):
3103f219e013SMasahiro Yamada        """Check if the next token is 'token'. If so, remove it from the token
3104f219e013SMasahiro Yamada        feed and return True. Otherwise, leave it in and return False."""
310590c36d8aSUlf Magnusson        if self.i < self.length and self.items[self.i] == token:
3106f219e013SMasahiro Yamada            self.i += 1
3107f219e013SMasahiro Yamada            return True
3108f219e013SMasahiro Yamada        return False
3109f219e013SMasahiro Yamada
311090c36d8aSUlf Magnusson    def unget_all(self):
311190c36d8aSUlf Magnusson        self.i = 0
3112f219e013SMasahiro Yamada
311390c36d8aSUlf Magnussonclass _FileFeed(object):
3114f219e013SMasahiro Yamada
311590c36d8aSUlf Magnusson    """Feeds lines from a file. Keeps track of the filename and current line
311690c36d8aSUlf Magnusson    number. Joins any line ending in \\ with the following line. We need to be
311790c36d8aSUlf Magnusson    careful to get the line number right in the presence of continuation
311890c36d8aSUlf Magnusson    lines."""
3119f219e013SMasahiro Yamada
312090c36d8aSUlf Magnusson    __slots__ = ['filename', 'lines', 'length', 'linenr']
3121f219e013SMasahiro Yamada
312290c36d8aSUlf Magnusson    def __init__(self, filename):
3123f219e013SMasahiro Yamada        self.filename = _clean_up_path(filename)
312490c36d8aSUlf Magnusson        with open(filename, "r") as f:
312590c36d8aSUlf Magnusson            # No interleaving of I/O and processing yet. Don't know if it would
312690c36d8aSUlf Magnusson            # help.
312790c36d8aSUlf Magnusson            self.lines = f.readlines()
312890c36d8aSUlf Magnusson        self.length = len(self.lines)
312990c36d8aSUlf Magnusson        self.linenr = 0
3130f219e013SMasahiro Yamada
313190c36d8aSUlf Magnusson    def get_next(self):
313290c36d8aSUlf Magnusson        if self.linenr >= self.length:
313390c36d8aSUlf Magnusson            return None
313490c36d8aSUlf Magnusson        line = self.lines[self.linenr]
313590c36d8aSUlf Magnusson        self.linenr += 1
313690c36d8aSUlf Magnusson        while line.endswith("\\\n"):
313790c36d8aSUlf Magnusson            line = line[:-2] + self.lines[self.linenr]
313890c36d8aSUlf Magnusson            self.linenr += 1
313990c36d8aSUlf Magnusson        return line
3140f219e013SMasahiro Yamada
314190c36d8aSUlf Magnusson    def peek_next(self):
314290c36d8aSUlf Magnusson        linenr = self.linenr
314390c36d8aSUlf Magnusson        if linenr >= self.length:
314490c36d8aSUlf Magnusson            return None
314590c36d8aSUlf Magnusson        line = self.lines[linenr]
314690c36d8aSUlf Magnusson        while line.endswith("\\\n"):
314790c36d8aSUlf Magnusson            linenr += 1
314890c36d8aSUlf Magnusson            line = line[:-2] + self.lines[linenr]
314990c36d8aSUlf Magnusson        return line
315090c36d8aSUlf Magnusson
315190c36d8aSUlf Magnusson    def unget(self):
315290c36d8aSUlf Magnusson        self.linenr -= 1
315390c36d8aSUlf Magnusson        while self.lines[self.linenr].endswith("\\\n"):
315490c36d8aSUlf Magnusson            self.linenr -= 1
315590c36d8aSUlf Magnusson
315690c36d8aSUlf Magnusson    def next_nonblank(self):
315790c36d8aSUlf Magnusson        """Removes lines up to and including the next non-blank (not all-space)
315890c36d8aSUlf Magnusson        line and returns it. Returns None if there are no more non-blank
315990c36d8aSUlf Magnusson        lines."""
316090c36d8aSUlf Magnusson        while 1:
316190c36d8aSUlf Magnusson            line = self.get_next()
316290c36d8aSUlf Magnusson            if line is None or not line.isspace():
316390c36d8aSUlf Magnusson                return line
3164f219e013SMasahiro Yamada
3165f219e013SMasahiro Yamada#
316690c36d8aSUlf Magnusson# Internal functions
3167f219e013SMasahiro Yamada#
3168f219e013SMasahiro Yamada
316990c36d8aSUlf Magnussondef _get_visibility(sc):
317090c36d8aSUlf Magnusson    """Symbols and Choices have a "visibility" that acts as an upper bound on
317190c36d8aSUlf Magnusson    the values a user can set for them, corresponding to the visibility in e.g.
317290c36d8aSUlf Magnusson    'make menuconfig'. This function calculates the visibility for the Symbol
317390c36d8aSUlf Magnusson    or Choice 'sc' -- the logic is nearly identical."""
317490c36d8aSUlf Magnusson    if sc.cached_visibility is None:
317590c36d8aSUlf Magnusson        vis = "n"
317690c36d8aSUlf Magnusson        for _, cond_expr in sc.prompts:
317790c36d8aSUlf Magnusson            vis = sc.config._eval_max(vis, cond_expr)
3178f219e013SMasahiro Yamada
317990c36d8aSUlf Magnusson        if isinstance(sc, Symbol) and sc.is_choice_sym:
318090c36d8aSUlf Magnusson            vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
3181f219e013SMasahiro Yamada
318290c36d8aSUlf Magnusson        # Promote "m" to "y" if we're dealing with a non-tristate
318390c36d8aSUlf Magnusson        if vis == "m" and sc.type != TRISTATE:
318490c36d8aSUlf Magnusson            vis = "y"
3185f219e013SMasahiro Yamada
318690c36d8aSUlf Magnusson        sc.cached_visibility = vis
3187f219e013SMasahiro Yamada
318890c36d8aSUlf Magnusson    return sc.cached_visibility
3189f219e013SMasahiro Yamada
319090c36d8aSUlf Magnussondef _make_and(e1, e2):
319190c36d8aSUlf Magnusson    """Constructs an AND (&&) expression. Performs trivial simplification.
319290c36d8aSUlf Magnusson    Nones equate to 'y'.
319390c36d8aSUlf Magnusson
319490c36d8aSUlf Magnusson    Note: returns None if e1 == e2 == None."""
319590c36d8aSUlf Magnusson    if e1 is None or e1 == "y":
319690c36d8aSUlf Magnusson        return e2
319790c36d8aSUlf Magnusson    if e2 is None or e2 == "y":
319890c36d8aSUlf Magnusson        return e1
319990c36d8aSUlf Magnusson
320090c36d8aSUlf Magnusson    # Prefer to merge argument lists if possible to reduce the number of nodes
320190c36d8aSUlf Magnusson
320290c36d8aSUlf Magnusson    if isinstance(e1, tuple) and e1[0] == AND:
320390c36d8aSUlf Magnusson        if isinstance(e2, tuple) and e2[0] == AND:
320490c36d8aSUlf Magnusson            return (AND, e1[1] + e2[1])
320590c36d8aSUlf Magnusson        return (AND, e1[1] + [e2])
320690c36d8aSUlf Magnusson
320790c36d8aSUlf Magnusson    if isinstance(e2, tuple) and e2[0] == AND:
320890c36d8aSUlf Magnusson        return (AND, e2[1] + [e1])
320990c36d8aSUlf Magnusson
321090c36d8aSUlf Magnusson    return (AND, [e1, e2])
321190c36d8aSUlf Magnusson
321290c36d8aSUlf Magnussondef _make_or(e1, e2):
321390c36d8aSUlf Magnusson    """Constructs an OR (||) expression. Performs trivial simplification and
321490c36d8aSUlf Magnusson    avoids Nones. Nones equate to 'y', which is usually what we want, but needs
321590c36d8aSUlf Magnusson    to be kept in mind."""
321690c36d8aSUlf Magnusson
321790c36d8aSUlf Magnusson    # Perform trivial simplification and avoid None's (which
321890c36d8aSUlf Magnusson    # correspond to y's)
321990c36d8aSUlf Magnusson    if e1 is None or e2 is None or e1 == "y" or e2 == "y":
322090c36d8aSUlf Magnusson        return "y"
322190c36d8aSUlf Magnusson    if e1 == "n":
322290c36d8aSUlf Magnusson        return e2
322390c36d8aSUlf Magnusson
322490c36d8aSUlf Magnusson    # Prefer to merge argument lists if possible to reduce the number of nodes
322590c36d8aSUlf Magnusson
322690c36d8aSUlf Magnusson    if isinstance(e1, tuple) and e1[0] == OR:
322790c36d8aSUlf Magnusson        if isinstance(e2, tuple) and e2[0] == OR:
322890c36d8aSUlf Magnusson            return (OR, e1[1] + e2[1])
322990c36d8aSUlf Magnusson        return (OR, e1[1] + [e2])
323090c36d8aSUlf Magnusson
323190c36d8aSUlf Magnusson    if isinstance(e2, tuple) and e2[0] == OR:
323290c36d8aSUlf Magnusson        return (OR, e2[1] + [e1])
323390c36d8aSUlf Magnusson
323490c36d8aSUlf Magnusson    return (OR, [e1, e2])
323590c36d8aSUlf Magnusson
323690c36d8aSUlf Magnussondef _get_expr_syms_rec(expr, res):
323790c36d8aSUlf Magnusson    """_get_expr_syms() helper. Recurses through expressions."""
323890c36d8aSUlf Magnusson    if isinstance(expr, Symbol):
323990c36d8aSUlf Magnusson        res.add(expr)
324090c36d8aSUlf Magnusson    elif isinstance(expr, str):
324190c36d8aSUlf Magnusson        return
324290c36d8aSUlf Magnusson    elif expr[0] == AND or expr[0] == OR:
324390c36d8aSUlf Magnusson        for term in expr[1]:
324490c36d8aSUlf Magnusson            _get_expr_syms_rec(term, res)
324590c36d8aSUlf Magnusson    elif expr[0] == NOT:
324690c36d8aSUlf Magnusson        _get_expr_syms_rec(expr[1], res)
324790c36d8aSUlf Magnusson    elif expr[0] == EQUAL or expr[0] == UNEQUAL:
324890c36d8aSUlf Magnusson        if isinstance(expr[1], Symbol):
324990c36d8aSUlf Magnusson            res.add(expr[1])
325090c36d8aSUlf Magnusson        if isinstance(expr[2], Symbol):
325190c36d8aSUlf Magnusson            res.add(expr[2])
325290c36d8aSUlf Magnusson    else:
325390c36d8aSUlf Magnusson        _internal_error("Internal error while fetching symbols from an "
325490c36d8aSUlf Magnusson                        "expression with token stream {0}.".format(expr))
325590c36d8aSUlf Magnusson
325690c36d8aSUlf Magnussondef _get_expr_syms(expr):
325790c36d8aSUlf Magnusson    """Returns the set() of symbols appearing in expr."""
325890c36d8aSUlf Magnusson    res = set()
325990c36d8aSUlf Magnusson    if expr is not None:
326090c36d8aSUlf Magnusson        _get_expr_syms_rec(expr, res)
326190c36d8aSUlf Magnusson    return res
326290c36d8aSUlf Magnusson
326390c36d8aSUlf Magnussondef _str_val(obj):
326490c36d8aSUlf Magnusson    """Returns the value of obj as a string. If obj is not a string (constant
326590c36d8aSUlf Magnusson    symbol), it must be a Symbol."""
326690c36d8aSUlf Magnusson    return obj if isinstance(obj, str) else obj.get_value()
326790c36d8aSUlf Magnusson
326890c36d8aSUlf Magnussondef _make_block_conf(block, append_fn):
326990c36d8aSUlf Magnusson    """Returns a list of .config strings for a block (list) of items."""
327090c36d8aSUlf Magnusson
327190c36d8aSUlf Magnusson    # Collect the substrings in a list and later use join() instead of += to
327290c36d8aSUlf Magnusson    # build the final .config contents. With older Python versions, this yields
327390c36d8aSUlf Magnusson    # linear instead of quadratic complexity.
327490c36d8aSUlf Magnusson    for item in block:
327590c36d8aSUlf Magnusson        item._make_conf(append_fn)
327690c36d8aSUlf Magnusson
327790c36d8aSUlf Magnussondef _sym_str_string(sym_or_str):
327890c36d8aSUlf Magnusson    if isinstance(sym_or_str, str):
327990c36d8aSUlf Magnusson        return '"' + sym_or_str + '"'
328090c36d8aSUlf Magnusson    return sym_or_str.name
328190c36d8aSUlf Magnusson
328290c36d8aSUlf Magnussondef _intersperse(lst, op):
328390c36d8aSUlf Magnusson    """_expr_to_str() helper. Gets the string representation of each expression
328490c36d8aSUlf Magnusson    in lst and produces a list where op has been inserted between the
328590c36d8aSUlf Magnusson    elements."""
328690c36d8aSUlf Magnusson    if not lst:
3287f219e013SMasahiro Yamada        return ""
328890c36d8aSUlf Magnusson
328990c36d8aSUlf Magnusson    res = []
329090c36d8aSUlf Magnusson
329190c36d8aSUlf Magnusson    def handle_sub_expr(expr):
329290c36d8aSUlf Magnusson        no_parens = isinstance(expr, (str, Symbol)) or \
329390c36d8aSUlf Magnusson                    expr[0] in (EQUAL, UNEQUAL) or \
329490c36d8aSUlf Magnusson                    PRECEDENCE[op] <= PRECEDENCE[expr[0]]
329590c36d8aSUlf Magnusson        if not no_parens:
329690c36d8aSUlf Magnusson            res.append("(")
329790c36d8aSUlf Magnusson        res.extend(_expr_to_str_rec(expr))
329890c36d8aSUlf Magnusson        if not no_parens:
329990c36d8aSUlf Magnusson            res.append(")")
330090c36d8aSUlf Magnusson
330190c36d8aSUlf Magnusson    op_str = OP_TO_STR[op]
330290c36d8aSUlf Magnusson
330390c36d8aSUlf Magnusson    handle_sub_expr(lst[0])
330490c36d8aSUlf Magnusson    for expr in lst[1:]:
330590c36d8aSUlf Magnusson        res.append(op_str)
330690c36d8aSUlf Magnusson        handle_sub_expr(expr)
330790c36d8aSUlf Magnusson
330890c36d8aSUlf Magnusson    return res
330990c36d8aSUlf Magnusson
331090c36d8aSUlf Magnussondef _expr_to_str_rec(expr):
331190c36d8aSUlf Magnusson    if expr is None:
331290c36d8aSUlf Magnusson        return [""]
331390c36d8aSUlf Magnusson
331490c36d8aSUlf Magnusson    if isinstance(expr, (Symbol, str)):
331590c36d8aSUlf Magnusson        return [_sym_str_string(expr)]
331690c36d8aSUlf Magnusson
331790c36d8aSUlf Magnusson    if expr[0] in (AND, OR):
331890c36d8aSUlf Magnusson        return _intersperse(expr[1], expr[0])
331990c36d8aSUlf Magnusson
332090c36d8aSUlf Magnusson    if expr[0] == NOT:
332190c36d8aSUlf Magnusson        need_parens = not isinstance(expr[1], (str, Symbol))
332290c36d8aSUlf Magnusson
332390c36d8aSUlf Magnusson        res = ["!"]
332490c36d8aSUlf Magnusson        if need_parens:
332590c36d8aSUlf Magnusson            res.append("(")
332690c36d8aSUlf Magnusson        res.extend(_expr_to_str_rec(expr[1]))
332790c36d8aSUlf Magnusson        if need_parens:
332890c36d8aSUlf Magnusson            res.append(")")
332990c36d8aSUlf Magnusson        return res
333090c36d8aSUlf Magnusson
333190c36d8aSUlf Magnusson    if expr[0] in (EQUAL, UNEQUAL):
333290c36d8aSUlf Magnusson        return [_sym_str_string(expr[1]),
333390c36d8aSUlf Magnusson                OP_TO_STR[expr[0]],
333490c36d8aSUlf Magnusson                _sym_str_string(expr[2])]
333590c36d8aSUlf Magnusson
333690c36d8aSUlf Magnussondef _expr_to_str(expr):
333790c36d8aSUlf Magnusson    return "".join(_expr_to_str_rec(expr))
3338f219e013SMasahiro Yamada
3339f219e013SMasahiro Yamadadef _indentation(line):
334090c36d8aSUlf Magnusson    """Returns the length of the line's leading whitespace, treating tab stops
334190c36d8aSUlf Magnusson    as being spaced 8 characters apart."""
334290c36d8aSUlf Magnusson    line = line.expandtabs()
334390c36d8aSUlf Magnusson    return len(line) - len(line.lstrip())
3344f219e013SMasahiro Yamada
3345f219e013SMasahiro Yamadadef _deindent(line, indent):
3346f219e013SMasahiro Yamada    """Deindent 'line' by 'indent' spaces."""
3347f219e013SMasahiro Yamada    line = line.expandtabs()
3348f219e013SMasahiro Yamada    if len(line) <= indent:
3349f219e013SMasahiro Yamada        return line
3350f219e013SMasahiro Yamada    return line[indent:]
3351f219e013SMasahiro Yamada
3352f219e013SMasahiro Yamadadef _is_base_n(s, n):
3353f219e013SMasahiro Yamada    try:
3354f219e013SMasahiro Yamada        int(s, n)
3355f219e013SMasahiro Yamada        return True
3356f219e013SMasahiro Yamada    except ValueError:
3357f219e013SMasahiro Yamada        return False
3358f219e013SMasahiro Yamada
335990c36d8aSUlf Magnussondef _lines(*args):
336090c36d8aSUlf Magnusson    """Returns a string consisting of all arguments, with newlines inserted
3361f219e013SMasahiro Yamada    between them."""
3362f219e013SMasahiro Yamada    return "\n".join(args)
3363f219e013SMasahiro Yamada
3364f219e013SMasahiro Yamadadef _comment(s):
3365f219e013SMasahiro Yamada    """Returns a new string with "#" inserted before each line in 's'."""
3366f219e013SMasahiro Yamada    if not s:
3367f219e013SMasahiro Yamada        return "#"
3368f219e013SMasahiro Yamada    res = "".join(["#" + line for line in s.splitlines(True)])
3369f219e013SMasahiro Yamada    if s.endswith("\n"):
3370f219e013SMasahiro Yamada        return res + "#"
3371f219e013SMasahiro Yamada    return res
3372f219e013SMasahiro Yamada
3373f219e013SMasahiro Yamadadef _clean_up_path(path):
337490c36d8aSUlf Magnusson    """Strips an initial "./" and any trailing slashes from 'path'."""
3375f219e013SMasahiro Yamada    if path.startswith("./"):
3376f219e013SMasahiro Yamada        path = path[2:]
337790c36d8aSUlf Magnusson    return path.rstrip("/")
3378f219e013SMasahiro Yamada
3379*0c69cd52SSimon Glassdef _build_msg(msg, filename, linenr):
3380f219e013SMasahiro Yamada    if filename is not None:
3381*0c69cd52SSimon Glass        msg = "{0}:{1}: ".format(_clean_up_path(filename), linenr) + msg
3382*0c69cd52SSimon Glass    return msg
3383*0c69cd52SSimon Glass
3384*0c69cd52SSimon Glassdef _stderr_msg(msg, filename, linenr):
3385*0c69cd52SSimon Glass    sys.stderr.write(_build_msg(msg, filename, linenr) + "\n")
3386f219e013SMasahiro Yamada
338790c36d8aSUlf Magnussondef _tokenization_error(s, filename, linenr):
338890c36d8aSUlf Magnusson    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
338990c36d8aSUlf Magnusson    raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'"
339090c36d8aSUlf Magnusson                               .format(loc, s.strip()))
3391f219e013SMasahiro Yamada
3392f219e013SMasahiro Yamadadef _parse_error(s, msg, filename, linenr):
339390c36d8aSUlf Magnusson    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
339490c36d8aSUlf Magnusson    raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}"
339590c36d8aSUlf Magnusson                               .format(loc, s.strip(),
339690c36d8aSUlf Magnusson                                       "." if msg is None else ": " + msg))
3397f219e013SMasahiro Yamada
3398f219e013SMasahiro Yamadadef _internal_error(msg):
339990c36d8aSUlf Magnusson    raise Internal_Error(msg +
340090c36d8aSUlf Magnusson      "\nSorry! You may want to send an email to ulfalizer a.t Google's "
340190c36d8aSUlf Magnusson      "email service to tell me about this. Include the message above and the "
340290c36d8aSUlf Magnusson      "stack trace and describe what you were doing.")
3403f219e013SMasahiro Yamada
340490c36d8aSUlf Magnusson#
340590c36d8aSUlf Magnusson# Internal global constants
340690c36d8aSUlf Magnusson#
3407f219e013SMasahiro Yamada
340890c36d8aSUlf Magnusson# Tokens
340990c36d8aSUlf Magnusson(T_AND, T_OR, T_NOT,
341090c36d8aSUlf Magnusson T_OPEN_PAREN, T_CLOSE_PAREN,
341190c36d8aSUlf Magnusson T_EQUAL, T_UNEQUAL,
341290c36d8aSUlf Magnusson T_MAINMENU, T_MENU, T_ENDMENU,
341390c36d8aSUlf Magnusson T_SOURCE, T_CHOICE, T_ENDCHOICE,
341490c36d8aSUlf Magnusson T_COMMENT, T_CONFIG, T_MENUCONFIG,
341590c36d8aSUlf Magnusson T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON,
341690c36d8aSUlf Magnusson T_OPTIONAL, T_PROMPT, T_DEFAULT,
341790c36d8aSUlf Magnusson T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING,
341890c36d8aSUlf Magnusson T_DEF_BOOL, T_DEF_TRISTATE,
3419d036107aSTom Rini T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV,
3420d036107aSTom Rini T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40)
3421f219e013SMasahiro Yamada
342290c36d8aSUlf Magnusson# The leading underscore before the function assignments below prevent pydoc
342390c36d8aSUlf Magnusson# from listing them. The constants could be hidden too, but they're fairly
342490c36d8aSUlf Magnusson# obviously internal anyway, so don't bother spamming the code.
3425f219e013SMasahiro Yamada
342690c36d8aSUlf Magnusson# Keyword to token map. Note that the get() method is assigned directly as a
342790c36d8aSUlf Magnusson# small optimization.
342890c36d8aSUlf Magnusson_get_keyword = \
342990c36d8aSUlf Magnusson  {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU,
343090c36d8aSUlf Magnusson   "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE,
343190c36d8aSUlf Magnusson   "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT,
343290c36d8aSUlf Magnusson   "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF,
343390c36d8aSUlf Magnusson   "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL,
343490c36d8aSUlf Magnusson   "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
343590c36d8aSUlf Magnusson   "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
343690c36d8aSUlf Magnusson   "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
3437d036107aSTom Rini   "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION,
3438d036107aSTom Rini   "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV,
3439d036107aSTom Rini   "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
344090c36d8aSUlf Magnusson   "visible": T_VISIBLE}.get
344190c36d8aSUlf Magnusson
344290c36d8aSUlf Magnusson# Strings to use for True and False
344390c36d8aSUlf MagnussonBOOL_STR = {False: "false", True: "true"}
344490c36d8aSUlf Magnusson
344590c36d8aSUlf Magnusson# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE
344690c36d8aSUlf Magnusson# is included to avoid symbols being registered for named choices.
344790c36d8aSUlf MagnussonSTRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE,
344890c36d8aSUlf Magnusson                        T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU))
344990c36d8aSUlf Magnusson
345090c36d8aSUlf Magnusson# Matches the initial token on a line; see _tokenize(). Also eats trailing
345190c36d8aSUlf Magnusson# whitespace as an optimization.
345290c36d8aSUlf Magnusson_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match
345390c36d8aSUlf Magnusson
345490c36d8aSUlf Magnusson# Matches an identifier/keyword optionally preceded by whitespace. Also eats
345590c36d8aSUlf Magnusson# trailing whitespace as an optimization.
345690c36d8aSUlf Magnusson_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
345790c36d8aSUlf Magnusson
345890c36d8aSUlf Magnusson# Regular expressions for parsing .config files
345990c36d8aSUlf Magnusson_set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match
346090c36d8aSUlf Magnusson_unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match
346190c36d8aSUlf Magnusson
346290c36d8aSUlf Magnusson# Regular expression for finding $-references to symbols in strings
346390c36d8aSUlf Magnusson_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search
346490c36d8aSUlf Magnusson
346590c36d8aSUlf Magnusson# Integers representing symbol types
346690c36d8aSUlf MagnussonUNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6)
346790c36d8aSUlf Magnusson
346890c36d8aSUlf Magnusson# Strings to use for types
346990c36d8aSUlf MagnussonTYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate",
347090c36d8aSUlf Magnusson            STRING: "string", HEX: "hex", INT: "int"}
347190c36d8aSUlf Magnusson
347290c36d8aSUlf Magnusson# Token to type mapping
347390c36d8aSUlf MagnussonTOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING,
347490c36d8aSUlf Magnusson                 T_INT: INT, T_HEX: HEX}
347590c36d8aSUlf Magnusson
347690c36d8aSUlf Magnusson# Default values for symbols of different types (the value the symbol gets if
347790c36d8aSUlf Magnusson# it is not assigned a user value and none of its 'default' clauses kick in)
347890c36d8aSUlf MagnussonDEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""}
347990c36d8aSUlf Magnusson
348090c36d8aSUlf Magnusson# Indicates that no item is selected in a choice statement
348190c36d8aSUlf MagnussonNO_SELECTION = 0
348290c36d8aSUlf Magnusson
348390c36d8aSUlf Magnusson# Integers representing expression types
348490c36d8aSUlf MagnussonAND, OR, NOT, EQUAL, UNEQUAL = range(5)
348590c36d8aSUlf Magnusson
348690c36d8aSUlf Magnusson# Map from tristate values to integers
348790c36d8aSUlf MagnussonTRI_TO_INT = {"n": 0, "m": 1, "y": 2}
348890c36d8aSUlf Magnusson
348990c36d8aSUlf Magnusson# Printing-related stuff
349090c36d8aSUlf Magnusson
349190c36d8aSUlf MagnussonOP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "}
349290c36d8aSUlf MagnussonPRECEDENCE = {OR: 0, AND: 1, NOT: 2}
3493