xref: /OK3568_Linux_fs/u-boot/tools/buildman/kconfiglib.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# SPDX-License-Identifier:	ISC
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Author: Ulf Magnusson
5*4882a593Smuzhiyun#   https://github.com/ulfalizer/Kconfiglib
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun# This is Kconfiglib, a Python library for scripting, debugging, and extracting
8*4882a593Smuzhiyun# information from Kconfig-based configuration systems. To view the
9*4882a593Smuzhiyun# documentation, run
10*4882a593Smuzhiyun#
11*4882a593Smuzhiyun#  $ pydoc kconfiglib
12*4882a593Smuzhiyun#
13*4882a593Smuzhiyun# or, if you prefer HTML,
14*4882a593Smuzhiyun#
15*4882a593Smuzhiyun#  $ pydoc -w kconfiglib
16*4882a593Smuzhiyun#
17*4882a593Smuzhiyun# The examples/ subdirectory contains examples, to be run with e.g.
18*4882a593Smuzhiyun#
19*4882a593Smuzhiyun#  $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
20*4882a593Smuzhiyun#
21*4882a593Smuzhiyun# Look in testsuite.py for the test suite.
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun"""
24*4882a593SmuzhiyunKconfiglib is a Python library for scripting and extracting information from
25*4882a593SmuzhiyunKconfig-based configuration systems. Features include the following:
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun - Symbol values and properties can be looked up and values assigned
28*4882a593Smuzhiyun   programmatically.
29*4882a593Smuzhiyun - .config files can be read and written.
30*4882a593Smuzhiyun - Expressions can be evaluated in the context of a Kconfig configuration.
31*4882a593Smuzhiyun - Relations between symbols can be quickly determined, such as finding all
32*4882a593Smuzhiyun   symbols that reference a particular symbol.
33*4882a593Smuzhiyun - Highly compatible with the scripts/kconfig/*conf utilities. The test suite
34*4882a593Smuzhiyun   automatically compares outputs between Kconfiglib and the C implementation
35*4882a593Smuzhiyun   for a large number of cases.
36*4882a593Smuzhiyun
37*4882a593SmuzhiyunFor the Linux kernel, scripts are run using
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>]
40*4882a593Smuzhiyun
41*4882a593SmuzhiyunUsing the 'scriptconfig' target ensures that required environment variables
42*4882a593Smuzhiyun(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly.
43*4882a593Smuzhiyun
44*4882a593SmuzhiyunScripts receive the name of the Kconfig file to load in sys.argv[1]. As of
45*4882a593SmuzhiyunLinux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory.
46*4882a593SmuzhiyunIf an argument is provided with SCRIPT_ARG, it appears as sys.argv[2].
47*4882a593Smuzhiyun
48*4882a593SmuzhiyunTo get an interactive Python prompt with Kconfiglib preloaded and a Config
49*4882a593Smuzhiyunobject 'c' created, run
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun $ make iscriptconfig [ARCH=<arch>]
52*4882a593Smuzhiyun
53*4882a593SmuzhiyunKconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python
54*4882a593Smuzhiyuninterpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy
55*4882a593Smuzhiyunworks well too, and might give a nice speedup for long-running jobs.
56*4882a593Smuzhiyun
57*4882a593SmuzhiyunThe examples/ directory contains short example scripts, which can be run with
58*4882a593Smuzhiyune.g.
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
61*4882a593Smuzhiyun
62*4882a593Smuzhiyunor
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel
65*4882a593Smuzhiyun
66*4882a593Smuzhiyuntestsuite.py contains the test suite. See the top of the script for how to run
67*4882a593Smuzhiyunit.
68*4882a593Smuzhiyun
69*4882a593SmuzhiyunCredits: Written by Ulf "Ulfalizer" Magnusson
70*4882a593Smuzhiyun
71*4882a593SmuzhiyunSend bug reports, suggestions and other feedback to ulfalizer a.t Google's
72*4882a593Smuzhiyunemail service. Don't wrestle with internal APIs. Tell me what you need and I
73*4882a593Smuzhiyunmight add it in a safe way as a client API instead."""
74*4882a593Smuzhiyun
75*4882a593Smuzhiyunimport os
76*4882a593Smuzhiyunimport re
77*4882a593Smuzhiyunimport sys
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun# File layout:
80*4882a593Smuzhiyun#
81*4882a593Smuzhiyun# Public classes
82*4882a593Smuzhiyun# Public functions
83*4882a593Smuzhiyun# Internal classes
84*4882a593Smuzhiyun# Internal functions
85*4882a593Smuzhiyun# Internal global constants
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun# Line length: 79 columns
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun#
90*4882a593Smuzhiyun# Public classes
91*4882a593Smuzhiyun#
92*4882a593Smuzhiyun
93*4882a593Smuzhiyunclass Config(object):
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun    """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the
96*4882a593Smuzhiyun    set of symbols and other items appearing in the configuration together with
97*4882a593Smuzhiyun    their values. Creating any number of Config objects -- including for
98*4882a593Smuzhiyun    different architectures -- is safe; Kconfiglib has no global state."""
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun    #
101*4882a593Smuzhiyun    # Public interface
102*4882a593Smuzhiyun    #
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun    def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True,
105*4882a593Smuzhiyun                 print_undef_assign=False):
106*4882a593Smuzhiyun        """Creates a new Config object, representing a Kconfig configuration.
107*4882a593Smuzhiyun        Raises Kconfig_Syntax_Error on syntax errors.
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun        filename (default: "Kconfig"): The base Kconfig file of the
110*4882a593Smuzhiyun           configuration. For the Linux kernel, you'll probably want "Kconfig"
111*4882a593Smuzhiyun           from the top-level directory, as environment variables will make
112*4882a593Smuzhiyun           sure the right Kconfig is included from there
113*4882a593Smuzhiyun           (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make
114*4882a593Smuzhiyun           scriptconfig', the filename of the base base Kconfig file will be in
115*4882a593Smuzhiyun           sys.argv[1].
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun        base_dir (default: None): The base directory relative to which 'source'
118*4882a593Smuzhiyun           statements within Kconfig files will work. For the Linux kernel this
119*4882a593Smuzhiyun           should be the top-level directory of the kernel tree. $-references
120*4882a593Smuzhiyun           to existing environment variables will be expanded.
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun           If None (the default), the environment variable 'srctree' will be
123*4882a593Smuzhiyun           used if set, and the current directory otherwise. 'srctree' is set
124*4882a593Smuzhiyun           by the Linux makefiles to the top-level kernel directory. A default
125*4882a593Smuzhiyun           of "." would not work with an alternative build directory.
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun        print_warnings (default: True): Set to True if warnings related to this
128*4882a593Smuzhiyun           configuration should be printed to stderr. This can be changed later
129*4882a593Smuzhiyun           with Config.set_print_warnings(). It is provided as a constructor
130*4882a593Smuzhiyun           argument since warnings might be generated during parsing.
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun        print_undef_assign (default: False): Set to True if informational
133*4882a593Smuzhiyun           messages related to assignments to undefined symbols should be
134*4882a593Smuzhiyun           printed to stderr for this configuration. Can be changed later with
135*4882a593Smuzhiyun           Config.set_print_undef_assign()."""
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun        # The set of all symbols, indexed by name (a string)
138*4882a593Smuzhiyun        self.syms = {}
139*4882a593Smuzhiyun        # Python 2/3 compatibility hack. This is the only one needed.
140*4882a593Smuzhiyun        if sys.version_info[0] >= 3:
141*4882a593Smuzhiyun            self.syms_iter = self.syms.values
142*4882a593Smuzhiyun        else:
143*4882a593Smuzhiyun            self.syms_iter = self.syms.itervalues
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun        # The set of all defined symbols in the configuration in the order they
146*4882a593Smuzhiyun        # appear in the Kconfig files. This excludes the special symbols n, m,
147*4882a593Smuzhiyun        # and y as well as symbols that are referenced but never defined.
148*4882a593Smuzhiyun        self.kconfig_syms = []
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun        # The set of all named choices (yes, choices can have names), indexed
151*4882a593Smuzhiyun        # by name (a string)
152*4882a593Smuzhiyun        self.named_choices = {}
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun        # Lists containing all choices, menus and comments in the configuration
155*4882a593Smuzhiyun        self.choices = []
156*4882a593Smuzhiyun        self.menus = []
157*4882a593Smuzhiyun        self.comments = []
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun        def register_special_symbol(type_, name, val):
160*4882a593Smuzhiyun            sym = Symbol()
161*4882a593Smuzhiyun            sym.is_special_ = True
162*4882a593Smuzhiyun            sym.is_defined_ = True
163*4882a593Smuzhiyun            sym.config = self
164*4882a593Smuzhiyun            sym.name = name
165*4882a593Smuzhiyun            sym.type = type_
166*4882a593Smuzhiyun            sym.cached_val = val
167*4882a593Smuzhiyun            self.syms[name] = sym
168*4882a593Smuzhiyun            return sym
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun        # The special symbols n, m and y, used as shorthand for "n", "m" and
171*4882a593Smuzhiyun        # "y"
172*4882a593Smuzhiyun        self.n = register_special_symbol(TRISTATE, "n", "n")
173*4882a593Smuzhiyun        self.m = register_special_symbol(TRISTATE, "m", "m")
174*4882a593Smuzhiyun        self.y = register_special_symbol(TRISTATE, "y", "y")
175*4882a593Smuzhiyun        # DEFCONFIG_LIST uses this
176*4882a593Smuzhiyun        register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2])
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun        # The symbol with "option defconfig_list" set, containing a list of
179*4882a593Smuzhiyun        # default .config files
180*4882a593Smuzhiyun        self.defconfig_sym = None
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun        # See Symbol.get_(src)arch()
183*4882a593Smuzhiyun        self.arch = os.environ.get("ARCH")
184*4882a593Smuzhiyun        self.srcarch = os.environ.get("SRCARCH")
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun        # See Config.__init__(). We need this for get_defconfig_filename().
187*4882a593Smuzhiyun        self.srctree = os.environ.get("srctree")
188*4882a593Smuzhiyun        if self.srctree is None:
189*4882a593Smuzhiyun            self.srctree = "."
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun        self.filename = filename
192*4882a593Smuzhiyun        if base_dir is None:
193*4882a593Smuzhiyun            self.base_dir = self.srctree
194*4882a593Smuzhiyun        else:
195*4882a593Smuzhiyun            self.base_dir = os.path.expandvars(base_dir)
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun        # The 'mainmenu' text
198*4882a593Smuzhiyun        self.mainmenu_text = None
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun        # The filename of the most recently loaded .config file
201*4882a593Smuzhiyun        self.config_filename = None
202*4882a593Smuzhiyun        # The textual header of the most recently loaded .config, uncommented
203*4882a593Smuzhiyun        self.config_header = None
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun        self.print_warnings = print_warnings
206*4882a593Smuzhiyun        self.print_undef_assign = print_undef_assign
207*4882a593Smuzhiyun        self._warnings = []
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun        # For parsing routines that stop when finding a line belonging to a
210*4882a593Smuzhiyun        # different construct, these holds that line and the tokenized version
211*4882a593Smuzhiyun        # of that line. The purpose is to avoid having to re-tokenize the line,
212*4882a593Smuzhiyun        # which is inefficient and causes problems when recording references to
213*4882a593Smuzhiyun        # symbols.
214*4882a593Smuzhiyun        self.end_line = None
215*4882a593Smuzhiyun        self.end_line_tokens = None
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun        # See the comment in _parse_expr().
218*4882a593Smuzhiyun        self._cur_item = None
219*4882a593Smuzhiyun        self._line = None
220*4882a593Smuzhiyun        self._filename = None
221*4882a593Smuzhiyun        self._linenr = None
222*4882a593Smuzhiyun        self._transform_m = None
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun        # Parse the Kconfig files
225*4882a593Smuzhiyun        self.top_block = self._parse_file(filename, None, None, None)
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun        # Build Symbol.dep for all symbols
228*4882a593Smuzhiyun        self._build_dep()
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun    def get_arch(self):
231*4882a593Smuzhiyun        """Returns the value the environment variable ARCH had at the time the
232*4882a593Smuzhiyun        Config instance was created, or None if ARCH was not set. For the
233*4882a593Smuzhiyun        kernel, this corresponds to the architecture being built for, with
234*4882a593Smuzhiyun        values such as "i386" or "mips"."""
235*4882a593Smuzhiyun        return self.arch
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun    def get_srcarch(self):
238*4882a593Smuzhiyun        """Returns the value the environment variable SRCARCH had at the time
239*4882a593Smuzhiyun        the Config instance was created, or None if SRCARCH was not set. For
240*4882a593Smuzhiyun        the kernel, this corresponds to the particular arch/ subdirectory
241*4882a593Smuzhiyun        containing architecture-specific code."""
242*4882a593Smuzhiyun        return self.srcarch
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun    def get_srctree(self):
245*4882a593Smuzhiyun        """Returns the value the environment variable srctree had at the time
246*4882a593Smuzhiyun        the Config instance was created, or None if srctree was not defined.
247*4882a593Smuzhiyun        This variable points to the source directory and is used when building
248*4882a593Smuzhiyun        in a separate directory."""
249*4882a593Smuzhiyun        return self.srctree
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun    def get_base_dir(self):
252*4882a593Smuzhiyun        """Returns the base directory relative to which 'source' statements
253*4882a593Smuzhiyun        will work, passed as an argument to Config.__init__()."""
254*4882a593Smuzhiyun        return self.base_dir
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun    def get_kconfig_filename(self):
257*4882a593Smuzhiyun        """Returns the name of the (base) kconfig file this configuration was
258*4882a593Smuzhiyun        loaded from."""
259*4882a593Smuzhiyun        return self.filename
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun    def get_config_filename(self):
262*4882a593Smuzhiyun        """Returns the filename of the most recently loaded configuration file,
263*4882a593Smuzhiyun        or None if no configuration has been loaded."""
264*4882a593Smuzhiyun        return self.config_filename
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun    def get_config_header(self):
267*4882a593Smuzhiyun        """Returns the (uncommented) textual header of the .config file most
268*4882a593Smuzhiyun        recently loaded with load_config(). Returns None if no .config file has
269*4882a593Smuzhiyun        been loaded or if the most recently loaded .config file has no header.
270*4882a593Smuzhiyun        The header consists of all lines up to but not including the first line
271*4882a593Smuzhiyun        that either
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun        1. Does not start with "#"
274*4882a593Smuzhiyun        2. Has the form "# CONFIG_FOO is not set."
275*4882a593Smuzhiyun        """
276*4882a593Smuzhiyun        return self.config_header
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun    def get_mainmenu_text(self):
279*4882a593Smuzhiyun        """Returns the text of the 'mainmenu' statement (with $-references to
280*4882a593Smuzhiyun        symbols replaced by symbol values), or None if the configuration has no
281*4882a593Smuzhiyun        'mainmenu' statement."""
282*4882a593Smuzhiyun        return None if self.mainmenu_text is None else \
283*4882a593Smuzhiyun          self._expand_sym_refs(self.mainmenu_text)
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun    def get_defconfig_filename(self):
286*4882a593Smuzhiyun        """Returns the name of the defconfig file, which is the first existing
287*4882a593Smuzhiyun        file in the list given in a symbol having 'option defconfig_list' set.
288*4882a593Smuzhiyun        $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if
289*4882a593Smuzhiyun        FOO has the value "foo"). Returns None in case of no defconfig file.
290*4882a593Smuzhiyun        Setting 'option defconfig_list' on multiple symbols currently results
291*4882a593Smuzhiyun        in undefined behavior.
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun        If the environment variable 'srctree' was set when the Config was
294*4882a593Smuzhiyun        created, get_defconfig_filename() will first look relative to that
295*4882a593Smuzhiyun        directory before looking in the current directory; see
296*4882a593Smuzhiyun        Config.__init__().
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun        WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses
299*4882a593Smuzhiyun        the --defconfig=<defconfig> option when calling the C implementation of
300*4882a593Smuzhiyun        e.g. 'make defconfig'. This option overrides the 'option
301*4882a593Smuzhiyun        defconfig_list' symbol, meaning the result from
302*4882a593Smuzhiyun        get_defconfig_filename() might not match what 'make defconfig' would
303*4882a593Smuzhiyun        use. That probably ought to be worked around somehow, so that this
304*4882a593Smuzhiyun        function always gives the "expected" result."""
305*4882a593Smuzhiyun        if self.defconfig_sym is None:
306*4882a593Smuzhiyun            return None
307*4882a593Smuzhiyun        for filename, cond_expr in self.defconfig_sym.def_exprs:
308*4882a593Smuzhiyun            if self._eval_expr(cond_expr) == "y":
309*4882a593Smuzhiyun                filename = self._expand_sym_refs(filename)
310*4882a593Smuzhiyun                # We first look in $srctree. os.path.join() won't work here as
311*4882a593Smuzhiyun                # an absolute path in filename would override $srctree.
312*4882a593Smuzhiyun                srctree_filename = os.path.normpath(self.srctree + "/" +
313*4882a593Smuzhiyun                                                    filename)
314*4882a593Smuzhiyun                if os.path.exists(srctree_filename):
315*4882a593Smuzhiyun                    return srctree_filename
316*4882a593Smuzhiyun                if os.path.exists(filename):
317*4882a593Smuzhiyun                    return filename
318*4882a593Smuzhiyun        return None
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun    def get_symbol(self, name):
321*4882a593Smuzhiyun        """Returns the symbol with name 'name', or None if no such symbol
322*4882a593Smuzhiyun        appears in the configuration. An alternative shorthand is conf[name],
323*4882a593Smuzhiyun        where conf is a Config instance, though that will instead raise
324*4882a593Smuzhiyun        KeyError if the symbol does not exist."""
325*4882a593Smuzhiyun        return self.syms.get(name)
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun    def __getitem__(self, name):
328*4882a593Smuzhiyun        """Returns the symbol with name 'name'. Raises KeyError if the symbol
329*4882a593Smuzhiyun        does not appear in the configuration."""
330*4882a593Smuzhiyun        return self.syms[name]
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun    def get_symbols(self, all_symbols=True):
333*4882a593Smuzhiyun        """Returns a list of symbols from the configuration. An alternative for
334*4882a593Smuzhiyun        iterating over all defined symbols (in the order of definition) is
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun        for sym in config:
337*4882a593Smuzhiyun            ...
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun        which relies on Config implementing __iter__() and is equivalent to
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun        for sym in config.get_symbols(False):
342*4882a593Smuzhiyun            ...
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun        all_symbols (default: True): If True, all symbols -- including special
345*4882a593Smuzhiyun           and undefined symbols -- will be included in the result, in an
346*4882a593Smuzhiyun           undefined order. If False, only symbols actually defined and not
347*4882a593Smuzhiyun           merely referred to in the configuration will be included in the
348*4882a593Smuzhiyun           result, and will appear in the order that they are defined within
349*4882a593Smuzhiyun           the Kconfig configuration files."""
350*4882a593Smuzhiyun        return list(self.syms.values()) if all_symbols else self.kconfig_syms
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun    def __iter__(self):
353*4882a593Smuzhiyun        """Convenience function for iterating over the set of all defined
354*4882a593Smuzhiyun        symbols in the configuration, used like
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun        for sym in conf:
357*4882a593Smuzhiyun            ...
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun        The iteration happens in the order of definition within the Kconfig
360*4882a593Smuzhiyun        configuration files. Symbols only referred to but not defined will not
361*4882a593Smuzhiyun        be included, nor will the special symbols n, m, and y. If you want to
362*4882a593Smuzhiyun        include such symbols as well, see config.get_symbols()."""
363*4882a593Smuzhiyun        return iter(self.kconfig_syms)
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun    def get_choices(self):
366*4882a593Smuzhiyun        """Returns a list containing all choice statements in the
367*4882a593Smuzhiyun        configuration, in the order they appear in the Kconfig files."""
368*4882a593Smuzhiyun        return self.choices
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun    def get_menus(self):
371*4882a593Smuzhiyun        """Returns a list containing all menus in the configuration, in the
372*4882a593Smuzhiyun        order they appear in the Kconfig files."""
373*4882a593Smuzhiyun        return self.menus
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun    def get_comments(self):
376*4882a593Smuzhiyun        """Returns a list containing all comments in the configuration, in the
377*4882a593Smuzhiyun        order they appear in the Kconfig files."""
378*4882a593Smuzhiyun        return self.comments
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun    def get_top_level_items(self):
381*4882a593Smuzhiyun        """Returns a list containing the items (symbols, menus, choices, and
382*4882a593Smuzhiyun        comments) at the top level of the configuration -- that is, all items
383*4882a593Smuzhiyun        that do not appear within a menu or choice. The items appear in the
384*4882a593Smuzhiyun        same order as within the configuration."""
385*4882a593Smuzhiyun        return self.top_block
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun    def load_config(self, filename, replace=True):
388*4882a593Smuzhiyun        """Loads symbol values from a file in the familiar .config format.
389*4882a593Smuzhiyun        Equivalent to calling Symbol.set_user_value() to set each of the
390*4882a593Smuzhiyun        values.
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun        "# CONFIG_FOO is not set" within a .config file is treated specially
393*4882a593Smuzhiyun        and sets the user value of FOO to 'n'. The C implementation works the
394*4882a593Smuzhiyun        same way.
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun        filename: The .config file to load. $-references to existing
397*4882a593Smuzhiyun          environment variables will be expanded. For scripts to work even when
398*4882a593Smuzhiyun          an alternative build directory is used with the Linux kernel, you
399*4882a593Smuzhiyun          need to refer to the top-level kernel directory with "$srctree".
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun        replace (default: True): True if the configuration should replace the
402*4882a593Smuzhiyun           old configuration; False if it should add to it.
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun        Returns a list or warnings (hopefully empty)
405*4882a593Smuzhiyun        """
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun        self._warnings = []
408*4882a593Smuzhiyun        # Put this first so that a missing file doesn't screw up our state
409*4882a593Smuzhiyun        filename = os.path.expandvars(filename)
410*4882a593Smuzhiyun        line_feeder = _FileFeed(filename)
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun        self.config_filename = filename
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun        #
415*4882a593Smuzhiyun        # Read header
416*4882a593Smuzhiyun        #
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun        def is_header_line(line):
419*4882a593Smuzhiyun            return line is not None and line.startswith("#") and \
420*4882a593Smuzhiyun                   not _unset_re_match(line)
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun        self.config_header = None
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun        line = line_feeder.peek_next()
425*4882a593Smuzhiyun        if is_header_line(line):
426*4882a593Smuzhiyun            self.config_header = ""
427*4882a593Smuzhiyun            while is_header_line(line_feeder.peek_next()):
428*4882a593Smuzhiyun                self.config_header += line_feeder.get_next()[1:]
429*4882a593Smuzhiyun            # Remove trailing newline
430*4882a593Smuzhiyun            if self.config_header.endswith("\n"):
431*4882a593Smuzhiyun                self.config_header = self.config_header[:-1]
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun        #
434*4882a593Smuzhiyun        # Read assignments. Hotspot for some workloads.
435*4882a593Smuzhiyun        #
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun        def warn_override(filename, linenr, name, old_user_val, new_user_val):
438*4882a593Smuzhiyun            self._warn('overriding the value of {0}. '
439*4882a593Smuzhiyun                       'Old value: "{1}", new value: "{2}".'
440*4882a593Smuzhiyun                       .format(name, old_user_val, new_user_val),
441*4882a593Smuzhiyun                       filename, linenr)
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun        # Invalidate everything to keep things simple. It might be possible to
444*4882a593Smuzhiyun        # improve performance for the case where multiple configurations are
445*4882a593Smuzhiyun        # loaded by only invalidating a symbol (and its dependent symbols) if
446*4882a593Smuzhiyun        # the new user value differs from the old. One complication would be
447*4882a593Smuzhiyun        # that symbols not mentioned in the .config must lose their user value
448*4882a593Smuzhiyun        # when replace = True, which is the usual case.
449*4882a593Smuzhiyun        if replace:
450*4882a593Smuzhiyun            self.unset_user_values()
451*4882a593Smuzhiyun        else:
452*4882a593Smuzhiyun            self._invalidate_all()
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun        while 1:
455*4882a593Smuzhiyun            line = line_feeder.get_next()
456*4882a593Smuzhiyun            if line is None:
457*4882a593Smuzhiyun                return self._warnings
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun            line = line.rstrip()
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun            set_match = _set_re_match(line)
462*4882a593Smuzhiyun            if set_match:
463*4882a593Smuzhiyun                name, val = set_match.groups()
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun                if val.startswith('"'):
466*4882a593Smuzhiyun                    if len(val) < 2 or val[-1] != '"':
467*4882a593Smuzhiyun                        _parse_error(line, "malformed string literal",
468*4882a593Smuzhiyun                                     line_feeder.filename, line_feeder.linenr)
469*4882a593Smuzhiyun                    # Strip quotes and remove escapings. The unescaping
470*4882a593Smuzhiyun                    # procedure should be safe since " can only appear as \"
471*4882a593Smuzhiyun                    # inside the string.
472*4882a593Smuzhiyun                    val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\")
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun                if name in self.syms:
475*4882a593Smuzhiyun                    sym = self.syms[name]
476*4882a593Smuzhiyun                    if sym.user_val is not None:
477*4882a593Smuzhiyun                        warn_override(line_feeder.filename, line_feeder.linenr,
478*4882a593Smuzhiyun                                      name, sym.user_val, val)
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun                    if sym.is_choice_sym:
481*4882a593Smuzhiyun                        user_mode = sym.parent.user_mode
482*4882a593Smuzhiyun                        if user_mode is not None and user_mode != val:
483*4882a593Smuzhiyun                            self._warn("assignment to {0} changes mode of "
484*4882a593Smuzhiyun                                       'containing choice from "{1}" to "{2}".'
485*4882a593Smuzhiyun                                       .format(name, val, user_mode),
486*4882a593Smuzhiyun                                       line_feeder.filename,
487*4882a593Smuzhiyun                                       line_feeder.linenr)
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun                    sym._set_user_value_no_invalidate(val, True)
490*4882a593Smuzhiyun                else:
491*4882a593Smuzhiyun                    if self.print_undef_assign:
492*4882a593Smuzhiyun                        _stderr_msg('note: attempt to assign the value "{0}" '
493*4882a593Smuzhiyun                                    "to the undefined symbol {1}."
494*4882a593Smuzhiyun                                    .format(val, name),
495*4882a593Smuzhiyun                                    line_feeder.filename, line_feeder.linenr)
496*4882a593Smuzhiyun            else:
497*4882a593Smuzhiyun                unset_match = _unset_re_match(line)
498*4882a593Smuzhiyun                if unset_match:
499*4882a593Smuzhiyun                    name = unset_match.group(1)
500*4882a593Smuzhiyun                    if name in self.syms:
501*4882a593Smuzhiyun                        sym = self.syms[name]
502*4882a593Smuzhiyun                        if sym.user_val is not None:
503*4882a593Smuzhiyun                            warn_override(line_feeder.filename,
504*4882a593Smuzhiyun                                          line_feeder.linenr,
505*4882a593Smuzhiyun                                          name, sym.user_val, "n")
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun                        sym._set_user_value_no_invalidate("n", True)
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun    def write_config(self, filename, header=None):
510*4882a593Smuzhiyun        """Writes out symbol values in the familiar .config format.
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun        Kconfiglib makes sure the format matches what the C implementation
513*4882a593Smuzhiyun        would generate, down to whitespace. This eases testing.
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun        filename: The filename under which to save the configuration.
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun        header (default: None): A textual header that will appear at the
518*4882a593Smuzhiyun           beginning of the file, with each line commented out automatically.
519*4882a593Smuzhiyun           None means no header."""
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun        for sym in self.syms_iter():
522*4882a593Smuzhiyun            sym.already_written = False
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun        with open(filename, "w") as f:
525*4882a593Smuzhiyun            # Write header
526*4882a593Smuzhiyun            if header is not None:
527*4882a593Smuzhiyun                f.write(_comment(header))
528*4882a593Smuzhiyun                f.write("\n")
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun            # Build and write configuration
531*4882a593Smuzhiyun            conf_strings = []
532*4882a593Smuzhiyun            _make_block_conf(self.top_block, conf_strings.append)
533*4882a593Smuzhiyun            f.write("\n".join(conf_strings))
534*4882a593Smuzhiyun            f.write("\n")
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun    def eval(self, s):
537*4882a593Smuzhiyun        """Returns the value of the expression 's' -- where 's' is represented
538*4882a593Smuzhiyun        as a string -- in the context of the configuration. Raises
539*4882a593Smuzhiyun        Kconfig_Syntax_Error if syntax errors are detected in 's'.
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun        For example, if FOO and BAR are tristate symbols at least one of which
542*4882a593Smuzhiyun        has the value "y", then config.eval("y && (FOO || BAR)") => "y"
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun        This function always yields a tristate value. To get the value of
545*4882a593Smuzhiyun        non-bool, non-tristate symbols, use Symbol.get_value().
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun        The result of this function is consistent with how evaluation works for
548*4882a593Smuzhiyun        conditional expressions in the configuration as well as in the C
549*4882a593Smuzhiyun        implementation. "m" and m are rewritten as '"m" && MODULES' and 'm &&
550*4882a593Smuzhiyun        MODULES', respectively, and a result of "m" will get promoted to "y" if
551*4882a593Smuzhiyun        we're running without modules.
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun        Syntax checking is somewhat lax, partly to be compatible with lax
554*4882a593Smuzhiyun        parsing in the C implementation."""
555*4882a593Smuzhiyun        return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed
556*4882a593Smuzhiyun                                                None, # Current symbol/choice
557*4882a593Smuzhiyun                                                s))   # line
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun    def unset_user_values(self):
560*4882a593Smuzhiyun        """Resets the values of all symbols, as if Config.load_config() or
561*4882a593Smuzhiyun        Symbol.set_user_value() had never been called."""
562*4882a593Smuzhiyun        for sym in self.syms_iter():
563*4882a593Smuzhiyun            sym._unset_user_value_no_recursive_invalidate()
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun    def set_print_warnings(self, print_warnings):
566*4882a593Smuzhiyun        """Determines whether warnings related to this configuration (for
567*4882a593Smuzhiyun        things like attempting to assign illegal values to symbols with
568*4882a593Smuzhiyun        Symbol.set_user_value()) should be printed to stderr.
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun        print_warnings: True if warnings should be printed."""
571*4882a593Smuzhiyun        self.print_warnings = print_warnings
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun    def set_print_undef_assign(self, print_undef_assign):
574*4882a593Smuzhiyun        """Determines whether informational messages related to assignments to
575*4882a593Smuzhiyun        undefined symbols should be printed to stderr for this configuration.
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun        print_undef_assign: If True, such messages will be printed."""
578*4882a593Smuzhiyun        self.print_undef_assign = print_undef_assign
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun    def __str__(self):
581*4882a593Smuzhiyun        """Returns a string containing various information about the Config."""
582*4882a593Smuzhiyun        return _lines("Configuration",
583*4882a593Smuzhiyun                      "File                                   : " +
584*4882a593Smuzhiyun                        self.filename,
585*4882a593Smuzhiyun                      "Base directory                         : " +
586*4882a593Smuzhiyun                        self.base_dir,
587*4882a593Smuzhiyun                      "Value of $ARCH at creation time        : " +
588*4882a593Smuzhiyun                        ("(not set)" if self.arch is None else self.arch),
589*4882a593Smuzhiyun                      "Value of $SRCARCH at creation time     : " +
590*4882a593Smuzhiyun                        ("(not set)" if self.srcarch is None else
591*4882a593Smuzhiyun                                        self.srcarch),
592*4882a593Smuzhiyun                      "Source tree (derived from $srctree;",
593*4882a593Smuzhiyun                      "defaults to '.' if $srctree isn't set) : " +
594*4882a593Smuzhiyun                        self.srctree,
595*4882a593Smuzhiyun                      "Most recently loaded .config           : " +
596*4882a593Smuzhiyun                        ("(no .config loaded)"
597*4882a593Smuzhiyun                          if self.config_filename is None else
598*4882a593Smuzhiyun                             self.config_filename),
599*4882a593Smuzhiyun                      "Print warnings                         : " +
600*4882a593Smuzhiyun                        BOOL_STR[self.print_warnings],
601*4882a593Smuzhiyun                      "Print assignments to undefined symbols : " +
602*4882a593Smuzhiyun                        BOOL_STR[self.print_undef_assign])
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun    #
605*4882a593Smuzhiyun    # Private methods
606*4882a593Smuzhiyun    #
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun    #
609*4882a593Smuzhiyun    # Kconfig parsing
610*4882a593Smuzhiyun    #
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun    def _parse_file(self, filename, parent, deps, visible_if_deps, res=None):
613*4882a593Smuzhiyun        """Parses the Kconfig file 'filename'. Returns a list with the Items in
614*4882a593Smuzhiyun        the file. See _parse_block() for the meaning of the parameters."""
615*4882a593Smuzhiyun        return self._parse_block(_FileFeed(filename), None, parent, deps,
616*4882a593Smuzhiyun                                 visible_if_deps, res)
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun    def _parse_block(self, line_feeder, end_marker, parent, deps,
619*4882a593Smuzhiyun                     visible_if_deps, res=None):
620*4882a593Smuzhiyun        """Parses a block, which is the contents of either a file or an if,
621*4882a593Smuzhiyun        menu, or choice statement. Returns a list with the Items in the block.
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun        line_feeder: A _FileFeed instance feeding lines from a file. The
624*4882a593Smuzhiyun          Kconfig language is line-based in practice.
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun        end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for
627*4882a593Smuzhiyun           ifs. None for files.
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun        parent: The enclosing menu or choice, or None if we're at the top
630*4882a593Smuzhiyun           level.
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun        deps: Dependencies from enclosing menus, choices and ifs.
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun        visible_if_deps (default: None): 'visible if' dependencies from
635*4882a593Smuzhiyun           enclosing menus.
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun        res (default: None): The list to add items to. If None, a new list is
638*4882a593Smuzhiyun           created to hold the items."""
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun        block = [] if res is None else res
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun        while 1:
643*4882a593Smuzhiyun            # Do we already have a tokenized line that we determined wasn't
644*4882a593Smuzhiyun            # part of whatever we were parsing earlier? See comment in
645*4882a593Smuzhiyun            # Config.__init__().
646*4882a593Smuzhiyun            if self.end_line is not None:
647*4882a593Smuzhiyun                line = self.end_line
648*4882a593Smuzhiyun                tokens = self.end_line_tokens
649*4882a593Smuzhiyun                tokens.unget_all()
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun                self.end_line = None
652*4882a593Smuzhiyun                self.end_line_tokens = None
653*4882a593Smuzhiyun            else:
654*4882a593Smuzhiyun                line = line_feeder.get_next()
655*4882a593Smuzhiyun                if line is None:
656*4882a593Smuzhiyun                    if end_marker is not None:
657*4882a593Smuzhiyun                        raise Kconfig_Syntax_Error("Unexpected end of file {0}"
658*4882a593Smuzhiyun                                                 .format(line_feeder.filename))
659*4882a593Smuzhiyun                    return block
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun                tokens = self._tokenize(line, False, line_feeder.filename,
662*4882a593Smuzhiyun                                        line_feeder.linenr)
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun            t0 = tokens.get_next()
665*4882a593Smuzhiyun            if t0 is None:
666*4882a593Smuzhiyun                continue
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun            # Cases are ordered roughly by frequency, which speeds things up a
669*4882a593Smuzhiyun            # bit
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun            if t0 == T_CONFIG or t0 == T_MENUCONFIG:
672*4882a593Smuzhiyun                # The tokenizer will automatically allocate a new Symbol object
673*4882a593Smuzhiyun                # for any new names it encounters, so we don't need to worry
674*4882a593Smuzhiyun                # about that here.
675*4882a593Smuzhiyun                sym = tokens.get_next()
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun                # Symbols defined in multiple places get the parent of their
678*4882a593Smuzhiyun                # first definition. However, for symbols whose parents are
679*4882a593Smuzhiyun                # choice statements, the choice statement takes precedence.
680*4882a593Smuzhiyun                if not sym.is_defined_ or isinstance(parent, Choice):
681*4882a593Smuzhiyun                    sym.parent = parent
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun                sym.is_defined_ = True
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun                self.kconfig_syms.append(sym)
686*4882a593Smuzhiyun                block.append(sym)
687*4882a593Smuzhiyun
688*4882a593Smuzhiyun                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun            elif t0 == T_SOURCE:
691*4882a593Smuzhiyun                kconfig_file = tokens.get_next()
692*4882a593Smuzhiyun                exp_kconfig_file = self._expand_sym_refs(kconfig_file)
693*4882a593Smuzhiyun                f = os.path.join(self.base_dir, exp_kconfig_file)
694*4882a593Smuzhiyun                if not os.path.exists(f):
695*4882a593Smuzhiyun                    raise IOError('{0}:{1}: sourced file "{2}" (expands to '
696*4882a593Smuzhiyun                                  '"{3}") not found. Perhaps base_dir '
697*4882a593Smuzhiyun                                  '(argument to Config.__init__(), currently '
698*4882a593Smuzhiyun                                  '"{4}") is set to the wrong value.'
699*4882a593Smuzhiyun                                  .format(line_feeder.filename,
700*4882a593Smuzhiyun                                          line_feeder.linenr,
701*4882a593Smuzhiyun                                          kconfig_file, exp_kconfig_file,
702*4882a593Smuzhiyun                                          self.base_dir))
703*4882a593Smuzhiyun                # Add items to the same block
704*4882a593Smuzhiyun                self._parse_file(f, parent, deps, visible_if_deps, block)
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun            elif t0 == end_marker:
707*4882a593Smuzhiyun                # We have reached the end of the block
708*4882a593Smuzhiyun                return block
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun            elif t0 == T_IF:
711*4882a593Smuzhiyun                # If statements are treated as syntactic sugar for adding
712*4882a593Smuzhiyun                # dependencies to enclosed items and do not have an explicit
713*4882a593Smuzhiyun                # object representation.
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun                dep_expr = self._parse_expr(tokens, None, line,
716*4882a593Smuzhiyun                                            line_feeder.filename,
717*4882a593Smuzhiyun                                            line_feeder.linenr)
718*4882a593Smuzhiyun                # Add items to the same block
719*4882a593Smuzhiyun                self._parse_block(line_feeder, T_ENDIF, parent,
720*4882a593Smuzhiyun                                  _make_and(dep_expr, deps),
721*4882a593Smuzhiyun                                  visible_if_deps, block)
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun            elif t0 == T_COMMENT:
724*4882a593Smuzhiyun                comment = Comment()
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun                comment.config = self
727*4882a593Smuzhiyun                comment.parent = parent
728*4882a593Smuzhiyun                comment.filename = line_feeder.filename
729*4882a593Smuzhiyun                comment.linenr = line_feeder.linenr
730*4882a593Smuzhiyun                comment.text = tokens.get_next()
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun                self.comments.append(comment)
733*4882a593Smuzhiyun                block.append(comment)
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun                self._parse_properties(line_feeder, comment, deps,
736*4882a593Smuzhiyun                                       visible_if_deps)
737*4882a593Smuzhiyun
738*4882a593Smuzhiyun            elif t0 == T_MENU:
739*4882a593Smuzhiyun                menu = Menu()
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun                menu.config = self
742*4882a593Smuzhiyun                menu.parent = parent
743*4882a593Smuzhiyun                menu.filename = line_feeder.filename
744*4882a593Smuzhiyun                menu.linenr = line_feeder.linenr
745*4882a593Smuzhiyun                menu.title = tokens.get_next()
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun                self.menus.append(menu)
748*4882a593Smuzhiyun                block.append(menu)
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun                # Parse properties and contents
751*4882a593Smuzhiyun                self._parse_properties(line_feeder, menu, deps,
752*4882a593Smuzhiyun                                       visible_if_deps)
753*4882a593Smuzhiyun                menu.block = self._parse_block(line_feeder, T_ENDMENU, menu,
754*4882a593Smuzhiyun                                               menu.dep_expr,
755*4882a593Smuzhiyun                                               _make_and(visible_if_deps,
756*4882a593Smuzhiyun                                                         menu.visible_if_expr))
757*4882a593Smuzhiyun
758*4882a593Smuzhiyun            elif t0 == T_CHOICE:
759*4882a593Smuzhiyun                name = tokens.get_next()
760*4882a593Smuzhiyun                if name is None:
761*4882a593Smuzhiyun                    choice = Choice()
762*4882a593Smuzhiyun                    self.choices.append(choice)
763*4882a593Smuzhiyun                else:
764*4882a593Smuzhiyun                    # Named choice
765*4882a593Smuzhiyun                    choice = self.named_choices.get(name)
766*4882a593Smuzhiyun                    if choice is None:
767*4882a593Smuzhiyun                        choice = Choice()
768*4882a593Smuzhiyun                        choice.name = name
769*4882a593Smuzhiyun                        self.named_choices[name] = choice
770*4882a593Smuzhiyun                        self.choices.append(choice)
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun                choice.config = self
773*4882a593Smuzhiyun                choice.parent = parent
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun                choice.def_locations.append((line_feeder.filename,
776*4882a593Smuzhiyun                                             line_feeder.linenr))
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun                # Parse properties and contents
779*4882a593Smuzhiyun                self._parse_properties(line_feeder, choice, deps,
780*4882a593Smuzhiyun                                       visible_if_deps)
781*4882a593Smuzhiyun                choice.block = self._parse_block(line_feeder, T_ENDCHOICE,
782*4882a593Smuzhiyun                                                 choice, deps, visible_if_deps)
783*4882a593Smuzhiyun
784*4882a593Smuzhiyun                choice._determine_actual_symbols()
785*4882a593Smuzhiyun
786*4882a593Smuzhiyun                # If no type is specified for the choice, its type is that of
787*4882a593Smuzhiyun                # the first choice item with a specified type
788*4882a593Smuzhiyun                if choice.type == UNKNOWN:
789*4882a593Smuzhiyun                    for item in choice.actual_symbols:
790*4882a593Smuzhiyun                        if item.type != UNKNOWN:
791*4882a593Smuzhiyun                            choice.type = item.type
792*4882a593Smuzhiyun                            break
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun                # Each choice item of UNKNOWN type gets the type of the choice
795*4882a593Smuzhiyun                for item in choice.actual_symbols:
796*4882a593Smuzhiyun                    if item.type == UNKNOWN:
797*4882a593Smuzhiyun                        item.type = choice.type
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun                block.append(choice)
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun            elif t0 == T_MAINMENU:
802*4882a593Smuzhiyun                text = tokens.get_next()
803*4882a593Smuzhiyun                if self.mainmenu_text is not None:
804*4882a593Smuzhiyun                    self._warn("overriding 'mainmenu' text. "
805*4882a593Smuzhiyun                               'Old value: "{0}", new value: "{1}".'
806*4882a593Smuzhiyun                               .format(self.mainmenu_text, text),
807*4882a593Smuzhiyun                               line_feeder.filename, line_feeder.linenr)
808*4882a593Smuzhiyun                self.mainmenu_text = text
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun            else:
811*4882a593Smuzhiyun                _parse_error(line, "unrecognized construct",
812*4882a593Smuzhiyun                             line_feeder.filename, line_feeder.linenr)
813*4882a593Smuzhiyun
814*4882a593Smuzhiyun    def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps):
815*4882a593Smuzhiyun        """Parsing of properties for symbols, menus, choices, and comments.
816*4882a593Smuzhiyun        Takes care of propagating dependencies from enclosing menus and ifs."""
817*4882a593Smuzhiyun
818*4882a593Smuzhiyun        def parse_val_and_cond(tokens, line, filename, linenr):
819*4882a593Smuzhiyun            """Parses '<expr1> if <expr2>' constructs, where the 'if' part is
820*4882a593Smuzhiyun            optional. Returns a tuple containing the parsed expressions, with
821*4882a593Smuzhiyun            None as the second element if the 'if' part is missing."""
822*4882a593Smuzhiyun            val = self._parse_expr(tokens, stmt, line, filename, linenr, False)
823*4882a593Smuzhiyun            if tokens.check(T_IF):
824*4882a593Smuzhiyun                return (val, self._parse_expr(tokens, stmt, line, filename,
825*4882a593Smuzhiyun                                              linenr))
826*4882a593Smuzhiyun            return (val, None)
827*4882a593Smuzhiyun
828*4882a593Smuzhiyun        # In case the symbol is defined in multiple locations, we need to
829*4882a593Smuzhiyun        # remember what prompts, defaults, and selects are new for this
830*4882a593Smuzhiyun        # definition, as "depends on" should only apply to the local
831*4882a593Smuzhiyun        # definition.
832*4882a593Smuzhiyun        new_prompt = None
833*4882a593Smuzhiyun        new_def_exprs = []
834*4882a593Smuzhiyun        new_selects = []
835*4882a593Smuzhiyun
836*4882a593Smuzhiyun        # Dependencies from 'depends on' statements
837*4882a593Smuzhiyun        depends_on_expr = None
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun        while 1:
840*4882a593Smuzhiyun            line = line_feeder.get_next()
841*4882a593Smuzhiyun            if line is None:
842*4882a593Smuzhiyun                break
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun            filename = line_feeder.filename
845*4882a593Smuzhiyun            linenr = line_feeder.linenr
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun            tokens = self._tokenize(line, False, filename, linenr)
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun            t0 = tokens.get_next()
850*4882a593Smuzhiyun            if t0 is None:
851*4882a593Smuzhiyun                continue
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun            # Cases are ordered roughly by frequency, which speeds things up a
854*4882a593Smuzhiyun            # bit
855*4882a593Smuzhiyun
856*4882a593Smuzhiyun            if t0 == T_DEPENDS:
857*4882a593Smuzhiyun                if not tokens.check(T_ON):
858*4882a593Smuzhiyun                    _parse_error(line, 'expected "on" after "depends"',
859*4882a593Smuzhiyun                                 filename, linenr)
860*4882a593Smuzhiyun
861*4882a593Smuzhiyun                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
862*4882a593Smuzhiyun                                               linenr)
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun                if isinstance(stmt, (Menu, Comment)):
865*4882a593Smuzhiyun                    stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps)
866*4882a593Smuzhiyun                else:
867*4882a593Smuzhiyun                    depends_on_expr = _make_and(depends_on_expr, parsed_deps)
868*4882a593Smuzhiyun
869*4882a593Smuzhiyun            elif t0 == T_HELP:
870*4882a593Smuzhiyun                # Find first non-blank (not all-space) line and get its
871*4882a593Smuzhiyun                # indentation
872*4882a593Smuzhiyun                line = line_feeder.next_nonblank()
873*4882a593Smuzhiyun                if line is None:
874*4882a593Smuzhiyun                    stmt.help = ""
875*4882a593Smuzhiyun                    break
876*4882a593Smuzhiyun                indent = _indentation(line)
877*4882a593Smuzhiyun                if indent == 0:
878*4882a593Smuzhiyun                    # If the first non-empty lines has zero indent, there is no
879*4882a593Smuzhiyun                    # help text
880*4882a593Smuzhiyun                    stmt.help = ""
881*4882a593Smuzhiyun                    line_feeder.unget()
882*4882a593Smuzhiyun                    break
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun                # The help text goes on till the first non-empty line with less
885*4882a593Smuzhiyun                # indent
886*4882a593Smuzhiyun                help_lines = [_deindent(line, indent)]
887*4882a593Smuzhiyun                while 1:
888*4882a593Smuzhiyun                    line = line_feeder.get_next()
889*4882a593Smuzhiyun                    if line is None or \
890*4882a593Smuzhiyun                       (not line.isspace() and _indentation(line) < indent):
891*4882a593Smuzhiyun                        stmt.help = "".join(help_lines)
892*4882a593Smuzhiyun                        break
893*4882a593Smuzhiyun                    help_lines.append(_deindent(line, indent))
894*4882a593Smuzhiyun
895*4882a593Smuzhiyun                if line is None:
896*4882a593Smuzhiyun                    break
897*4882a593Smuzhiyun
898*4882a593Smuzhiyun                line_feeder.unget()
899*4882a593Smuzhiyun
900*4882a593Smuzhiyun            elif t0 == T_SELECT or t0 == T_IMPLY:
901*4882a593Smuzhiyun                target = tokens.get_next()
902*4882a593Smuzhiyun
903*4882a593Smuzhiyun                stmt.referenced_syms.add(target)
904*4882a593Smuzhiyun                stmt.selected_syms.add(target)
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun                if tokens.check(T_IF):
907*4882a593Smuzhiyun                    new_selects.append((target,
908*4882a593Smuzhiyun                                        self._parse_expr(tokens, stmt, line,
909*4882a593Smuzhiyun                                                         filename, linenr)))
910*4882a593Smuzhiyun                else:
911*4882a593Smuzhiyun                    new_selects.append((target, None))
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun            elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
914*4882a593Smuzhiyun                stmt.type = TOKEN_TO_TYPE[t0]
915*4882a593Smuzhiyun                if tokens.peek_next() is not None:
916*4882a593Smuzhiyun                    new_prompt = parse_val_and_cond(tokens, line, filename,
917*4882a593Smuzhiyun                                                    linenr)
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun            elif t0 == T_DEFAULT:
920*4882a593Smuzhiyun                new_def_exprs.append(parse_val_and_cond(tokens, line, filename,
921*4882a593Smuzhiyun                                                        linenr))
922*4882a593Smuzhiyun
923*4882a593Smuzhiyun            elif t0 == T_DEF_BOOL:
924*4882a593Smuzhiyun                stmt.type = BOOL
925*4882a593Smuzhiyun                if tokens.peek_next() is not None:
926*4882a593Smuzhiyun                    new_def_exprs.append(parse_val_and_cond(tokens, line,
927*4882a593Smuzhiyun                                                            filename, linenr))
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun            elif t0 == T_PROMPT:
930*4882a593Smuzhiyun                # 'prompt' properties override each other within a single
931*4882a593Smuzhiyun                # definition of a symbol, but additional prompts can be added
932*4882a593Smuzhiyun                # by defining the symbol multiple times; hence 'new_prompt'
933*4882a593Smuzhiyun                # instead of 'prompt'.
934*4882a593Smuzhiyun                new_prompt = parse_val_and_cond(tokens, line, filename, linenr)
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun            elif t0 == T_RANGE:
937*4882a593Smuzhiyun                low = tokens.get_next()
938*4882a593Smuzhiyun                high = tokens.get_next()
939*4882a593Smuzhiyun                stmt.referenced_syms.add(low)
940*4882a593Smuzhiyun                stmt.referenced_syms.add(high)
941*4882a593Smuzhiyun
942*4882a593Smuzhiyun                if tokens.check(T_IF):
943*4882a593Smuzhiyun                    stmt.ranges.append((low, high,
944*4882a593Smuzhiyun                                        self._parse_expr(tokens, stmt, line,
945*4882a593Smuzhiyun                                                         filename, linenr)))
946*4882a593Smuzhiyun                else:
947*4882a593Smuzhiyun                    stmt.ranges.append((low, high, None))
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun            elif t0 == T_DEF_TRISTATE:
950*4882a593Smuzhiyun                stmt.type = TRISTATE
951*4882a593Smuzhiyun                if tokens.peek_next() is not None:
952*4882a593Smuzhiyun                    new_def_exprs.append(parse_val_and_cond(tokens, line,
953*4882a593Smuzhiyun                                                            filename, linenr))
954*4882a593Smuzhiyun
955*4882a593Smuzhiyun            elif t0 == T_OPTION:
956*4882a593Smuzhiyun                if tokens.check(T_ENV) and tokens.check(T_EQUAL):
957*4882a593Smuzhiyun                    env_var = tokens.get_next()
958*4882a593Smuzhiyun
959*4882a593Smuzhiyun                    stmt.is_special_ = True
960*4882a593Smuzhiyun                    stmt.is_from_env = True
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun                    if env_var not in os.environ:
963*4882a593Smuzhiyun                        self._warn("The symbol {0} references the "
964*4882a593Smuzhiyun                                   "non-existent environment variable {1} and "
965*4882a593Smuzhiyun                                   "will get the empty string as its value. "
966*4882a593Smuzhiyun                                   "If you're using Kconfiglib via "
967*4882a593Smuzhiyun                                   "'make (i)scriptconfig', it should have "
968*4882a593Smuzhiyun                                   "set up the environment correctly for you. "
969*4882a593Smuzhiyun                                   "If you still got this message, that "
970*4882a593Smuzhiyun                                   "might be an error, and you should email "
971*4882a593Smuzhiyun                                   "ulfalizer a.t Google's email service."""
972*4882a593Smuzhiyun                                   .format(stmt.name, env_var),
973*4882a593Smuzhiyun                                   filename, linenr)
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun                        stmt.cached_val = ""
976*4882a593Smuzhiyun                    else:
977*4882a593Smuzhiyun                        stmt.cached_val = os.environ[env_var]
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun                elif tokens.check(T_DEFCONFIG_LIST):
980*4882a593Smuzhiyun                    self.defconfig_sym = stmt
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun                elif tokens.check(T_MODULES):
983*4882a593Smuzhiyun                    # To reduce warning spam, only warn if 'option modules' is
984*4882a593Smuzhiyun                    # set on some symbol that isn't MODULES, which should be
985*4882a593Smuzhiyun                    # safe. I haven't run into any projects that make use
986*4882a593Smuzhiyun                    # modules besides the kernel yet, and there it's likely to
987*4882a593Smuzhiyun                    # keep being called "MODULES".
988*4882a593Smuzhiyun                    if stmt.name != "MODULES":
989*4882a593Smuzhiyun                        self._warn("the 'modules' option is not supported. "
990*4882a593Smuzhiyun                                   "Let me know if this is a problem for you; "
991*4882a593Smuzhiyun                                   "it shouldn't be that hard to implement. "
992*4882a593Smuzhiyun                                   "(Note that modules are still supported -- "
993*4882a593Smuzhiyun                                   "Kconfiglib just assumes the symbol name "
994*4882a593Smuzhiyun                                   "MODULES, like older versions of the C "
995*4882a593Smuzhiyun                                   "implementation did when 'option modules' "
996*4882a593Smuzhiyun                                   "wasn't used.)",
997*4882a593Smuzhiyun                                   filename, linenr)
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun                elif tokens.check(T_ALLNOCONFIG_Y):
1000*4882a593Smuzhiyun                    if not isinstance(stmt, Symbol):
1001*4882a593Smuzhiyun                        _parse_error(line,
1002*4882a593Smuzhiyun                                     "the 'allnoconfig_y' option is only "
1003*4882a593Smuzhiyun                                     "valid for symbols",
1004*4882a593Smuzhiyun                                     filename, linenr)
1005*4882a593Smuzhiyun                    stmt.allnoconfig_y = True
1006*4882a593Smuzhiyun
1007*4882a593Smuzhiyun                else:
1008*4882a593Smuzhiyun                    _parse_error(line, "unrecognized option", filename, linenr)
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun            elif t0 == T_VISIBLE:
1011*4882a593Smuzhiyun                if not tokens.check(T_IF):
1012*4882a593Smuzhiyun                    _parse_error(line, 'expected "if" after "visible"',
1013*4882a593Smuzhiyun                                 filename, linenr)
1014*4882a593Smuzhiyun                if not isinstance(stmt, Menu):
1015*4882a593Smuzhiyun                    _parse_error(line,
1016*4882a593Smuzhiyun                                 "'visible if' is only valid for menus",
1017*4882a593Smuzhiyun                                 filename, linenr)
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
1020*4882a593Smuzhiyun                                               linenr)
1021*4882a593Smuzhiyun                stmt.visible_if_expr = _make_and(stmt.visible_if_expr,
1022*4882a593Smuzhiyun                                                 parsed_deps)
1023*4882a593Smuzhiyun
1024*4882a593Smuzhiyun            elif t0 == T_OPTIONAL:
1025*4882a593Smuzhiyun                if not isinstance(stmt, Choice):
1026*4882a593Smuzhiyun                    _parse_error(line,
1027*4882a593Smuzhiyun                                 '"optional" is only valid for choices',
1028*4882a593Smuzhiyun                                 filename,
1029*4882a593Smuzhiyun                                 linenr)
1030*4882a593Smuzhiyun                stmt.optional = True
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun            else:
1033*4882a593Smuzhiyun                # See comment in Config.__init__()
1034*4882a593Smuzhiyun                self.end_line = line
1035*4882a593Smuzhiyun                self.end_line_tokens = tokens
1036*4882a593Smuzhiyun                break
1037*4882a593Smuzhiyun
1038*4882a593Smuzhiyun        # Done parsing properties. Now propagate 'depends on' and enclosing
1039*4882a593Smuzhiyun        # menu/if dependencies to expressions.
1040*4882a593Smuzhiyun
1041*4882a593Smuzhiyun        # The set of symbols referenced directly by the statement plus all
1042*4882a593Smuzhiyun        # symbols referenced by enclosing menus and ifs
1043*4882a593Smuzhiyun        stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps)
1044*4882a593Smuzhiyun
1045*4882a593Smuzhiyun        # Save original dependencies from enclosing menus and ifs
1046*4882a593Smuzhiyun        stmt.deps_from_containing = deps
1047*4882a593Smuzhiyun
1048*4882a593Smuzhiyun        if isinstance(stmt, (Menu, Comment)):
1049*4882a593Smuzhiyun            stmt.dep_expr = _make_and(stmt.orig_deps, deps)
1050*4882a593Smuzhiyun        else:
1051*4882a593Smuzhiyun            # Symbol or Choice
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun            # See comment for 'menu_dep'
1054*4882a593Smuzhiyun            stmt.menu_dep = depends_on_expr
1055*4882a593Smuzhiyun
1056*4882a593Smuzhiyun            # Propagate dependencies to prompts
1057*4882a593Smuzhiyun
1058*4882a593Smuzhiyun            if new_prompt is not None:
1059*4882a593Smuzhiyun                # Propagate 'visible if' dependencies from enclosing menus
1060*4882a593Smuzhiyun                prompt, cond_expr = new_prompt
1061*4882a593Smuzhiyun                cond_expr = _make_and(cond_expr, visible_if_deps)
1062*4882a593Smuzhiyun                # Propagate 'depends on' dependencies
1063*4882a593Smuzhiyun                new_prompt = (prompt, _make_and(cond_expr, depends_on_expr))
1064*4882a593Smuzhiyun                # Save original
1065*4882a593Smuzhiyun                stmt.orig_prompts.append(new_prompt)
1066*4882a593Smuzhiyun                # Finalize with dependencies from enclosing menus and ifs
1067*4882a593Smuzhiyun                stmt.prompts.append((new_prompt[0],
1068*4882a593Smuzhiyun                                     _make_and(new_prompt[1], deps)))
1069*4882a593Smuzhiyun
1070*4882a593Smuzhiyun            # Propagate dependencies to defaults
1071*4882a593Smuzhiyun
1072*4882a593Smuzhiyun            # Propagate 'depends on' dependencies
1073*4882a593Smuzhiyun            new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr))
1074*4882a593Smuzhiyun                             for val_expr, cond_expr in new_def_exprs]
1075*4882a593Smuzhiyun            # Save original
1076*4882a593Smuzhiyun            stmt.orig_def_exprs.extend(new_def_exprs)
1077*4882a593Smuzhiyun            # Finalize with dependencies from enclosing menus and ifs
1078*4882a593Smuzhiyun            stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
1079*4882a593Smuzhiyun                                   for val_expr, cond_expr in new_def_exprs])
1080*4882a593Smuzhiyun
1081*4882a593Smuzhiyun            # Propagate dependencies to selects
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun            # Only symbols can select
1084*4882a593Smuzhiyun            if isinstance(stmt, Symbol):
1085*4882a593Smuzhiyun                # Propagate 'depends on' dependencies
1086*4882a593Smuzhiyun                new_selects = [(target, _make_and(cond_expr, depends_on_expr))
1087*4882a593Smuzhiyun                               for target, cond_expr in new_selects]
1088*4882a593Smuzhiyun                # Save original
1089*4882a593Smuzhiyun                stmt.orig_selects.extend(new_selects)
1090*4882a593Smuzhiyun                # Finalize with dependencies from enclosing menus and ifs
1091*4882a593Smuzhiyun                for target, cond in new_selects:
1092*4882a593Smuzhiyun                    target.rev_dep = _make_or(target.rev_dep,
1093*4882a593Smuzhiyun                                              _make_and(stmt,
1094*4882a593Smuzhiyun                                                        _make_and(cond, deps)))
1095*4882a593Smuzhiyun
1096*4882a593Smuzhiyun    def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
1097*4882a593Smuzhiyun                    transform_m=True):
1098*4882a593Smuzhiyun        """Parses an expression from the tokens in 'feed' using a simple
1099*4882a593Smuzhiyun        top-down approach. The result has the form
1100*4882a593Smuzhiyun        '(<operator>, [<parsed operands>])', where <operator> is e.g.
1101*4882a593Smuzhiyun        kconfiglib.AND. If there is only one operand (i.e., no && or ||), then
1102*4882a593Smuzhiyun        the operand is returned directly. This also goes for subexpressions.
1103*4882a593Smuzhiyun
1104*4882a593Smuzhiyun        feed: _Feed instance containing the tokens for the expression.
1105*4882a593Smuzhiyun
1106*4882a593Smuzhiyun        cur_item: The item (Symbol, Choice, Menu, or Comment) currently being
1107*4882a593Smuzhiyun           parsed, or None if we're not parsing an item. Used for recording
1108*4882a593Smuzhiyun           references to symbols.
1109*4882a593Smuzhiyun
1110*4882a593Smuzhiyun        line: The line containing the expression being parsed.
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun        filename (default: None): The file containing the expression.
1113*4882a593Smuzhiyun
1114*4882a593Smuzhiyun        linenr (default: None): The line number containing the expression.
1115*4882a593Smuzhiyun
1116*4882a593Smuzhiyun        transform_m (default: False): Determines if 'm' should be rewritten to
1117*4882a593Smuzhiyun           'm && MODULES' -- see parse_val_and_cond().
1118*4882a593Smuzhiyun
1119*4882a593Smuzhiyun        Expression grammar, in decreasing order of precedence:
1120*4882a593Smuzhiyun
1121*4882a593Smuzhiyun        <expr> -> <symbol>
1122*4882a593Smuzhiyun                  <symbol> '=' <symbol>
1123*4882a593Smuzhiyun                  <symbol> '!=' <symbol>
1124*4882a593Smuzhiyun                  '(' <expr> ')'
1125*4882a593Smuzhiyun                  '!' <expr>
1126*4882a593Smuzhiyun                  <expr> '&&' <expr>
1127*4882a593Smuzhiyun                  <expr> '||' <expr>"""
1128*4882a593Smuzhiyun
1129*4882a593Smuzhiyun        # Use instance variables to avoid having to pass these as arguments
1130*4882a593Smuzhiyun        # through the top-down parser in _parse_expr_rec(), which is tedious
1131*4882a593Smuzhiyun        # and obfuscates the code. A profiler run shows no noticeable
1132*4882a593Smuzhiyun        # performance difference.
1133*4882a593Smuzhiyun        self._cur_item = cur_item
1134*4882a593Smuzhiyun        self._transform_m = transform_m
1135*4882a593Smuzhiyun        self._line = line
1136*4882a593Smuzhiyun        self._filename = filename
1137*4882a593Smuzhiyun        self._linenr = linenr
1138*4882a593Smuzhiyun
1139*4882a593Smuzhiyun        return self._parse_expr_rec(feed)
1140*4882a593Smuzhiyun
1141*4882a593Smuzhiyun    def _parse_expr_rec(self, feed):
1142*4882a593Smuzhiyun        or_term = self._parse_or_term(feed)
1143*4882a593Smuzhiyun        if not feed.check(T_OR):
1144*4882a593Smuzhiyun            # Common case -- no need for an OR node since it's just a single
1145*4882a593Smuzhiyun            # operand
1146*4882a593Smuzhiyun            return or_term
1147*4882a593Smuzhiyun        or_terms = [or_term, self._parse_or_term(feed)]
1148*4882a593Smuzhiyun        while feed.check(T_OR):
1149*4882a593Smuzhiyun            or_terms.append(self._parse_or_term(feed))
1150*4882a593Smuzhiyun        return (OR, or_terms)
1151*4882a593Smuzhiyun
1152*4882a593Smuzhiyun    def _parse_or_term(self, feed):
1153*4882a593Smuzhiyun        and_term = self._parse_factor(feed)
1154*4882a593Smuzhiyun        if not feed.check(T_AND):
1155*4882a593Smuzhiyun            # Common case -- no need for an AND node since it's just a single
1156*4882a593Smuzhiyun            # operand
1157*4882a593Smuzhiyun            return and_term
1158*4882a593Smuzhiyun        and_terms = [and_term, self._parse_factor(feed)]
1159*4882a593Smuzhiyun        while feed.check(T_AND):
1160*4882a593Smuzhiyun            and_terms.append(self._parse_factor(feed))
1161*4882a593Smuzhiyun        return (AND, and_terms)
1162*4882a593Smuzhiyun
1163*4882a593Smuzhiyun    def _parse_factor(self, feed):
1164*4882a593Smuzhiyun        token = feed.get_next()
1165*4882a593Smuzhiyun
1166*4882a593Smuzhiyun        if isinstance(token, (Symbol, str)):
1167*4882a593Smuzhiyun            if self._cur_item is not None and isinstance(token, Symbol):
1168*4882a593Smuzhiyun                self._cur_item.referenced_syms.add(token)
1169*4882a593Smuzhiyun
1170*4882a593Smuzhiyun            next_token = feed.peek_next()
1171*4882a593Smuzhiyun            # For conditional expressions ('depends on <expr>',
1172*4882a593Smuzhiyun            # '... if <expr>', # etc.), "m" and m are rewritten to
1173*4882a593Smuzhiyun            # "m" && MODULES.
1174*4882a593Smuzhiyun            if next_token != T_EQUAL and next_token != T_UNEQUAL:
1175*4882a593Smuzhiyun                if self._transform_m and (token is self.m or token == "m"):
1176*4882a593Smuzhiyun                    return (AND, ["m", self._sym_lookup("MODULES")])
1177*4882a593Smuzhiyun                return token
1178*4882a593Smuzhiyun
1179*4882a593Smuzhiyun            relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL
1180*4882a593Smuzhiyun            token_2 = feed.get_next()
1181*4882a593Smuzhiyun            if self._cur_item is not None and isinstance(token_2, Symbol):
1182*4882a593Smuzhiyun                self._cur_item.referenced_syms.add(token_2)
1183*4882a593Smuzhiyun            return (relation, token, token_2)
1184*4882a593Smuzhiyun
1185*4882a593Smuzhiyun        if token == T_NOT:
1186*4882a593Smuzhiyun            return (NOT, self._parse_factor(feed))
1187*4882a593Smuzhiyun
1188*4882a593Smuzhiyun        if token == T_OPEN_PAREN:
1189*4882a593Smuzhiyun            expr_parse = self._parse_expr_rec(feed)
1190*4882a593Smuzhiyun            if not feed.check(T_CLOSE_PAREN):
1191*4882a593Smuzhiyun                _parse_error(self._line, "missing end parenthesis",
1192*4882a593Smuzhiyun                             self._filename, self._linenr)
1193*4882a593Smuzhiyun            return expr_parse
1194*4882a593Smuzhiyun
1195*4882a593Smuzhiyun        _parse_error(self._line, "malformed expression", self._filename,
1196*4882a593Smuzhiyun                     self._linenr)
1197*4882a593Smuzhiyun
1198*4882a593Smuzhiyun    def _tokenize(self, s, for_eval, filename=None, linenr=None):
1199*4882a593Smuzhiyun        """Returns a _Feed instance containing tokens derived from the string
1200*4882a593Smuzhiyun        's'. Registers any new symbols encountered (via _sym_lookup()).
1201*4882a593Smuzhiyun
1202*4882a593Smuzhiyun        (I experimented with a pure regular expression implementation, but it
1203*4882a593Smuzhiyun        came out slower, less readable, and wouldn't have been as flexible.)
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyun        for_eval: True when parsing an expression for a call to Config.eval(),
1206*4882a593Smuzhiyun           in which case we should not treat the first token specially nor
1207*4882a593Smuzhiyun           register new symbols."""
1208*4882a593Smuzhiyun
1209*4882a593Smuzhiyun        s = s.strip()
1210*4882a593Smuzhiyun        if s == "" or s[0] == "#":
1211*4882a593Smuzhiyun            return _Feed([])
1212*4882a593Smuzhiyun
1213*4882a593Smuzhiyun        if for_eval:
1214*4882a593Smuzhiyun            previous = None # The previous token seen
1215*4882a593Smuzhiyun            tokens = []
1216*4882a593Smuzhiyun            i = 0 # The current index in the string being tokenized
1217*4882a593Smuzhiyun
1218*4882a593Smuzhiyun        else:
1219*4882a593Smuzhiyun            # The initial word on a line is parsed specially. Let
1220*4882a593Smuzhiyun            # command_chars = [A-Za-z0-9_]. Then
1221*4882a593Smuzhiyun            #  - leading non-command_chars characters are ignored, and
1222*4882a593Smuzhiyun            #  - the first token consists the following one or more
1223*4882a593Smuzhiyun            #    command_chars characters.
1224*4882a593Smuzhiyun            # This is why things like "----help--" are accepted.
1225*4882a593Smuzhiyun            initial_token_match = _initial_token_re_match(s)
1226*4882a593Smuzhiyun            if initial_token_match is None:
1227*4882a593Smuzhiyun                return _Feed([])
1228*4882a593Smuzhiyun            keyword = _get_keyword(initial_token_match.group(1))
1229*4882a593Smuzhiyun            if keyword == T_HELP:
1230*4882a593Smuzhiyun                # Avoid junk after "help", e.g. "---", being registered as a
1231*4882a593Smuzhiyun                # symbol
1232*4882a593Smuzhiyun                return _Feed([T_HELP])
1233*4882a593Smuzhiyun            if keyword is None:
1234*4882a593Smuzhiyun                # We expect a keyword as the first token
1235*4882a593Smuzhiyun                _tokenization_error(s, filename, linenr)
1236*4882a593Smuzhiyun
1237*4882a593Smuzhiyun            previous = keyword
1238*4882a593Smuzhiyun            tokens = [keyword]
1239*4882a593Smuzhiyun            # The current index in the string being tokenized
1240*4882a593Smuzhiyun            i = initial_token_match.end()
1241*4882a593Smuzhiyun
1242*4882a593Smuzhiyun        # _tokenize() is a hotspot during parsing, and this speeds things up a
1243*4882a593Smuzhiyun        # bit
1244*4882a593Smuzhiyun        strlen = len(s)
1245*4882a593Smuzhiyun        append = tokens.append
1246*4882a593Smuzhiyun
1247*4882a593Smuzhiyun        # Main tokenization loop. (Handles tokens past the first one.)
1248*4882a593Smuzhiyun        while i < strlen:
1249*4882a593Smuzhiyun            # Test for an identifier/keyword preceded by whitespace first; this
1250*4882a593Smuzhiyun            # is the most common case.
1251*4882a593Smuzhiyun            id_keyword_match = _id_keyword_re_match(s, i)
1252*4882a593Smuzhiyun            if id_keyword_match:
1253*4882a593Smuzhiyun                # We have an identifier or keyword. The above also stripped any
1254*4882a593Smuzhiyun                # whitespace for us.
1255*4882a593Smuzhiyun                name = id_keyword_match.group(1)
1256*4882a593Smuzhiyun                # Jump past it
1257*4882a593Smuzhiyun                i = id_keyword_match.end()
1258*4882a593Smuzhiyun
1259*4882a593Smuzhiyun                keyword = _get_keyword(name)
1260*4882a593Smuzhiyun                if keyword is not None:
1261*4882a593Smuzhiyun                    # It's a keyword
1262*4882a593Smuzhiyun                    append(keyword)
1263*4882a593Smuzhiyun                elif previous in STRING_LEX:
1264*4882a593Smuzhiyun                    # What would ordinarily be considered an identifier is
1265*4882a593Smuzhiyun                    # treated as a string after certain tokens
1266*4882a593Smuzhiyun                    append(name)
1267*4882a593Smuzhiyun                else:
1268*4882a593Smuzhiyun                    # It's a symbol name. _sym_lookup() will take care of
1269*4882a593Smuzhiyun                    # allocating a new Symbol instance if it's the first time
1270*4882a593Smuzhiyun                    # we see it.
1271*4882a593Smuzhiyun                    sym = self._sym_lookup(name, for_eval)
1272*4882a593Smuzhiyun
1273*4882a593Smuzhiyun                    if previous == T_CONFIG or previous == T_MENUCONFIG:
1274*4882a593Smuzhiyun                        # If the previous token is T_(MENU)CONFIG
1275*4882a593Smuzhiyun                        # ("(menu)config"), we're tokenizing the first line of
1276*4882a593Smuzhiyun                        # a symbol definition, and should remember this as a
1277*4882a593Smuzhiyun                        # location where the symbol is defined
1278*4882a593Smuzhiyun                        sym.def_locations.append((filename, linenr))
1279*4882a593Smuzhiyun                    else:
1280*4882a593Smuzhiyun                        # Otherwise, it's a reference to the symbol
1281*4882a593Smuzhiyun                        sym.ref_locations.append((filename, linenr))
1282*4882a593Smuzhiyun
1283*4882a593Smuzhiyun                    append(sym)
1284*4882a593Smuzhiyun
1285*4882a593Smuzhiyun            else:
1286*4882a593Smuzhiyun                # Not an identifier/keyword
1287*4882a593Smuzhiyun
1288*4882a593Smuzhiyun                while i < strlen and s[i].isspace():
1289*4882a593Smuzhiyun                    i += 1
1290*4882a593Smuzhiyun                if i == strlen:
1291*4882a593Smuzhiyun                    break
1292*4882a593Smuzhiyun                c = s[i]
1293*4882a593Smuzhiyun                i += 1
1294*4882a593Smuzhiyun
1295*4882a593Smuzhiyun                # String literal (constant symbol)
1296*4882a593Smuzhiyun                if c == '"' or c == "'":
1297*4882a593Smuzhiyun                    if "\\" in s:
1298*4882a593Smuzhiyun                        # Slow path: This could probably be sped up, but it's a
1299*4882a593Smuzhiyun                        # very unusual case anyway.
1300*4882a593Smuzhiyun                        quote = c
1301*4882a593Smuzhiyun                        val = ""
1302*4882a593Smuzhiyun                        while 1:
1303*4882a593Smuzhiyun                            if i >= len(s):
1304*4882a593Smuzhiyun                                _tokenization_error(s, filename, linenr)
1305*4882a593Smuzhiyun                            c = s[i]
1306*4882a593Smuzhiyun                            if c == quote:
1307*4882a593Smuzhiyun                                break
1308*4882a593Smuzhiyun                            if c == "\\":
1309*4882a593Smuzhiyun                                if i + 1 >= len(s):
1310*4882a593Smuzhiyun                                    _tokenization_error(s, filename, linenr)
1311*4882a593Smuzhiyun                                val += s[i + 1]
1312*4882a593Smuzhiyun                                i += 2
1313*4882a593Smuzhiyun                            else:
1314*4882a593Smuzhiyun                                val += c
1315*4882a593Smuzhiyun                                i += 1
1316*4882a593Smuzhiyun                        i += 1
1317*4882a593Smuzhiyun                        append(val)
1318*4882a593Smuzhiyun                    else:
1319*4882a593Smuzhiyun                        # Fast path: If the string contains no backslashes
1320*4882a593Smuzhiyun                        # (almost always) we can simply look for the matching
1321*4882a593Smuzhiyun                        # quote.
1322*4882a593Smuzhiyun                        end = s.find(c, i)
1323*4882a593Smuzhiyun                        if end == -1:
1324*4882a593Smuzhiyun                            _tokenization_error(s, filename, linenr)
1325*4882a593Smuzhiyun                        append(s[i:end])
1326*4882a593Smuzhiyun                        i = end + 1
1327*4882a593Smuzhiyun
1328*4882a593Smuzhiyun                elif c == "&":
1329*4882a593Smuzhiyun                    # Invalid characters are ignored
1330*4882a593Smuzhiyun                    if i >= len(s) or s[i] != "&": continue
1331*4882a593Smuzhiyun                    append(T_AND)
1332*4882a593Smuzhiyun                    i += 1
1333*4882a593Smuzhiyun
1334*4882a593Smuzhiyun                elif c == "|":
1335*4882a593Smuzhiyun                    # Invalid characters are ignored
1336*4882a593Smuzhiyun                    if i >= len(s) or s[i] != "|": continue
1337*4882a593Smuzhiyun                    append(T_OR)
1338*4882a593Smuzhiyun                    i += 1
1339*4882a593Smuzhiyun
1340*4882a593Smuzhiyun                elif c == "!":
1341*4882a593Smuzhiyun                    if i < len(s) and s[i] == "=":
1342*4882a593Smuzhiyun                        append(T_UNEQUAL)
1343*4882a593Smuzhiyun                        i += 1
1344*4882a593Smuzhiyun                    else:
1345*4882a593Smuzhiyun                        append(T_NOT)
1346*4882a593Smuzhiyun
1347*4882a593Smuzhiyun                elif c == "=": append(T_EQUAL)
1348*4882a593Smuzhiyun                elif c == "(": append(T_OPEN_PAREN)
1349*4882a593Smuzhiyun                elif c == ")": append(T_CLOSE_PAREN)
1350*4882a593Smuzhiyun                elif c == "#": break # Comment
1351*4882a593Smuzhiyun
1352*4882a593Smuzhiyun                else: continue # Invalid characters are ignored
1353*4882a593Smuzhiyun
1354*4882a593Smuzhiyun            previous = tokens[-1]
1355*4882a593Smuzhiyun
1356*4882a593Smuzhiyun        return _Feed(tokens)
1357*4882a593Smuzhiyun
1358*4882a593Smuzhiyun    def _sym_lookup(self, name, for_eval=False):
1359*4882a593Smuzhiyun        """Fetches the symbol 'name' from the symbol table, creating and
1360*4882a593Smuzhiyun        registering it if it does not exist. If 'for_eval' is True, the symbol
1361*4882a593Smuzhiyun        won't be added to the symbol table if it does not exist -- this is for
1362*4882a593Smuzhiyun        Config.eval()."""
1363*4882a593Smuzhiyun        if name in self.syms:
1364*4882a593Smuzhiyun            return self.syms[name]
1365*4882a593Smuzhiyun
1366*4882a593Smuzhiyun        new_sym = Symbol()
1367*4882a593Smuzhiyun        new_sym.config = self
1368*4882a593Smuzhiyun        new_sym.name = name
1369*4882a593Smuzhiyun        if for_eval:
1370*4882a593Smuzhiyun            self._warn("no symbol {0} in configuration".format(name))
1371*4882a593Smuzhiyun        else:
1372*4882a593Smuzhiyun            self.syms[name] = new_sym
1373*4882a593Smuzhiyun        return new_sym
1374*4882a593Smuzhiyun
1375*4882a593Smuzhiyun    #
1376*4882a593Smuzhiyun    # Expression evaluation
1377*4882a593Smuzhiyun    #
1378*4882a593Smuzhiyun
1379*4882a593Smuzhiyun    def _eval_expr(self, expr):
1380*4882a593Smuzhiyun        """Evaluates an expression to "n", "m", or "y"."""
1381*4882a593Smuzhiyun
1382*4882a593Smuzhiyun        # Handles e.g. an "x if y" condition where the "if y" part is missing.
1383*4882a593Smuzhiyun        if expr is None:
1384*4882a593Smuzhiyun            return "y"
1385*4882a593Smuzhiyun
1386*4882a593Smuzhiyun        res = self._eval_expr_rec(expr)
1387*4882a593Smuzhiyun        if res == "m":
1388*4882a593Smuzhiyun            # Promote "m" to "y" if we're running without modules.
1389*4882a593Smuzhiyun            #
1390*4882a593Smuzhiyun            # Internally, "m" is often rewritten to "m" && MODULES by both the
1391*4882a593Smuzhiyun            # C implementation and Kconfiglib, which takes care of cases where
1392*4882a593Smuzhiyun            # "m" should be demoted to "n" instead.
1393*4882a593Smuzhiyun            modules_sym = self.syms.get("MODULES")
1394*4882a593Smuzhiyun            if modules_sym is None or modules_sym.get_value() != "y":
1395*4882a593Smuzhiyun                return "y"
1396*4882a593Smuzhiyun        return res
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun    def _eval_expr_rec(self, expr):
1399*4882a593Smuzhiyun        if isinstance(expr, Symbol):
1400*4882a593Smuzhiyun            # Non-bool/tristate symbols are always "n" in a tristate sense,
1401*4882a593Smuzhiyun            # regardless of their value
1402*4882a593Smuzhiyun            if expr.type != BOOL and expr.type != TRISTATE:
1403*4882a593Smuzhiyun                return "n"
1404*4882a593Smuzhiyun            return expr.get_value()
1405*4882a593Smuzhiyun
1406*4882a593Smuzhiyun        if isinstance(expr, str):
1407*4882a593Smuzhiyun            return expr if (expr == "y" or expr == "m") else "n"
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun        # Ordered by frequency
1410*4882a593Smuzhiyun
1411*4882a593Smuzhiyun        if expr[0] == AND:
1412*4882a593Smuzhiyun            res = "y"
1413*4882a593Smuzhiyun            for subexpr in expr[1]:
1414*4882a593Smuzhiyun                ev = self._eval_expr_rec(subexpr)
1415*4882a593Smuzhiyun                # Return immediately upon discovering an "n" term
1416*4882a593Smuzhiyun                if ev == "n":
1417*4882a593Smuzhiyun                    return "n"
1418*4882a593Smuzhiyun                if ev == "m":
1419*4882a593Smuzhiyun                    res = "m"
1420*4882a593Smuzhiyun            # 'res' is either "m" or "y" here; we already handled the
1421*4882a593Smuzhiyun            # short-circuiting "n" case in the loop.
1422*4882a593Smuzhiyun            return res
1423*4882a593Smuzhiyun
1424*4882a593Smuzhiyun        if expr[0] == NOT:
1425*4882a593Smuzhiyun            ev = self._eval_expr_rec(expr[1])
1426*4882a593Smuzhiyun            if ev == "y":
1427*4882a593Smuzhiyun                return "n"
1428*4882a593Smuzhiyun            return "y" if (ev == "n") else "m"
1429*4882a593Smuzhiyun
1430*4882a593Smuzhiyun        if expr[0] == OR:
1431*4882a593Smuzhiyun            res = "n"
1432*4882a593Smuzhiyun            for subexpr in expr[1]:
1433*4882a593Smuzhiyun                ev = self._eval_expr_rec(subexpr)
1434*4882a593Smuzhiyun                # Return immediately upon discovering a "y" term
1435*4882a593Smuzhiyun                if ev == "y":
1436*4882a593Smuzhiyun                    return "y"
1437*4882a593Smuzhiyun                if ev == "m":
1438*4882a593Smuzhiyun                    res = "m"
1439*4882a593Smuzhiyun            # 'res' is either "n" or "m" here; we already handled the
1440*4882a593Smuzhiyun            # short-circuiting "y" case in the loop.
1441*4882a593Smuzhiyun            return res
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun        if expr[0] == EQUAL:
1444*4882a593Smuzhiyun            return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n"
1445*4882a593Smuzhiyun
1446*4882a593Smuzhiyun        if expr[0] == UNEQUAL:
1447*4882a593Smuzhiyun            return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n"
1448*4882a593Smuzhiyun
1449*4882a593Smuzhiyun        _internal_error("Internal error while evaluating expression: "
1450*4882a593Smuzhiyun                        "unknown operation {0}.".format(expr[0]))
1451*4882a593Smuzhiyun
1452*4882a593Smuzhiyun    def _eval_min(self, e1, e2):
1453*4882a593Smuzhiyun        """Returns the minimum value of the two expressions. Equates None with
1454*4882a593Smuzhiyun        'y'."""
1455*4882a593Smuzhiyun        e1_eval = self._eval_expr(e1)
1456*4882a593Smuzhiyun        e2_eval = self._eval_expr(e2)
1457*4882a593Smuzhiyun        return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval
1458*4882a593Smuzhiyun
1459*4882a593Smuzhiyun    def _eval_max(self, e1, e2):
1460*4882a593Smuzhiyun        """Returns the maximum value of the two expressions. Equates None with
1461*4882a593Smuzhiyun        'y'."""
1462*4882a593Smuzhiyun        e1_eval = self._eval_expr(e1)
1463*4882a593Smuzhiyun        e2_eval = self._eval_expr(e2)
1464*4882a593Smuzhiyun        return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval
1465*4882a593Smuzhiyun
1466*4882a593Smuzhiyun    #
1467*4882a593Smuzhiyun    # Dependency tracking (for caching and invalidation)
1468*4882a593Smuzhiyun    #
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun    def _build_dep(self):
1471*4882a593Smuzhiyun        """Populates the Symbol.dep sets, linking the symbol to the symbols
1472*4882a593Smuzhiyun        that immediately depend on it in the sense that changing the value of
1473*4882a593Smuzhiyun        the symbol might affect the values of those other symbols. This is used
1474*4882a593Smuzhiyun        for caching/invalidation purposes. The calculated sets might be larger
1475*4882a593Smuzhiyun        than necessary as we don't do any complicated analysis of the
1476*4882a593Smuzhiyun        expressions."""
1477*4882a593Smuzhiyun
1478*4882a593Smuzhiyun        # Adds 'sym' as a directly dependent symbol to all symbols that appear
1479*4882a593Smuzhiyun        # in the expression 'e'
1480*4882a593Smuzhiyun        def add_expr_deps(e, sym):
1481*4882a593Smuzhiyun            for s in _get_expr_syms(e):
1482*4882a593Smuzhiyun                s.dep.add(sym)
1483*4882a593Smuzhiyun
1484*4882a593Smuzhiyun        # The directly dependent symbols of a symbol are:
1485*4882a593Smuzhiyun        #  - Any symbols whose prompts, default values, rev_dep (select
1486*4882a593Smuzhiyun        #    condition), or ranges depend on the symbol
1487*4882a593Smuzhiyun        #  - Any symbols that belong to the same choice statement as the symbol
1488*4882a593Smuzhiyun        #    (these won't be included in 'dep' as that makes the dependency
1489*4882a593Smuzhiyun        #    graph unwieldy, but Symbol._get_dependent() will include them)
1490*4882a593Smuzhiyun        #  - Any symbols in a choice statement that depends on the symbol
1491*4882a593Smuzhiyun        for sym in self.syms_iter():
1492*4882a593Smuzhiyun            for _, e in sym.prompts:
1493*4882a593Smuzhiyun                add_expr_deps(e, sym)
1494*4882a593Smuzhiyun
1495*4882a593Smuzhiyun            for v, e in sym.def_exprs:
1496*4882a593Smuzhiyun                add_expr_deps(v, sym)
1497*4882a593Smuzhiyun                add_expr_deps(e, sym)
1498*4882a593Smuzhiyun
1499*4882a593Smuzhiyun            add_expr_deps(sym.rev_dep, sym)
1500*4882a593Smuzhiyun
1501*4882a593Smuzhiyun            for l, u, e in sym.ranges:
1502*4882a593Smuzhiyun                add_expr_deps(l, sym)
1503*4882a593Smuzhiyun                add_expr_deps(u, sym)
1504*4882a593Smuzhiyun                add_expr_deps(e, sym)
1505*4882a593Smuzhiyun
1506*4882a593Smuzhiyun            if sym.is_choice_sym:
1507*4882a593Smuzhiyun                choice = sym.parent
1508*4882a593Smuzhiyun                for _, e in choice.prompts:
1509*4882a593Smuzhiyun                    add_expr_deps(e, sym)
1510*4882a593Smuzhiyun                for _, e in choice.def_exprs:
1511*4882a593Smuzhiyun                    add_expr_deps(e, sym)
1512*4882a593Smuzhiyun
1513*4882a593Smuzhiyun    def _eq_to_sym(self, eq):
1514*4882a593Smuzhiyun        """_expr_depends_on() helper. For (in)equalities of the form sym = y/m
1515*4882a593Smuzhiyun        or sym != n, returns sym. For other (in)equalities, returns None."""
1516*4882a593Smuzhiyun        relation, left, right = eq
1517*4882a593Smuzhiyun
1518*4882a593Smuzhiyun        def transform_y_m_n(item):
1519*4882a593Smuzhiyun            if item is self.y: return "y"
1520*4882a593Smuzhiyun            if item is self.m: return "m"
1521*4882a593Smuzhiyun            if item is self.n: return "n"
1522*4882a593Smuzhiyun            return item
1523*4882a593Smuzhiyun
1524*4882a593Smuzhiyun        left = transform_y_m_n(left)
1525*4882a593Smuzhiyun        right = transform_y_m_n(right)
1526*4882a593Smuzhiyun
1527*4882a593Smuzhiyun        # Make sure the symbol (if any) appears to the left
1528*4882a593Smuzhiyun        if not isinstance(left, Symbol):
1529*4882a593Smuzhiyun            left, right = right, left
1530*4882a593Smuzhiyun        if not isinstance(left, Symbol):
1531*4882a593Smuzhiyun            return None
1532*4882a593Smuzhiyun        if (relation == EQUAL and (right == "y" or right == "m")) or \
1533*4882a593Smuzhiyun           (relation == UNEQUAL and right == "n"):
1534*4882a593Smuzhiyun            return left
1535*4882a593Smuzhiyun        return None
1536*4882a593Smuzhiyun
1537*4882a593Smuzhiyun    def _expr_depends_on(self, expr, sym):
1538*4882a593Smuzhiyun        """Reimplementation of expr_depends_symbol() from mconf.c. Used to
1539*4882a593Smuzhiyun        determine if a submenu should be implicitly created, which influences
1540*4882a593Smuzhiyun        what items inside choice statements are considered choice items."""
1541*4882a593Smuzhiyun        if expr is None:
1542*4882a593Smuzhiyun            return False
1543*4882a593Smuzhiyun
1544*4882a593Smuzhiyun        def rec(expr):
1545*4882a593Smuzhiyun            if isinstance(expr, str):
1546*4882a593Smuzhiyun                return False
1547*4882a593Smuzhiyun            if isinstance(expr, Symbol):
1548*4882a593Smuzhiyun                return expr is sym
1549*4882a593Smuzhiyun
1550*4882a593Smuzhiyun            if expr[0] in (EQUAL, UNEQUAL):
1551*4882a593Smuzhiyun                return self._eq_to_sym(expr) is sym
1552*4882a593Smuzhiyun            if expr[0] == AND:
1553*4882a593Smuzhiyun                for and_expr in expr[1]:
1554*4882a593Smuzhiyun                    if rec(and_expr):
1555*4882a593Smuzhiyun                        return True
1556*4882a593Smuzhiyun            return False
1557*4882a593Smuzhiyun
1558*4882a593Smuzhiyun        return rec(expr)
1559*4882a593Smuzhiyun
1560*4882a593Smuzhiyun    def _invalidate_all(self):
1561*4882a593Smuzhiyun        for sym in self.syms_iter():
1562*4882a593Smuzhiyun            sym._invalidate()
1563*4882a593Smuzhiyun
1564*4882a593Smuzhiyun    #
1565*4882a593Smuzhiyun    # Printing and misc.
1566*4882a593Smuzhiyun    #
1567*4882a593Smuzhiyun
1568*4882a593Smuzhiyun    def _expand_sym_refs(self, s):
1569*4882a593Smuzhiyun        """Expands $-references to symbols in 's' to symbol values, or to the
1570*4882a593Smuzhiyun        empty string for undefined symbols."""
1571*4882a593Smuzhiyun
1572*4882a593Smuzhiyun        while 1:
1573*4882a593Smuzhiyun            sym_ref_match = _sym_ref_re_search(s)
1574*4882a593Smuzhiyun            if sym_ref_match is None:
1575*4882a593Smuzhiyun                return s
1576*4882a593Smuzhiyun
1577*4882a593Smuzhiyun            sym_name = sym_ref_match.group(0)[1:]
1578*4882a593Smuzhiyun            sym = self.syms.get(sym_name)
1579*4882a593Smuzhiyun            expansion = "" if sym is None else sym.get_value()
1580*4882a593Smuzhiyun
1581*4882a593Smuzhiyun            s = s[:sym_ref_match.start()] + \
1582*4882a593Smuzhiyun                expansion + \
1583*4882a593Smuzhiyun                s[sym_ref_match.end():]
1584*4882a593Smuzhiyun
1585*4882a593Smuzhiyun    def _expr_val_str(self, expr, no_value_str="(none)",
1586*4882a593Smuzhiyun                      get_val_instead_of_eval=False):
1587*4882a593Smuzhiyun        """Printing helper. Returns a string with 'expr' and its value.
1588*4882a593Smuzhiyun
1589*4882a593Smuzhiyun        no_value_str: String to return when 'expr' is missing (None).
1590*4882a593Smuzhiyun
1591*4882a593Smuzhiyun        get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant
1592*4882a593Smuzhiyun          symbol) and get its value directly instead of evaluating it to a
1593*4882a593Smuzhiyun          tristate value."""
1594*4882a593Smuzhiyun
1595*4882a593Smuzhiyun        if expr is None:
1596*4882a593Smuzhiyun            return no_value_str
1597*4882a593Smuzhiyun
1598*4882a593Smuzhiyun        if get_val_instead_of_eval:
1599*4882a593Smuzhiyun            if isinstance(expr, str):
1600*4882a593Smuzhiyun                return _expr_to_str(expr)
1601*4882a593Smuzhiyun            val = expr.get_value()
1602*4882a593Smuzhiyun        else:
1603*4882a593Smuzhiyun            val = self._eval_expr(expr)
1604*4882a593Smuzhiyun
1605*4882a593Smuzhiyun        return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val))
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun    def _get_sym_or_choice_str(self, sc):
1608*4882a593Smuzhiyun        """Symbols and choices have many properties in common, so we factor out
1609*4882a593Smuzhiyun        common __str__() stuff here. "sc" is short for "symbol or choice"."""
1610*4882a593Smuzhiyun
1611*4882a593Smuzhiyun        # As we deal a lot with string representations here, use some
1612*4882a593Smuzhiyun        # convenient shorthand:
1613*4882a593Smuzhiyun        s = _expr_to_str
1614*4882a593Smuzhiyun
1615*4882a593Smuzhiyun        #
1616*4882a593Smuzhiyun        # Common symbol/choice properties
1617*4882a593Smuzhiyun        #
1618*4882a593Smuzhiyun
1619*4882a593Smuzhiyun        user_val_str = "(no user value)" if sc.user_val is None else \
1620*4882a593Smuzhiyun                       s(sc.user_val)
1621*4882a593Smuzhiyun
1622*4882a593Smuzhiyun        # Build prompts string
1623*4882a593Smuzhiyun        if not sc.prompts:
1624*4882a593Smuzhiyun            prompts_str = " (no prompts)"
1625*4882a593Smuzhiyun        else:
1626*4882a593Smuzhiyun            prompts_str_rows = []
1627*4882a593Smuzhiyun            for prompt, cond_expr in sc.orig_prompts:
1628*4882a593Smuzhiyun                if cond_expr is None:
1629*4882a593Smuzhiyun                    prompts_str_rows.append(' "{0}"'.format(prompt))
1630*4882a593Smuzhiyun                else:
1631*4882a593Smuzhiyun                    prompts_str_rows.append(
1632*4882a593Smuzhiyun                      ' "{0}" if {1}'.format(prompt,
1633*4882a593Smuzhiyun                                             self._expr_val_str(cond_expr)))
1634*4882a593Smuzhiyun            prompts_str = "\n".join(prompts_str_rows)
1635*4882a593Smuzhiyun
1636*4882a593Smuzhiyun        # Build locations string
1637*4882a593Smuzhiyun        if not sc.def_locations:
1638*4882a593Smuzhiyun            locations_str = "(no locations)"
1639*4882a593Smuzhiyun        else:
1640*4882a593Smuzhiyun            locations_str = " ".join(["{0}:{1}".format(filename, linenr) for
1641*4882a593Smuzhiyun                                      (filename, linenr) in sc.def_locations])
1642*4882a593Smuzhiyun
1643*4882a593Smuzhiyun        # Build additional-dependencies-from-menus-and-ifs string
1644*4882a593Smuzhiyun        additional_deps_str = " " + \
1645*4882a593Smuzhiyun          self._expr_val_str(sc.deps_from_containing,
1646*4882a593Smuzhiyun                             "(no additional dependencies)")
1647*4882a593Smuzhiyun
1648*4882a593Smuzhiyun        #
1649*4882a593Smuzhiyun        # Symbol-specific stuff
1650*4882a593Smuzhiyun        #
1651*4882a593Smuzhiyun
1652*4882a593Smuzhiyun        if isinstance(sc, Symbol):
1653*4882a593Smuzhiyun            # Build ranges string
1654*4882a593Smuzhiyun            if isinstance(sc, Symbol):
1655*4882a593Smuzhiyun                if not sc.ranges:
1656*4882a593Smuzhiyun                    ranges_str = " (no ranges)"
1657*4882a593Smuzhiyun                else:
1658*4882a593Smuzhiyun                    ranges_str_rows = []
1659*4882a593Smuzhiyun                    for l, u, cond_expr in sc.ranges:
1660*4882a593Smuzhiyun                        if cond_expr is None:
1661*4882a593Smuzhiyun                            ranges_str_rows.append(" [{0}, {1}]".format(s(l),
1662*4882a593Smuzhiyun                                                                        s(u)))
1663*4882a593Smuzhiyun                        else:
1664*4882a593Smuzhiyun                            ranges_str_rows.append(" [{0}, {1}] if {2}"
1665*4882a593Smuzhiyun                              .format(s(l), s(u),
1666*4882a593Smuzhiyun                                      self._expr_val_str(cond_expr)))
1667*4882a593Smuzhiyun                    ranges_str = "\n".join(ranges_str_rows)
1668*4882a593Smuzhiyun
1669*4882a593Smuzhiyun            # Build default values string
1670*4882a593Smuzhiyun            if not sc.def_exprs:
1671*4882a593Smuzhiyun                defaults_str = " (no default values)"
1672*4882a593Smuzhiyun            else:
1673*4882a593Smuzhiyun                defaults_str_rows = []
1674*4882a593Smuzhiyun                for val_expr, cond_expr in sc.orig_def_exprs:
1675*4882a593Smuzhiyun                    row_str = " " + self._expr_val_str(val_expr, "(none)",
1676*4882a593Smuzhiyun                                                       sc.type == STRING)
1677*4882a593Smuzhiyun                    defaults_str_rows.append(row_str)
1678*4882a593Smuzhiyun                    defaults_str_rows.append("  Condition: " +
1679*4882a593Smuzhiyun                                               self._expr_val_str(cond_expr))
1680*4882a593Smuzhiyun                defaults_str = "\n".join(defaults_str_rows)
1681*4882a593Smuzhiyun
1682*4882a593Smuzhiyun            # Build selects string
1683*4882a593Smuzhiyun            if not sc.orig_selects:
1684*4882a593Smuzhiyun                selects_str = " (no selects)"
1685*4882a593Smuzhiyun            else:
1686*4882a593Smuzhiyun                selects_str_rows = []
1687*4882a593Smuzhiyun                for target, cond_expr in sc.orig_selects:
1688*4882a593Smuzhiyun                    if cond_expr is None:
1689*4882a593Smuzhiyun                        selects_str_rows.append(" {0}".format(target.name))
1690*4882a593Smuzhiyun                    else:
1691*4882a593Smuzhiyun                        selects_str_rows.append(
1692*4882a593Smuzhiyun                          " {0} if {1}".format(target.name,
1693*4882a593Smuzhiyun                                               self._expr_val_str(cond_expr)))
1694*4882a593Smuzhiyun                selects_str = "\n".join(selects_str_rows)
1695*4882a593Smuzhiyun
1696*4882a593Smuzhiyun            res = _lines("Symbol " +
1697*4882a593Smuzhiyun                           ("(no name)" if sc.name is None else sc.name),
1698*4882a593Smuzhiyun                         "Type           : " + TYPENAME[sc.type],
1699*4882a593Smuzhiyun                         "Value          : " + s(sc.get_value()),
1700*4882a593Smuzhiyun                         "User value     : " + user_val_str,
1701*4882a593Smuzhiyun                         "Visibility     : " + s(_get_visibility(sc)),
1702*4882a593Smuzhiyun                         "Is choice item : " + BOOL_STR[sc.is_choice_sym],
1703*4882a593Smuzhiyun                         "Is defined     : " + BOOL_STR[sc.is_defined_],
1704*4882a593Smuzhiyun                         "Is from env.   : " + BOOL_STR[sc.is_from_env],
1705*4882a593Smuzhiyun                         "Is special     : " + BOOL_STR[sc.is_special_] + "\n")
1706*4882a593Smuzhiyun            if sc.ranges:
1707*4882a593Smuzhiyun                res += _lines("Ranges:", ranges_str + "\n")
1708*4882a593Smuzhiyun            res += _lines("Prompts:",
1709*4882a593Smuzhiyun                          prompts_str,
1710*4882a593Smuzhiyun                          "Default values:",
1711*4882a593Smuzhiyun                          defaults_str,
1712*4882a593Smuzhiyun                          "Selects:",
1713*4882a593Smuzhiyun                          selects_str,
1714*4882a593Smuzhiyun                          "Reverse (select-related) dependencies:",
1715*4882a593Smuzhiyun                          " (no reverse dependencies)" if sc.rev_dep == "n"
1716*4882a593Smuzhiyun                            else " " + self._expr_val_str(sc.rev_dep),
1717*4882a593Smuzhiyun                          "Additional dependencies from enclosing menus "
1718*4882a593Smuzhiyun                            "and ifs:",
1719*4882a593Smuzhiyun                          additional_deps_str,
1720*4882a593Smuzhiyun                          "Locations: " + locations_str)
1721*4882a593Smuzhiyun
1722*4882a593Smuzhiyun            return res
1723*4882a593Smuzhiyun
1724*4882a593Smuzhiyun        #
1725*4882a593Smuzhiyun        # Choice-specific stuff
1726*4882a593Smuzhiyun        #
1727*4882a593Smuzhiyun
1728*4882a593Smuzhiyun        # Build selected symbol string
1729*4882a593Smuzhiyun        sel = sc.get_selection()
1730*4882a593Smuzhiyun        sel_str = "(no selection)" if sel is None else sel.name
1731*4882a593Smuzhiyun
1732*4882a593Smuzhiyun        # Build default values string
1733*4882a593Smuzhiyun        if not sc.def_exprs:
1734*4882a593Smuzhiyun            defaults_str = " (no default values)"
1735*4882a593Smuzhiyun        else:
1736*4882a593Smuzhiyun            defaults_str_rows = []
1737*4882a593Smuzhiyun            for sym, cond_expr in sc.orig_def_exprs:
1738*4882a593Smuzhiyun                if cond_expr is None:
1739*4882a593Smuzhiyun                    defaults_str_rows.append(" {0}".format(sym.name))
1740*4882a593Smuzhiyun                else:
1741*4882a593Smuzhiyun                    defaults_str_rows.append(" {0} if {1}".format(sym.name,
1742*4882a593Smuzhiyun                                                self._expr_val_str(cond_expr)))
1743*4882a593Smuzhiyun            defaults_str = "\n".join(defaults_str_rows)
1744*4882a593Smuzhiyun
1745*4882a593Smuzhiyun        # Build contained symbols string
1746*4882a593Smuzhiyun        names = [sym.name for sym in sc.actual_symbols]
1747*4882a593Smuzhiyun        syms_string = " ".join(names) if names else "(empty)"
1748*4882a593Smuzhiyun
1749*4882a593Smuzhiyun        return _lines("Choice",
1750*4882a593Smuzhiyun                      "Name (for named choices): " +
1751*4882a593Smuzhiyun                        ("(no name)" if sc.name is None else sc.name),
1752*4882a593Smuzhiyun                      "Type            : " + TYPENAME[sc.type],
1753*4882a593Smuzhiyun                      "Selected symbol : " + sel_str,
1754*4882a593Smuzhiyun                      "User value      : " + user_val_str,
1755*4882a593Smuzhiyun                      "Mode            : " + s(sc.get_mode()),
1756*4882a593Smuzhiyun                      "Visibility      : " + s(_get_visibility(sc)),
1757*4882a593Smuzhiyun                      "Optional        : " + BOOL_STR[sc.optional],
1758*4882a593Smuzhiyun                      "Prompts:",
1759*4882a593Smuzhiyun                      prompts_str,
1760*4882a593Smuzhiyun                      "Defaults:",
1761*4882a593Smuzhiyun                      defaults_str,
1762*4882a593Smuzhiyun                      "Choice symbols:",
1763*4882a593Smuzhiyun                      " " + syms_string,
1764*4882a593Smuzhiyun                      "Additional dependencies from enclosing menus and "
1765*4882a593Smuzhiyun                        "ifs:",
1766*4882a593Smuzhiyun                      additional_deps_str,
1767*4882a593Smuzhiyun                      "Locations: " + locations_str)
1768*4882a593Smuzhiyun
1769*4882a593Smuzhiyun    def _warn(self, msg, filename=None, linenr=None):
1770*4882a593Smuzhiyun        """For printing warnings to stderr."""
1771*4882a593Smuzhiyun        msg = _build_msg("warning: " + msg, filename, linenr)
1772*4882a593Smuzhiyun        if self.print_warnings:
1773*4882a593Smuzhiyun            sys.stderr.write(msg + "\n")
1774*4882a593Smuzhiyun        self._warnings.append(msg)
1775*4882a593Smuzhiyun
1776*4882a593Smuzhiyunclass Item(object):
1777*4882a593Smuzhiyun
1778*4882a593Smuzhiyun    """Base class for symbols and other Kconfig constructs. Subclasses are
1779*4882a593Smuzhiyun    Symbol, Choice, Menu, and Comment."""
1780*4882a593Smuzhiyun
1781*4882a593Smuzhiyun    def is_symbol(self):
1782*4882a593Smuzhiyun        """Returns True if the item is a symbol. Short for
1783*4882a593Smuzhiyun        isinstance(item, kconfiglib.Symbol)."""
1784*4882a593Smuzhiyun        return isinstance(self, Symbol)
1785*4882a593Smuzhiyun
1786*4882a593Smuzhiyun    def is_choice(self):
1787*4882a593Smuzhiyun        """Returns True if the item is a choice. Short for
1788*4882a593Smuzhiyun        isinstance(item, kconfiglib.Choice)."""
1789*4882a593Smuzhiyun        return isinstance(self, Choice)
1790*4882a593Smuzhiyun
1791*4882a593Smuzhiyun    def is_menu(self):
1792*4882a593Smuzhiyun        """Returns True if the item is a menu. Short for
1793*4882a593Smuzhiyun        isinstance(item, kconfiglib.Menu)."""
1794*4882a593Smuzhiyun        return isinstance(self, Menu)
1795*4882a593Smuzhiyun
1796*4882a593Smuzhiyun    def is_comment(self):
1797*4882a593Smuzhiyun        """Returns True if the item is a comment. Short for
1798*4882a593Smuzhiyun        isinstance(item, kconfiglib.Comment)."""
1799*4882a593Smuzhiyun        return isinstance(self, Comment)
1800*4882a593Smuzhiyun
1801*4882a593Smuzhiyunclass Symbol(Item):
1802*4882a593Smuzhiyun
1803*4882a593Smuzhiyun    """Represents a configuration symbol - e.g. FOO for
1804*4882a593Smuzhiyun
1805*4882a593Smuzhiyun    config FOO
1806*4882a593Smuzhiyun        ..."""
1807*4882a593Smuzhiyun
1808*4882a593Smuzhiyun    #
1809*4882a593Smuzhiyun    # Public interface
1810*4882a593Smuzhiyun    #
1811*4882a593Smuzhiyun
1812*4882a593Smuzhiyun    def get_config(self):
1813*4882a593Smuzhiyun        """Returns the Config instance this symbol is from."""
1814*4882a593Smuzhiyun        return self.config
1815*4882a593Smuzhiyun
1816*4882a593Smuzhiyun    def get_name(self):
1817*4882a593Smuzhiyun        """Returns the name of the symbol."""
1818*4882a593Smuzhiyun        return self.name
1819*4882a593Smuzhiyun
1820*4882a593Smuzhiyun    def get_type(self):
1821*4882a593Smuzhiyun        """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE,
1822*4882a593Smuzhiyun        STRING, HEX, or INT. These are defined at the top level of the module,
1823*4882a593Smuzhiyun        so you'd do something like
1824*4882a593Smuzhiyun
1825*4882a593Smuzhiyun        if sym.get_type() == kconfiglib.STRING:
1826*4882a593Smuzhiyun            ..."""
1827*4882a593Smuzhiyun        return self.type
1828*4882a593Smuzhiyun
1829*4882a593Smuzhiyun    def get_prompts(self):
1830*4882a593Smuzhiyun        """Returns a list of prompts defined for the symbol, in the order they
1831*4882a593Smuzhiyun        appear in the configuration files. Returns the empty list for symbols
1832*4882a593Smuzhiyun        with no prompt.
1833*4882a593Smuzhiyun
1834*4882a593Smuzhiyun        This list will have a single entry for the vast majority of symbols
1835*4882a593Smuzhiyun        having prompts, but having multiple prompts for a single symbol is
1836*4882a593Smuzhiyun        possible through having multiple 'config' entries for it."""
1837*4882a593Smuzhiyun        return [prompt for prompt, _ in self.orig_prompts]
1838*4882a593Smuzhiyun
1839*4882a593Smuzhiyun    def get_help(self):
1840*4882a593Smuzhiyun        """Returns the help text of the symbol, or None if the symbol has no
1841*4882a593Smuzhiyun        help text."""
1842*4882a593Smuzhiyun        return self.help
1843*4882a593Smuzhiyun
1844*4882a593Smuzhiyun    def get_parent(self):
1845*4882a593Smuzhiyun        """Returns the menu or choice statement that contains the symbol, or
1846*4882a593Smuzhiyun        None if the symbol is at the top level. Note that if statements are
1847*4882a593Smuzhiyun        treated as syntactic and do not have an explicit class
1848*4882a593Smuzhiyun        representation."""
1849*4882a593Smuzhiyun        return self.parent
1850*4882a593Smuzhiyun
1851*4882a593Smuzhiyun    def get_def_locations(self):
1852*4882a593Smuzhiyun        """Returns a list of (filename, linenr) tuples, where filename (string)
1853*4882a593Smuzhiyun        and linenr (int) represent a location where the symbol is defined. For
1854*4882a593Smuzhiyun        the vast majority of symbols this list will only contain one element.
1855*4882a593Smuzhiyun        For the following Kconfig, FOO would get two entries: the lines marked
1856*4882a593Smuzhiyun        with *.
1857*4882a593Smuzhiyun
1858*4882a593Smuzhiyun        config FOO *
1859*4882a593Smuzhiyun            bool "foo prompt 1"
1860*4882a593Smuzhiyun
1861*4882a593Smuzhiyun        config FOO *
1862*4882a593Smuzhiyun            bool "foo prompt 2"
1863*4882a593Smuzhiyun        """
1864*4882a593Smuzhiyun        return self.def_locations
1865*4882a593Smuzhiyun
1866*4882a593Smuzhiyun    def get_ref_locations(self):
1867*4882a593Smuzhiyun        """Returns a list of (filename, linenr) tuples, where filename (string)
1868*4882a593Smuzhiyun        and linenr (int) represent a location where the symbol is referenced in
1869*4882a593Smuzhiyun        the configuration. For example, the lines marked by * would be included
1870*4882a593Smuzhiyun        for FOO below:
1871*4882a593Smuzhiyun
1872*4882a593Smuzhiyun        config A
1873*4882a593Smuzhiyun            bool
1874*4882a593Smuzhiyun            default BAR || FOO *
1875*4882a593Smuzhiyun
1876*4882a593Smuzhiyun        config B
1877*4882a593Smuzhiyun            tristate
1878*4882a593Smuzhiyun            depends on FOO *
1879*4882a593Smuzhiyun            default m if FOO *
1880*4882a593Smuzhiyun
1881*4882a593Smuzhiyun        if FOO *
1882*4882a593Smuzhiyun            config A
1883*4882a593Smuzhiyun                bool "A"
1884*4882a593Smuzhiyun        endif
1885*4882a593Smuzhiyun
1886*4882a593Smuzhiyun        config FOO (definition not included)
1887*4882a593Smuzhiyun            bool
1888*4882a593Smuzhiyun        """
1889*4882a593Smuzhiyun        return self.ref_locations
1890*4882a593Smuzhiyun
1891*4882a593Smuzhiyun    def get_value(self):
1892*4882a593Smuzhiyun        """Calculate and return the value of the symbol. See also
1893*4882a593Smuzhiyun        Symbol.set_user_value()."""
1894*4882a593Smuzhiyun
1895*4882a593Smuzhiyun        if self.cached_val is not None:
1896*4882a593Smuzhiyun            return self.cached_val
1897*4882a593Smuzhiyun
1898*4882a593Smuzhiyun        # As a quirk of Kconfig, undefined symbols get their name as their
1899*4882a593Smuzhiyun        # value. This is why things like "FOO = bar" work for seeing if FOO has
1900*4882a593Smuzhiyun        # the value "bar".
1901*4882a593Smuzhiyun        if self.type == UNKNOWN:
1902*4882a593Smuzhiyun            self.cached_val = self.name
1903*4882a593Smuzhiyun            return self.name
1904*4882a593Smuzhiyun
1905*4882a593Smuzhiyun        new_val = DEFAULT_VALUE[self.type]
1906*4882a593Smuzhiyun        vis = _get_visibility(self)
1907*4882a593Smuzhiyun
1908*4882a593Smuzhiyun        # This is easiest to calculate together with the value
1909*4882a593Smuzhiyun        self.write_to_conf = False
1910*4882a593Smuzhiyun
1911*4882a593Smuzhiyun        if self.type == BOOL or self.type == TRISTATE:
1912*4882a593Smuzhiyun            # The visibility and mode (modules-only or single-selection) of
1913*4882a593Smuzhiyun            # choice items will be taken into account in _get_visibility()
1914*4882a593Smuzhiyun            if self.is_choice_sym:
1915*4882a593Smuzhiyun                if vis != "n":
1916*4882a593Smuzhiyun                    choice = self.parent
1917*4882a593Smuzhiyun                    mode = choice.get_mode()
1918*4882a593Smuzhiyun
1919*4882a593Smuzhiyun                    self.write_to_conf = (mode != "n")
1920*4882a593Smuzhiyun
1921*4882a593Smuzhiyun                    if mode == "y":
1922*4882a593Smuzhiyun                        if choice.get_selection() is self:
1923*4882a593Smuzhiyun                            new_val = "y"
1924*4882a593Smuzhiyun                        else:
1925*4882a593Smuzhiyun                            new_val = "n"
1926*4882a593Smuzhiyun                    elif mode == "m":
1927*4882a593Smuzhiyun                        if self.user_val == "m" or self.user_val == "y":
1928*4882a593Smuzhiyun                            new_val = "m"
1929*4882a593Smuzhiyun
1930*4882a593Smuzhiyun            else:
1931*4882a593Smuzhiyun                # If the symbol is visible and has a user value, use that.
1932*4882a593Smuzhiyun                # Otherwise, look at defaults.
1933*4882a593Smuzhiyun                use_defaults = True
1934*4882a593Smuzhiyun
1935*4882a593Smuzhiyun                if vis != "n":
1936*4882a593Smuzhiyun                    self.write_to_conf = True
1937*4882a593Smuzhiyun                    if self.user_val is not None:
1938*4882a593Smuzhiyun                        new_val = self.config._eval_min(self.user_val, vis)
1939*4882a593Smuzhiyun                        use_defaults = False
1940*4882a593Smuzhiyun
1941*4882a593Smuzhiyun                if use_defaults:
1942*4882a593Smuzhiyun                    for val_expr, cond_expr in self.def_exprs:
1943*4882a593Smuzhiyun                        cond_eval = self.config._eval_expr(cond_expr)
1944*4882a593Smuzhiyun                        if cond_eval != "n":
1945*4882a593Smuzhiyun                            self.write_to_conf = True
1946*4882a593Smuzhiyun                            new_val = self.config._eval_min(val_expr,
1947*4882a593Smuzhiyun                                                            cond_eval)
1948*4882a593Smuzhiyun                            break
1949*4882a593Smuzhiyun
1950*4882a593Smuzhiyun                # Reverse (select-related) dependencies take precedence
1951*4882a593Smuzhiyun                rev_dep_val = self.config._eval_expr(self.rev_dep)
1952*4882a593Smuzhiyun                if rev_dep_val != "n":
1953*4882a593Smuzhiyun                    self.write_to_conf = True
1954*4882a593Smuzhiyun                    new_val = self.config._eval_max(new_val, rev_dep_val)
1955*4882a593Smuzhiyun
1956*4882a593Smuzhiyun            # Promote "m" to "y" for booleans
1957*4882a593Smuzhiyun            if new_val == "m" and self.type == BOOL:
1958*4882a593Smuzhiyun                new_val = "y"
1959*4882a593Smuzhiyun
1960*4882a593Smuzhiyun        elif self.type == INT or self.type == HEX:
1961*4882a593Smuzhiyun            has_active_range = False
1962*4882a593Smuzhiyun            low = None
1963*4882a593Smuzhiyun            high = None
1964*4882a593Smuzhiyun            use_defaults = True
1965*4882a593Smuzhiyun
1966*4882a593Smuzhiyun            base = 16 if self.type == HEX else 10
1967*4882a593Smuzhiyun
1968*4882a593Smuzhiyun            for l, h, cond_expr in self.ranges:
1969*4882a593Smuzhiyun                if self.config._eval_expr(cond_expr) != "n":
1970*4882a593Smuzhiyun                    has_active_range = True
1971*4882a593Smuzhiyun
1972*4882a593Smuzhiyun                    low_str = _str_val(l)
1973*4882a593Smuzhiyun                    high_str = _str_val(h)
1974*4882a593Smuzhiyun                    low = int(low_str, base) if \
1975*4882a593Smuzhiyun                      _is_base_n(low_str, base) else 0
1976*4882a593Smuzhiyun                    high = int(high_str, base) if \
1977*4882a593Smuzhiyun                      _is_base_n(high_str, base) else 0
1978*4882a593Smuzhiyun
1979*4882a593Smuzhiyun                    break
1980*4882a593Smuzhiyun
1981*4882a593Smuzhiyun            if vis != "n":
1982*4882a593Smuzhiyun                self.write_to_conf = True
1983*4882a593Smuzhiyun
1984*4882a593Smuzhiyun                if self.user_val is not None and \
1985*4882a593Smuzhiyun                   _is_base_n(self.user_val, base) and \
1986*4882a593Smuzhiyun                   (not has_active_range or
1987*4882a593Smuzhiyun                    low <= int(self.user_val, base) <= high):
1988*4882a593Smuzhiyun
1989*4882a593Smuzhiyun                    # If the user value is OK, it is stored in exactly the same
1990*4882a593Smuzhiyun                    # form as specified in the assignment (with or without
1991*4882a593Smuzhiyun                    # "0x", etc).
1992*4882a593Smuzhiyun
1993*4882a593Smuzhiyun                    use_defaults = False
1994*4882a593Smuzhiyun                    new_val = self.user_val
1995*4882a593Smuzhiyun
1996*4882a593Smuzhiyun            if use_defaults:
1997*4882a593Smuzhiyun                for val_expr, cond_expr in self.def_exprs:
1998*4882a593Smuzhiyun                    if self.config._eval_expr(cond_expr) != "n":
1999*4882a593Smuzhiyun                        self.write_to_conf = True
2000*4882a593Smuzhiyun
2001*4882a593Smuzhiyun                        # If the default value is OK, it is stored in exactly
2002*4882a593Smuzhiyun                        # the same form as specified. Otherwise, it is clamped
2003*4882a593Smuzhiyun                        # to the range, and the output has "0x" as appropriate
2004*4882a593Smuzhiyun                        # for the type.
2005*4882a593Smuzhiyun
2006*4882a593Smuzhiyun                        new_val = _str_val(val_expr)
2007*4882a593Smuzhiyun
2008*4882a593Smuzhiyun                        if _is_base_n(new_val, base):
2009*4882a593Smuzhiyun                            new_val_num = int(new_val, base)
2010*4882a593Smuzhiyun                            if has_active_range:
2011*4882a593Smuzhiyun                                clamped_val = None
2012*4882a593Smuzhiyun
2013*4882a593Smuzhiyun                                if new_val_num < low:
2014*4882a593Smuzhiyun                                    clamped_val = low
2015*4882a593Smuzhiyun                                elif new_val_num > high:
2016*4882a593Smuzhiyun                                    clamped_val = high
2017*4882a593Smuzhiyun
2018*4882a593Smuzhiyun                                if clamped_val is not None:
2019*4882a593Smuzhiyun                                    new_val = (hex(clamped_val) if \
2020*4882a593Smuzhiyun                                      self.type == HEX else str(clamped_val))
2021*4882a593Smuzhiyun
2022*4882a593Smuzhiyun                            break
2023*4882a593Smuzhiyun                else: # For the for loop
2024*4882a593Smuzhiyun                    # If no user value or default kicks in but the hex/int has
2025*4882a593Smuzhiyun                    # an active range, then the low end of the range is used,
2026*4882a593Smuzhiyun                    # provided it's > 0, with "0x" prepended as appropriate.
2027*4882a593Smuzhiyun                    if has_active_range and low > 0:
2028*4882a593Smuzhiyun                        new_val = (hex(low) if self.type == HEX else str(low))
2029*4882a593Smuzhiyun
2030*4882a593Smuzhiyun        elif self.type == STRING:
2031*4882a593Smuzhiyun            use_defaults = True
2032*4882a593Smuzhiyun
2033*4882a593Smuzhiyun            if vis != "n":
2034*4882a593Smuzhiyun                self.write_to_conf = True
2035*4882a593Smuzhiyun                if self.user_val is not None:
2036*4882a593Smuzhiyun                    new_val = self.user_val
2037*4882a593Smuzhiyun                    use_defaults = False
2038*4882a593Smuzhiyun
2039*4882a593Smuzhiyun            if use_defaults:
2040*4882a593Smuzhiyun                for val_expr, cond_expr in self.def_exprs:
2041*4882a593Smuzhiyun                    if self.config._eval_expr(cond_expr) != "n":
2042*4882a593Smuzhiyun                        self.write_to_conf = True
2043*4882a593Smuzhiyun                        new_val = _str_val(val_expr)
2044*4882a593Smuzhiyun                        break
2045*4882a593Smuzhiyun
2046*4882a593Smuzhiyun        self.cached_val = new_val
2047*4882a593Smuzhiyun        return new_val
2048*4882a593Smuzhiyun
2049*4882a593Smuzhiyun    def get_user_value(self):
2050*4882a593Smuzhiyun        """Returns the value assigned to the symbol in a .config or via
2051*4882a593Smuzhiyun        Symbol.set_user_value() (provided the value was valid for the type of
2052*4882a593Smuzhiyun        the symbol). Returns None in case of no user value."""
2053*4882a593Smuzhiyun        return self.user_val
2054*4882a593Smuzhiyun
2055*4882a593Smuzhiyun    def get_upper_bound(self):
2056*4882a593Smuzhiyun        """For string/hex/int symbols and for bool and tristate symbols that
2057*4882a593Smuzhiyun        cannot be modified (see is_modifiable()), returns None.
2058*4882a593Smuzhiyun
2059*4882a593Smuzhiyun        Otherwise, returns the highest value the symbol can be set to with
2060*4882a593Smuzhiyun        Symbol.set_user_value() (that will not be truncated): one of "m" or
2061*4882a593Smuzhiyun        "y", arranged from lowest to highest. This corresponds to the highest
2062*4882a593Smuzhiyun        value the symbol could be given in e.g. the 'make menuconfig'
2063*4882a593Smuzhiyun        interface.
2064*4882a593Smuzhiyun
2065*4882a593Smuzhiyun        See also the tri_less*() and tri_greater*() functions, which could come
2066*4882a593Smuzhiyun        in handy."""
2067*4882a593Smuzhiyun        if self.type != BOOL and self.type != TRISTATE:
2068*4882a593Smuzhiyun            return None
2069*4882a593Smuzhiyun        rev_dep = self.config._eval_expr(self.rev_dep)
2070*4882a593Smuzhiyun        # A bool selected to "m" gets promoted to "y", pinning it
2071*4882a593Smuzhiyun        if rev_dep == "m" and self.type == BOOL:
2072*4882a593Smuzhiyun            return None
2073*4882a593Smuzhiyun        vis = _get_visibility(self)
2074*4882a593Smuzhiyun        if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]:
2075*4882a593Smuzhiyun            return vis
2076*4882a593Smuzhiyun        return None
2077*4882a593Smuzhiyun
2078*4882a593Smuzhiyun    def get_lower_bound(self):
2079*4882a593Smuzhiyun        """For string/hex/int symbols and for bool and tristate symbols that
2080*4882a593Smuzhiyun        cannot be modified (see is_modifiable()), returns None.
2081*4882a593Smuzhiyun
2082*4882a593Smuzhiyun        Otherwise, returns the lowest value the symbol can be set to with
2083*4882a593Smuzhiyun        Symbol.set_user_value() (that will not be truncated): one of "n" or
2084*4882a593Smuzhiyun        "m", arranged from lowest to highest. This corresponds to the lowest
2085*4882a593Smuzhiyun        value the symbol could be given in e.g. the 'make menuconfig'
2086*4882a593Smuzhiyun        interface.
2087*4882a593Smuzhiyun
2088*4882a593Smuzhiyun        See also the tri_less*() and tri_greater*() functions, which could come
2089*4882a593Smuzhiyun        in handy."""
2090*4882a593Smuzhiyun        if self.type != BOOL and self.type != TRISTATE:
2091*4882a593Smuzhiyun            return None
2092*4882a593Smuzhiyun        rev_dep = self.config._eval_expr(self.rev_dep)
2093*4882a593Smuzhiyun        # A bool selected to "m" gets promoted to "y", pinning it
2094*4882a593Smuzhiyun        if rev_dep == "m" and self.type == BOOL:
2095*4882a593Smuzhiyun            return None
2096*4882a593Smuzhiyun        if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]:
2097*4882a593Smuzhiyun            return rev_dep
2098*4882a593Smuzhiyun        return None
2099*4882a593Smuzhiyun
2100*4882a593Smuzhiyun    def get_assignable_values(self):
2101*4882a593Smuzhiyun        """For string/hex/int symbols and for bool and tristate symbols that
2102*4882a593Smuzhiyun        cannot be modified (see is_modifiable()), returns the empty list.
2103*4882a593Smuzhiyun
2104*4882a593Smuzhiyun        Otherwise, returns a list containing the user values that can be
2105*4882a593Smuzhiyun        assigned to the symbol (that won't be truncated). Usage example:
2106*4882a593Smuzhiyun
2107*4882a593Smuzhiyun        if "m" in sym.get_assignable_values():
2108*4882a593Smuzhiyun            sym.set_user_value("m")
2109*4882a593Smuzhiyun
2110*4882a593Smuzhiyun        This is basically a more convenient interface to
2111*4882a593Smuzhiyun        get_lower/upper_bound() when wanting to test if a particular tristate
2112*4882a593Smuzhiyun        value can be assigned."""
2113*4882a593Smuzhiyun        if self.type != BOOL and self.type != TRISTATE:
2114*4882a593Smuzhiyun            return []
2115*4882a593Smuzhiyun        rev_dep = self.config._eval_expr(self.rev_dep)
2116*4882a593Smuzhiyun        # A bool selected to "m" gets promoted to "y", pinning it
2117*4882a593Smuzhiyun        if rev_dep == "m" and self.type == BOOL:
2118*4882a593Smuzhiyun            return []
2119*4882a593Smuzhiyun        res = ["n", "m", "y"][TRI_TO_INT[rev_dep] :
2120*4882a593Smuzhiyun                              TRI_TO_INT[_get_visibility(self)] + 1]
2121*4882a593Smuzhiyun        return res if len(res) > 1 else []
2122*4882a593Smuzhiyun
2123*4882a593Smuzhiyun    def get_visibility(self):
2124*4882a593Smuzhiyun        """Returns the visibility of the symbol: one of "n", "m" or "y". For
2125*4882a593Smuzhiyun        bool and tristate symbols, this is an upper bound on the value users
2126*4882a593Smuzhiyun        can set for the symbol. For other types of symbols, a visibility of "n"
2127*4882a593Smuzhiyun        means the user value will be ignored. A visibility of "n" corresponds
2128*4882a593Smuzhiyun        to not being visible in the 'make *config' interfaces.
2129*4882a593Smuzhiyun
2130*4882a593Smuzhiyun        Example (assuming we're running with modules enabled -- i.e., MODULES
2131*4882a593Smuzhiyun        set to 'y'):
2132*4882a593Smuzhiyun
2133*4882a593Smuzhiyun        # Assume this has been assigned 'n'
2134*4882a593Smuzhiyun        config N_SYM
2135*4882a593Smuzhiyun            tristate "N_SYM"
2136*4882a593Smuzhiyun
2137*4882a593Smuzhiyun        # Assume this has been assigned 'm'
2138*4882a593Smuzhiyun        config M_SYM
2139*4882a593Smuzhiyun            tristate "M_SYM"
2140*4882a593Smuzhiyun
2141*4882a593Smuzhiyun        # Has visibility 'n'
2142*4882a593Smuzhiyun        config A
2143*4882a593Smuzhiyun            tristate "A"
2144*4882a593Smuzhiyun            depends on N_SYM
2145*4882a593Smuzhiyun
2146*4882a593Smuzhiyun        # Has visibility 'm'
2147*4882a593Smuzhiyun        config B
2148*4882a593Smuzhiyun            tristate "B"
2149*4882a593Smuzhiyun            depends on M_SYM
2150*4882a593Smuzhiyun
2151*4882a593Smuzhiyun        # Has visibility 'y'
2152*4882a593Smuzhiyun        config C
2153*4882a593Smuzhiyun            tristate "C"
2154*4882a593Smuzhiyun
2155*4882a593Smuzhiyun        # Has no prompt, and hence visibility 'n'
2156*4882a593Smuzhiyun        config D
2157*4882a593Smuzhiyun            tristate
2158*4882a593Smuzhiyun
2159*4882a593Smuzhiyun        Having visibility be tri-valued ensures that e.g. a symbol cannot be
2160*4882a593Smuzhiyun        set to "y" by the user if it depends on a symbol with value "m", which
2161*4882a593Smuzhiyun        wouldn't be safe.
2162*4882a593Smuzhiyun
2163*4882a593Smuzhiyun        You should probably look at get_lower/upper_bound(),
2164*4882a593Smuzhiyun        get_assignable_values() and is_modifiable() before using this."""
2165*4882a593Smuzhiyun        return _get_visibility(self)
2166*4882a593Smuzhiyun
2167*4882a593Smuzhiyun    def get_referenced_symbols(self, refs_from_enclosing=False):
2168*4882a593Smuzhiyun        """Returns the set() of all symbols referenced by this symbol. For
2169*4882a593Smuzhiyun        example, the symbol defined by
2170*4882a593Smuzhiyun
2171*4882a593Smuzhiyun        config FOO
2172*4882a593Smuzhiyun            bool
2173*4882a593Smuzhiyun            prompt "foo" if A && B
2174*4882a593Smuzhiyun            default C if D
2175*4882a593Smuzhiyun            depends on E
2176*4882a593Smuzhiyun            select F if G
2177*4882a593Smuzhiyun
2178*4882a593Smuzhiyun        references the symbols A through G.
2179*4882a593Smuzhiyun
2180*4882a593Smuzhiyun        refs_from_enclosing (default: False): If True, the symbols referenced
2181*4882a593Smuzhiyun           by enclosing menus and ifs will be included in the result."""
2182*4882a593Smuzhiyun        return self.all_referenced_syms if refs_from_enclosing else \
2183*4882a593Smuzhiyun               self.referenced_syms
2184*4882a593Smuzhiyun
2185*4882a593Smuzhiyun    def get_selected_symbols(self):
2186*4882a593Smuzhiyun        """Returns the set() of all symbols X for which this symbol has a
2187*4882a593Smuzhiyun        'select X' or 'select X if Y' (regardless of whether Y is satisfied or
2188*4882a593Smuzhiyun        not). This is a subset of the symbols returned by
2189*4882a593Smuzhiyun        get_referenced_symbols()."""
2190*4882a593Smuzhiyun        return self.selected_syms
2191*4882a593Smuzhiyun
2192*4882a593Smuzhiyun    def set_user_value(self, v):
2193*4882a593Smuzhiyun        """Sets the user value of the symbol.
2194*4882a593Smuzhiyun
2195*4882a593Smuzhiyun        Equal in effect to assigning the value to the symbol within a .config
2196*4882a593Smuzhiyun        file. Use get_lower/upper_bound() or get_assignable_values() to find
2197*4882a593Smuzhiyun        the range of currently assignable values for bool and tristate symbols;
2198*4882a593Smuzhiyun        setting values outside this range will cause the user value to differ
2199*4882a593Smuzhiyun        from the result of Symbol.get_value() (be truncated). Values that are
2200*4882a593Smuzhiyun        invalid for the type (such as a_bool.set_user_value("foo")) are
2201*4882a593Smuzhiyun        ignored, and a warning is emitted if an attempt is made to assign such
2202*4882a593Smuzhiyun        a value.
2203*4882a593Smuzhiyun
2204*4882a593Smuzhiyun        For any type of symbol, is_modifiable() can be used to check if a user
2205*4882a593Smuzhiyun        value will currently have any effect on the symbol, as determined by
2206*4882a593Smuzhiyun        its visibility and range of assignable values. Any value that is valid
2207*4882a593Smuzhiyun        for the type (bool, tristate, etc.) will end up being reflected in
2208*4882a593Smuzhiyun        get_user_value() though, and might have an effect later if conditions
2209*4882a593Smuzhiyun        change. To get rid of the user value, use unset_user_value().
2210*4882a593Smuzhiyun
2211*4882a593Smuzhiyun        Any symbols dependent on the symbol are (recursively) invalidated, so
2212*4882a593Smuzhiyun        things will just work with regards to dependencies.
2213*4882a593Smuzhiyun
2214*4882a593Smuzhiyun        v: The user value to give to the symbol."""
2215*4882a593Smuzhiyun        self._set_user_value_no_invalidate(v, False)
2216*4882a593Smuzhiyun
2217*4882a593Smuzhiyun        # There might be something more efficient you could do here, but play
2218*4882a593Smuzhiyun        # it safe.
2219*4882a593Smuzhiyun        if self.name == "MODULES":
2220*4882a593Smuzhiyun            self.config._invalidate_all()
2221*4882a593Smuzhiyun            return
2222*4882a593Smuzhiyun
2223*4882a593Smuzhiyun        self._invalidate()
2224*4882a593Smuzhiyun        self._invalidate_dependent()
2225*4882a593Smuzhiyun
2226*4882a593Smuzhiyun    def unset_user_value(self):
2227*4882a593Smuzhiyun        """Resets the user value of the symbol, as if the symbol had never
2228*4882a593Smuzhiyun        gotten a user value via Config.load_config() or
2229*4882a593Smuzhiyun        Symbol.set_user_value()."""
2230*4882a593Smuzhiyun        self._unset_user_value_no_recursive_invalidate()
2231*4882a593Smuzhiyun        self._invalidate_dependent()
2232*4882a593Smuzhiyun
2233*4882a593Smuzhiyun    def is_modifiable(self):
2234*4882a593Smuzhiyun        """Returns True if the value of the symbol could be modified by calling
2235*4882a593Smuzhiyun        Symbol.set_user_value().
2236*4882a593Smuzhiyun
2237*4882a593Smuzhiyun        For bools and tristates, this corresponds to the symbol being visible
2238*4882a593Smuzhiyun        in the 'make menuconfig' interface and not already being pinned to a
2239*4882a593Smuzhiyun        specific value (e.g. because it is selected by another symbol).
2240*4882a593Smuzhiyun
2241*4882a593Smuzhiyun        For strings and numbers, this corresponds to just being visible. (See
2242*4882a593Smuzhiyun        Symbol.get_visibility().)"""
2243*4882a593Smuzhiyun        if self.is_special_:
2244*4882a593Smuzhiyun            return False
2245*4882a593Smuzhiyun        if self.type == BOOL or self.type == TRISTATE:
2246*4882a593Smuzhiyun            rev_dep = self.config._eval_expr(self.rev_dep)
2247*4882a593Smuzhiyun            # A bool selected to "m" gets promoted to "y", pinning it
2248*4882a593Smuzhiyun            if rev_dep == "m" and self.type == BOOL:
2249*4882a593Smuzhiyun                return False
2250*4882a593Smuzhiyun            return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]
2251*4882a593Smuzhiyun        return _get_visibility(self) != "n"
2252*4882a593Smuzhiyun
2253*4882a593Smuzhiyun    def is_defined(self):
2254*4882a593Smuzhiyun        """Returns False if the symbol is referred to in the Kconfig but never
2255*4882a593Smuzhiyun        actually defined."""
2256*4882a593Smuzhiyun        return self.is_defined_
2257*4882a593Smuzhiyun
2258*4882a593Smuzhiyun    def is_special(self):
2259*4882a593Smuzhiyun        """Returns True if the symbol is one of the special symbols n, m, y, or
2260*4882a593Smuzhiyun        UNAME_RELEASE, or gets its value from the environment."""
2261*4882a593Smuzhiyun        return self.is_special_
2262*4882a593Smuzhiyun
2263*4882a593Smuzhiyun    def is_from_environment(self):
2264*4882a593Smuzhiyun        """Returns True if the symbol gets its value from the environment."""
2265*4882a593Smuzhiyun        return self.is_from_env
2266*4882a593Smuzhiyun
2267*4882a593Smuzhiyun    def has_ranges(self):
2268*4882a593Smuzhiyun        """Returns True if the symbol is of type INT or HEX and has ranges that
2269*4882a593Smuzhiyun        limit what values it can take on."""
2270*4882a593Smuzhiyun        return bool(self.ranges)
2271*4882a593Smuzhiyun
2272*4882a593Smuzhiyun    def is_choice_symbol(self):
2273*4882a593Smuzhiyun        """Returns True if the symbol is in a choice statement and is an actual
2274*4882a593Smuzhiyun        choice symbol (see Choice.get_symbols())."""
2275*4882a593Smuzhiyun        return self.is_choice_sym
2276*4882a593Smuzhiyun
2277*4882a593Smuzhiyun    def is_choice_selection(self):
2278*4882a593Smuzhiyun        """Returns True if the symbol is contained in a choice statement and is
2279*4882a593Smuzhiyun        the selected item. Equivalent to
2280*4882a593Smuzhiyun
2281*4882a593Smuzhiyun        sym.is_choice_symbol() and sym.get_parent().get_selection() is sym"""
2282*4882a593Smuzhiyun        return self.is_choice_sym and self.parent.get_selection() is self
2283*4882a593Smuzhiyun
2284*4882a593Smuzhiyun    def is_allnoconfig_y(self):
2285*4882a593Smuzhiyun        """Returns True if the symbol has the 'allnoconfig_y' option set."""
2286*4882a593Smuzhiyun        return self.allnoconfig_y
2287*4882a593Smuzhiyun
2288*4882a593Smuzhiyun    def __str__(self):
2289*4882a593Smuzhiyun        """Returns a string containing various information about the symbol."""
2290*4882a593Smuzhiyun        return self.config._get_sym_or_choice_str(self)
2291*4882a593Smuzhiyun
2292*4882a593Smuzhiyun    #
2293*4882a593Smuzhiyun    # Private methods
2294*4882a593Smuzhiyun    #
2295*4882a593Smuzhiyun
2296*4882a593Smuzhiyun    def __init__(self):
2297*4882a593Smuzhiyun        """Symbol constructor -- not intended to be called directly by
2298*4882a593Smuzhiyun        Kconfiglib clients."""
2299*4882a593Smuzhiyun
2300*4882a593Smuzhiyun        self.name = None
2301*4882a593Smuzhiyun        self.type = UNKNOWN
2302*4882a593Smuzhiyun        self.prompts = []
2303*4882a593Smuzhiyun        self.def_exprs = [] # 'default' properties
2304*4882a593Smuzhiyun        self.ranges = [] # 'range' properties (for int and hex)
2305*4882a593Smuzhiyun        self.help = None # Help text
2306*4882a593Smuzhiyun        self.rev_dep = "n" # Reverse (select-related) dependencies
2307*4882a593Smuzhiyun        self.config = None
2308*4882a593Smuzhiyun        self.parent = None
2309*4882a593Smuzhiyun
2310*4882a593Smuzhiyun        self.user_val = None # Value set by user
2311*4882a593Smuzhiyun
2312*4882a593Smuzhiyun        # The prompt, default value and select conditions without any
2313*4882a593Smuzhiyun        # dependencies from menus and ifs propagated to them
2314*4882a593Smuzhiyun        self.orig_prompts = []
2315*4882a593Smuzhiyun        self.orig_def_exprs = []
2316*4882a593Smuzhiyun        self.orig_selects = []
2317*4882a593Smuzhiyun
2318*4882a593Smuzhiyun        # Dependencies inherited from containing menus and ifs
2319*4882a593Smuzhiyun        self.deps_from_containing = None
2320*4882a593Smuzhiyun        # The set of symbols referenced by this symbol (see
2321*4882a593Smuzhiyun        # get_referenced_symbols())
2322*4882a593Smuzhiyun        self.referenced_syms = set()
2323*4882a593Smuzhiyun        # The set of symbols selected by this symbol (see
2324*4882a593Smuzhiyun        # get_selected_symbols())
2325*4882a593Smuzhiyun        self.selected_syms = set()
2326*4882a593Smuzhiyun        # Like 'referenced_syms', but includes symbols from
2327*4882a593Smuzhiyun        # dependencies inherited from enclosing menus and ifs
2328*4882a593Smuzhiyun        self.all_referenced_syms = set()
2329*4882a593Smuzhiyun
2330*4882a593Smuzhiyun        # This records only dependencies specified with 'depends on'. Needed
2331*4882a593Smuzhiyun        # when determining actual choice items (hrrrr...). See also
2332*4882a593Smuzhiyun        # Choice._determine_actual_symbols().
2333*4882a593Smuzhiyun        self.menu_dep = None
2334*4882a593Smuzhiyun
2335*4882a593Smuzhiyun        # See Symbol.get_ref/def_locations().
2336*4882a593Smuzhiyun        self.def_locations = []
2337*4882a593Smuzhiyun        self.ref_locations = []
2338*4882a593Smuzhiyun
2339*4882a593Smuzhiyun        # Populated in Config._build_dep() after parsing. Links the symbol to
2340*4882a593Smuzhiyun        # the symbols that immediately depend on it (in a caching/invalidation
2341*4882a593Smuzhiyun        # sense). The total set of dependent symbols for the symbol (the
2342*4882a593Smuzhiyun        # transitive closure) is calculated on an as-needed basis in
2343*4882a593Smuzhiyun        # _get_dependent().
2344*4882a593Smuzhiyun        self.dep = set()
2345*4882a593Smuzhiyun
2346*4882a593Smuzhiyun        # Cached values
2347*4882a593Smuzhiyun
2348*4882a593Smuzhiyun        # Caches the calculated value
2349*4882a593Smuzhiyun        self.cached_val = None
2350*4882a593Smuzhiyun        # Caches the visibility, which acts as an upper bound on the value
2351*4882a593Smuzhiyun        self.cached_visibility = None
2352*4882a593Smuzhiyun        # Caches the total list of dependent symbols. Calculated in
2353*4882a593Smuzhiyun        # _get_dependent().
2354*4882a593Smuzhiyun        self.cached_deps = None
2355*4882a593Smuzhiyun
2356*4882a593Smuzhiyun        # Flags
2357*4882a593Smuzhiyun
2358*4882a593Smuzhiyun        # Does the symbol have an entry in the Kconfig file? The trailing
2359*4882a593Smuzhiyun        # underscore avoids a collision with is_defined().
2360*4882a593Smuzhiyun        self.is_defined_ = False
2361*4882a593Smuzhiyun        # Should the symbol get an entry in .config?
2362*4882a593Smuzhiyun        self.write_to_conf = False
2363*4882a593Smuzhiyun        # Set to true when _make_conf() is called on a symbol, so that symbols
2364*4882a593Smuzhiyun        # defined in multiple locations only get one .config entry. We need to
2365*4882a593Smuzhiyun        # reset it prior to writing out a new .config.
2366*4882a593Smuzhiyun        self.already_written = False
2367*4882a593Smuzhiyun        # This is set to True for "actual" choice symbols; see
2368*4882a593Smuzhiyun        # Choice._determine_actual_symbols().
2369*4882a593Smuzhiyun        self.is_choice_sym = False
2370*4882a593Smuzhiyun        # Does the symbol get its value in some special way, e.g. from the
2371*4882a593Smuzhiyun        # environment or by being one of the special symbols n, m, and y? If
2372*4882a593Smuzhiyun        # so, the value is stored in self.cached_val, which is never
2373*4882a593Smuzhiyun        # invalidated. The trailing underscore avoids a collision with
2374*4882a593Smuzhiyun        # is_special().
2375*4882a593Smuzhiyun        self.is_special_ = False
2376*4882a593Smuzhiyun        # Does the symbol get its value from the environment?
2377*4882a593Smuzhiyun        self.is_from_env = False
2378*4882a593Smuzhiyun        # Does the symbol have the 'allnoconfig_y' option set?
2379*4882a593Smuzhiyun        self.allnoconfig_y = False
2380*4882a593Smuzhiyun
2381*4882a593Smuzhiyun    def _invalidate(self):
2382*4882a593Smuzhiyun        if self.is_special_:
2383*4882a593Smuzhiyun            return
2384*4882a593Smuzhiyun
2385*4882a593Smuzhiyun        if self.is_choice_sym:
2386*4882a593Smuzhiyun            self.parent._invalidate()
2387*4882a593Smuzhiyun
2388*4882a593Smuzhiyun        self.cached_val = None
2389*4882a593Smuzhiyun        self.cached_visibility = None
2390*4882a593Smuzhiyun
2391*4882a593Smuzhiyun    def _invalidate_dependent(self):
2392*4882a593Smuzhiyun        for sym in self._get_dependent():
2393*4882a593Smuzhiyun            sym._invalidate()
2394*4882a593Smuzhiyun
2395*4882a593Smuzhiyun    def _set_user_value_no_invalidate(self, v, suppress_load_warnings):
2396*4882a593Smuzhiyun        """Like set_user_value(), but does not invalidate any symbols.
2397*4882a593Smuzhiyun
2398*4882a593Smuzhiyun        suppress_load_warnings: some warnings are annoying when loading a
2399*4882a593Smuzhiyun           .config that can be helpful when manually invoking set_user_value().
2400*4882a593Smuzhiyun           This flag is set to True to suppress such warnings.
2401*4882a593Smuzhiyun
2402*4882a593Smuzhiyun           Perhaps this could be made optional for load_config() instead."""
2403*4882a593Smuzhiyun
2404*4882a593Smuzhiyun        if self.is_special_:
2405*4882a593Smuzhiyun            if self.is_from_env:
2406*4882a593Smuzhiyun                self.config._warn('attempt to assign the value "{0}" to the '
2407*4882a593Smuzhiyun                                  'symbol {1}, which gets its value from the '
2408*4882a593Smuzhiyun                                  'environment. Assignment ignored.'
2409*4882a593Smuzhiyun                                  .format(v, self.name))
2410*4882a593Smuzhiyun            else:
2411*4882a593Smuzhiyun                self.config._warn('attempt to assign the value "{0}" to the '
2412*4882a593Smuzhiyun                                  'special symbol {1}. Assignment ignored.'
2413*4882a593Smuzhiyun                                  .format(v, self.name))
2414*4882a593Smuzhiyun            return
2415*4882a593Smuzhiyun
2416*4882a593Smuzhiyun        if not self.is_defined_:
2417*4882a593Smuzhiyun            filename, linenr = self.ref_locations[0]
2418*4882a593Smuzhiyun            if self.config.print_undef_assign:
2419*4882a593Smuzhiyun                _stderr_msg('note: attempt to assign the value "{0}" to {1}, '
2420*4882a593Smuzhiyun                            "which is referenced at {2}:{3} but never "
2421*4882a593Smuzhiyun                            "defined. Assignment ignored."
2422*4882a593Smuzhiyun                            .format(v, self.name, filename, linenr))
2423*4882a593Smuzhiyun            return
2424*4882a593Smuzhiyun
2425*4882a593Smuzhiyun        # Check if the value is valid for our type
2426*4882a593Smuzhiyun        if not ((self.type == BOOL     and (v == "y" or v == "n")   ) or
2427*4882a593Smuzhiyun                (self.type == TRISTATE and (v == "y" or v == "m" or
2428*4882a593Smuzhiyun                                            v == "n")               ) or
2429*4882a593Smuzhiyun                (self.type == STRING                                ) or
2430*4882a593Smuzhiyun                (self.type == INT      and _is_base_n(v, 10)        ) or
2431*4882a593Smuzhiyun                (self.type == HEX      and _is_base_n(v, 16)        )):
2432*4882a593Smuzhiyun            self.config._warn('the value "{0}" is invalid for {1}, which has '
2433*4882a593Smuzhiyun                              "type {2}. Assignment ignored."
2434*4882a593Smuzhiyun                              .format(v, self.name, TYPENAME[self.type]))
2435*4882a593Smuzhiyun            return
2436*4882a593Smuzhiyun
2437*4882a593Smuzhiyun        if not self.prompts and not suppress_load_warnings:
2438*4882a593Smuzhiyun            self.config._warn('assigning "{0}" to the symbol {1} which '
2439*4882a593Smuzhiyun                              'lacks prompts and thus has visibility "n". '
2440*4882a593Smuzhiyun                              'The assignment will have no effect.'
2441*4882a593Smuzhiyun                              .format(v, self.name))
2442*4882a593Smuzhiyun
2443*4882a593Smuzhiyun        self.user_val = v
2444*4882a593Smuzhiyun
2445*4882a593Smuzhiyun        if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE):
2446*4882a593Smuzhiyun            choice = self.parent
2447*4882a593Smuzhiyun            if v == "y":
2448*4882a593Smuzhiyun                choice.user_val = self
2449*4882a593Smuzhiyun                choice.user_mode = "y"
2450*4882a593Smuzhiyun            elif v == "m":
2451*4882a593Smuzhiyun                choice.user_val = None
2452*4882a593Smuzhiyun                choice.user_mode = "m"
2453*4882a593Smuzhiyun
2454*4882a593Smuzhiyun    def _unset_user_value_no_recursive_invalidate(self):
2455*4882a593Smuzhiyun        self._invalidate()
2456*4882a593Smuzhiyun        self.user_val = None
2457*4882a593Smuzhiyun
2458*4882a593Smuzhiyun        if self.is_choice_sym:
2459*4882a593Smuzhiyun            self.parent._unset_user_value()
2460*4882a593Smuzhiyun
2461*4882a593Smuzhiyun    def _make_conf(self, append_fn):
2462*4882a593Smuzhiyun        if self.already_written:
2463*4882a593Smuzhiyun            return
2464*4882a593Smuzhiyun
2465*4882a593Smuzhiyun        self.already_written = True
2466*4882a593Smuzhiyun
2467*4882a593Smuzhiyun        # Note: write_to_conf is determined in get_value()
2468*4882a593Smuzhiyun        val = self.get_value()
2469*4882a593Smuzhiyun        if not self.write_to_conf:
2470*4882a593Smuzhiyun            return
2471*4882a593Smuzhiyun
2472*4882a593Smuzhiyun        if self.type == BOOL or self.type == TRISTATE:
2473*4882a593Smuzhiyun            if val == "y" or val == "m":
2474*4882a593Smuzhiyun                append_fn("CONFIG_{0}={1}".format(self.name, val))
2475*4882a593Smuzhiyun            else:
2476*4882a593Smuzhiyun                append_fn("# CONFIG_{0} is not set".format(self.name))
2477*4882a593Smuzhiyun
2478*4882a593Smuzhiyun        elif self.type == INT or self.type == HEX:
2479*4882a593Smuzhiyun            append_fn("CONFIG_{0}={1}".format(self.name, val))
2480*4882a593Smuzhiyun
2481*4882a593Smuzhiyun        elif self.type == STRING:
2482*4882a593Smuzhiyun            # Escape \ and "
2483*4882a593Smuzhiyun            append_fn('CONFIG_{0}="{1}"'
2484*4882a593Smuzhiyun                      .format(self.name,
2485*4882a593Smuzhiyun                              val.replace("\\", "\\\\").replace('"', '\\"')))
2486*4882a593Smuzhiyun
2487*4882a593Smuzhiyun        else:
2488*4882a593Smuzhiyun            _internal_error("Internal error while creating .config: unknown "
2489*4882a593Smuzhiyun                            'type "{0}".'.format(self.type))
2490*4882a593Smuzhiyun
2491*4882a593Smuzhiyun    def _get_dependent(self):
2492*4882a593Smuzhiyun        """Returns the set of symbols that should be invalidated if the value
2493*4882a593Smuzhiyun        of the symbol changes, because they might be affected by the change.
2494*4882a593Smuzhiyun        Note that this is an internal API -- it's probably of limited
2495*4882a593Smuzhiyun        usefulness to clients."""
2496*4882a593Smuzhiyun        if self.cached_deps is not None:
2497*4882a593Smuzhiyun            return self.cached_deps
2498*4882a593Smuzhiyun
2499*4882a593Smuzhiyun        res = set(self.dep)
2500*4882a593Smuzhiyun        for s in self.dep:
2501*4882a593Smuzhiyun            res |= s._get_dependent()
2502*4882a593Smuzhiyun
2503*4882a593Smuzhiyun        if self.is_choice_sym:
2504*4882a593Smuzhiyun            # Choice symbols also depend (recursively) on their siblings. The
2505*4882a593Smuzhiyun            # siblings are not included in 'dep' to avoid dependency loops.
2506*4882a593Smuzhiyun            for sibling in self.parent.actual_symbols:
2507*4882a593Smuzhiyun                if sibling is not self:
2508*4882a593Smuzhiyun                    res.add(sibling)
2509*4882a593Smuzhiyun                    res |= sibling.dep
2510*4882a593Smuzhiyun                    for s in sibling.dep:
2511*4882a593Smuzhiyun                        res |= s._get_dependent()
2512*4882a593Smuzhiyun
2513*4882a593Smuzhiyun        self.cached_deps = res
2514*4882a593Smuzhiyun        return res
2515*4882a593Smuzhiyun
2516*4882a593Smuzhiyun    def _has_auto_menu_dep_on(self, on):
2517*4882a593Smuzhiyun        """See Choice._determine_actual_symbols()."""
2518*4882a593Smuzhiyun        if not isinstance(self.parent, Choice):
2519*4882a593Smuzhiyun            _internal_error("Attempt to determine auto menu dependency for "
2520*4882a593Smuzhiyun                            "symbol ouside of choice.")
2521*4882a593Smuzhiyun
2522*4882a593Smuzhiyun        if not self.prompts:
2523*4882a593Smuzhiyun            # If we have no prompt, use the menu dependencies instead (what was
2524*4882a593Smuzhiyun            # specified with 'depends on')
2525*4882a593Smuzhiyun            return self.menu_dep is not None and \
2526*4882a593Smuzhiyun                   self.config._expr_depends_on(self.menu_dep, on)
2527*4882a593Smuzhiyun
2528*4882a593Smuzhiyun        for _, cond_expr in self.prompts:
2529*4882a593Smuzhiyun            if self.config._expr_depends_on(cond_expr, on):
2530*4882a593Smuzhiyun                return True
2531*4882a593Smuzhiyun
2532*4882a593Smuzhiyun        return False
2533*4882a593Smuzhiyun
2534*4882a593Smuzhiyunclass Menu(Item):
2535*4882a593Smuzhiyun
2536*4882a593Smuzhiyun    """Represents a menu statement."""
2537*4882a593Smuzhiyun
2538*4882a593Smuzhiyun    #
2539*4882a593Smuzhiyun    # Public interface
2540*4882a593Smuzhiyun    #
2541*4882a593Smuzhiyun
2542*4882a593Smuzhiyun    def get_config(self):
2543*4882a593Smuzhiyun        """Return the Config instance this menu is from."""
2544*4882a593Smuzhiyun        return self.config
2545*4882a593Smuzhiyun
2546*4882a593Smuzhiyun    def get_title(self):
2547*4882a593Smuzhiyun        """Returns the title text of the menu."""
2548*4882a593Smuzhiyun        return self.title
2549*4882a593Smuzhiyun
2550*4882a593Smuzhiyun    def get_parent(self):
2551*4882a593Smuzhiyun        """Returns the menu or choice statement that contains the menu, or
2552*4882a593Smuzhiyun        None if the menu is at the top level. Note that if statements are
2553*4882a593Smuzhiyun        treated as syntactic sugar and do not have an explicit class
2554*4882a593Smuzhiyun        representation."""
2555*4882a593Smuzhiyun        return self.parent
2556*4882a593Smuzhiyun
2557*4882a593Smuzhiyun    def get_location(self):
2558*4882a593Smuzhiyun        """Returns the location of the menu as a (filename, linenr) tuple,
2559*4882a593Smuzhiyun        where filename is a string and linenr an int."""
2560*4882a593Smuzhiyun        return (self.filename, self.linenr)
2561*4882a593Smuzhiyun
2562*4882a593Smuzhiyun    def get_items(self, recursive=False):
2563*4882a593Smuzhiyun        """Returns a list containing the items (symbols, menus, choice
2564*4882a593Smuzhiyun        statements and comments) in in the menu, in the same order that the
2565*4882a593Smuzhiyun        items appear within the menu.
2566*4882a593Smuzhiyun
2567*4882a593Smuzhiyun        recursive (default: False): True if items contained in items within the
2568*4882a593Smuzhiyun           menu should be included recursively (preorder)."""
2569*4882a593Smuzhiyun
2570*4882a593Smuzhiyun        if not recursive:
2571*4882a593Smuzhiyun            return self.block
2572*4882a593Smuzhiyun
2573*4882a593Smuzhiyun        res = []
2574*4882a593Smuzhiyun        for item in self.block:
2575*4882a593Smuzhiyun            res.append(item)
2576*4882a593Smuzhiyun            if isinstance(item, Menu):
2577*4882a593Smuzhiyun                res.extend(item.get_items(True))
2578*4882a593Smuzhiyun            elif isinstance(item, Choice):
2579*4882a593Smuzhiyun                res.extend(item.get_items())
2580*4882a593Smuzhiyun        return res
2581*4882a593Smuzhiyun
2582*4882a593Smuzhiyun    def get_symbols(self, recursive=False):
2583*4882a593Smuzhiyun        """Returns a list containing the symbols in the menu, in the same order
2584*4882a593Smuzhiyun        that they appear within the menu.
2585*4882a593Smuzhiyun
2586*4882a593Smuzhiyun        recursive (default: False): True if symbols contained in items within
2587*4882a593Smuzhiyun           the menu should be included recursively."""
2588*4882a593Smuzhiyun
2589*4882a593Smuzhiyun        return [item for item in self.get_items(recursive) if
2590*4882a593Smuzhiyun                isinstance(item, Symbol)]
2591*4882a593Smuzhiyun
2592*4882a593Smuzhiyun    def get_visibility(self):
2593*4882a593Smuzhiyun        """Returns the visibility of the menu. This also affects the visibility
2594*4882a593Smuzhiyun        of subitems. See also Symbol.get_visibility()."""
2595*4882a593Smuzhiyun        return self.config._eval_expr(self.dep_expr)
2596*4882a593Smuzhiyun
2597*4882a593Smuzhiyun    def get_visible_if_visibility(self):
2598*4882a593Smuzhiyun        """Returns the visibility the menu gets from its 'visible if'
2599*4882a593Smuzhiyun        condition. "y" if the menu has no 'visible if' condition."""
2600*4882a593Smuzhiyun        return self.config._eval_expr(self.visible_if_expr)
2601*4882a593Smuzhiyun
2602*4882a593Smuzhiyun    def get_referenced_symbols(self, refs_from_enclosing=False):
2603*4882a593Smuzhiyun        """See Symbol.get_referenced_symbols()."""
2604*4882a593Smuzhiyun        return self.all_referenced_syms if refs_from_enclosing else \
2605*4882a593Smuzhiyun               self.referenced_syms
2606*4882a593Smuzhiyun
2607*4882a593Smuzhiyun    def __str__(self):
2608*4882a593Smuzhiyun        """Returns a string containing various information about the menu."""
2609*4882a593Smuzhiyun        depends_on_str = self.config._expr_val_str(self.orig_deps,
2610*4882a593Smuzhiyun                                                   "(no dependencies)")
2611*4882a593Smuzhiyun        visible_if_str = self.config._expr_val_str(self.visible_if_expr,
2612*4882a593Smuzhiyun                                                   "(no dependencies)")
2613*4882a593Smuzhiyun
2614*4882a593Smuzhiyun        additional_deps_str = " " + \
2615*4882a593Smuzhiyun          self.config._expr_val_str(self.deps_from_containing,
2616*4882a593Smuzhiyun                                    "(no additional dependencies)")
2617*4882a593Smuzhiyun
2618*4882a593Smuzhiyun        return _lines("Menu",
2619*4882a593Smuzhiyun                      "Title                     : " + self.title,
2620*4882a593Smuzhiyun                      "'depends on' dependencies : " + depends_on_str,
2621*4882a593Smuzhiyun                      "'visible if' dependencies : " + visible_if_str,
2622*4882a593Smuzhiyun                      "Additional dependencies from enclosing menus and "
2623*4882a593Smuzhiyun                        "ifs:",
2624*4882a593Smuzhiyun                      additional_deps_str,
2625*4882a593Smuzhiyun                      "Location: {0}:{1}".format(self.filename, self.linenr))
2626*4882a593Smuzhiyun
2627*4882a593Smuzhiyun    #
2628*4882a593Smuzhiyun    # Private methods
2629*4882a593Smuzhiyun    #
2630*4882a593Smuzhiyun
2631*4882a593Smuzhiyun    def __init__(self):
2632*4882a593Smuzhiyun        """Menu constructor -- not intended to be called directly by
2633*4882a593Smuzhiyun        Kconfiglib clients."""
2634*4882a593Smuzhiyun
2635*4882a593Smuzhiyun        self.title = None
2636*4882a593Smuzhiyun        self.dep_expr = None
2637*4882a593Smuzhiyun        self.visible_if_expr = None
2638*4882a593Smuzhiyun        self.block = None
2639*4882a593Smuzhiyun        self.config = None
2640*4882a593Smuzhiyun        self.parent = None
2641*4882a593Smuzhiyun
2642*4882a593Smuzhiyun        # Dependency expression without dependencies from enclosing menus and
2643*4882a593Smuzhiyun        # ifs propagated
2644*4882a593Smuzhiyun        self.orig_deps = None
2645*4882a593Smuzhiyun
2646*4882a593Smuzhiyun        # Dependencies inherited from containing menus and ifs
2647*4882a593Smuzhiyun        self.deps_from_containing = None
2648*4882a593Smuzhiyun        # The set of symbols referenced by this menu (see
2649*4882a593Smuzhiyun        # get_referenced_symbols())
2650*4882a593Smuzhiyun        self.referenced_syms = set()
2651*4882a593Smuzhiyun        # Like 'referenced_syms', but includes symbols from
2652*4882a593Smuzhiyun        # dependencies inherited from enclosing menus and ifs
2653*4882a593Smuzhiyun        self.all_referenced_syms = None
2654*4882a593Smuzhiyun
2655*4882a593Smuzhiyun        self.filename = None
2656*4882a593Smuzhiyun        self.linenr = None
2657*4882a593Smuzhiyun
2658*4882a593Smuzhiyun    def _make_conf(self, append_fn):
2659*4882a593Smuzhiyun        if self.config._eval_expr(self.dep_expr) != "n" and \
2660*4882a593Smuzhiyun           self.config._eval_expr(self.visible_if_expr) != "n":
2661*4882a593Smuzhiyun            append_fn("\n#\n# {0}\n#".format(self.title))
2662*4882a593Smuzhiyun        _make_block_conf(self.block, append_fn)
2663*4882a593Smuzhiyun
2664*4882a593Smuzhiyunclass Choice(Item):
2665*4882a593Smuzhiyun
2666*4882a593Smuzhiyun    """Represents a choice statement. A choice can be in one of three modes:
2667*4882a593Smuzhiyun
2668*4882a593Smuzhiyun    "n" - The choice is not visible and no symbols can be selected.
2669*4882a593Smuzhiyun
2670*4882a593Smuzhiyun    "m" - Any number of symbols can be set to "m". The rest will be "n". This
2671*4882a593Smuzhiyun          is safe since potentially conflicting options don't actually get
2672*4882a593Smuzhiyun          compiled into the kernel simultaneously with "m".
2673*4882a593Smuzhiyun
2674*4882a593Smuzhiyun    "y" - One symbol will be "y" while the rest are "n".
2675*4882a593Smuzhiyun
2676*4882a593Smuzhiyun    Only tristate choices can be in "m" mode, and the visibility of the choice
2677*4882a593Smuzhiyun    is an upper bound on the mode, so that e.g. a choice that depends on a
2678*4882a593Smuzhiyun    symbol with value "m" will be in "m" mode.
2679*4882a593Smuzhiyun
2680*4882a593Smuzhiyun    The mode changes automatically when a value is assigned to a symbol within
2681*4882a593Smuzhiyun    the choice.
2682*4882a593Smuzhiyun
2683*4882a593Smuzhiyun    See Symbol.get_visibility() too."""
2684*4882a593Smuzhiyun
2685*4882a593Smuzhiyun    #
2686*4882a593Smuzhiyun    # Public interface
2687*4882a593Smuzhiyun    #
2688*4882a593Smuzhiyun
2689*4882a593Smuzhiyun    def get_config(self):
2690*4882a593Smuzhiyun        """Returns the Config instance this choice is from."""
2691*4882a593Smuzhiyun        return self.config
2692*4882a593Smuzhiyun
2693*4882a593Smuzhiyun    def get_name(self):
2694*4882a593Smuzhiyun        """For named choices, returns the name. Returns None for unnamed
2695*4882a593Smuzhiyun        choices. No named choices appear anywhere in the kernel Kconfig files
2696*4882a593Smuzhiyun        as of Linux 3.7.0-rc8."""
2697*4882a593Smuzhiyun        return self.name
2698*4882a593Smuzhiyun
2699*4882a593Smuzhiyun    def get_type(self):
2700*4882a593Smuzhiyun        """Returns the type of the choice. See Symbol.get_type()."""
2701*4882a593Smuzhiyun        return self.type
2702*4882a593Smuzhiyun
2703*4882a593Smuzhiyun    def get_prompts(self):
2704*4882a593Smuzhiyun        """Returns a list of prompts defined for the choice, in the order they
2705*4882a593Smuzhiyun        appear in the configuration files. Returns the empty list for choices
2706*4882a593Smuzhiyun        with no prompt.
2707*4882a593Smuzhiyun
2708*4882a593Smuzhiyun        This list will have a single entry for the vast majority of choices
2709*4882a593Smuzhiyun        having prompts, but having multiple prompts for a single choice is
2710*4882a593Smuzhiyun        possible through having multiple 'choice' entries for it (though I'm
2711*4882a593Smuzhiyun        not sure if that ever happens in practice)."""
2712*4882a593Smuzhiyun        return [prompt for prompt, _ in self.orig_prompts]
2713*4882a593Smuzhiyun
2714*4882a593Smuzhiyun    def get_help(self):
2715*4882a593Smuzhiyun        """Returns the help text of the choice, or None if the choice has no
2716*4882a593Smuzhiyun        help text."""
2717*4882a593Smuzhiyun        return self.help
2718*4882a593Smuzhiyun
2719*4882a593Smuzhiyun    def get_parent(self):
2720*4882a593Smuzhiyun        """Returns the menu or choice statement that contains the choice, or
2721*4882a593Smuzhiyun        None if the choice is at the top level. Note that if statements are
2722*4882a593Smuzhiyun        treated as syntactic sugar and do not have an explicit class
2723*4882a593Smuzhiyun        representation."""
2724*4882a593Smuzhiyun        return self.parent
2725*4882a593Smuzhiyun
2726*4882a593Smuzhiyun    def get_def_locations(self):
2727*4882a593Smuzhiyun        """Returns a list of (filename, linenr) tuples, where filename (string)
2728*4882a593Smuzhiyun        and linenr (int) represent a location where the choice is defined. For
2729*4882a593Smuzhiyun        the vast majority of choices (all of them as of Linux 3.7.0-rc8) this
2730*4882a593Smuzhiyun        list will only contain one element, but its possible for named choices
2731*4882a593Smuzhiyun        to be defined in multiple locations."""
2732*4882a593Smuzhiyun        return self.def_locations
2733*4882a593Smuzhiyun
2734*4882a593Smuzhiyun    def get_selection(self):
2735*4882a593Smuzhiyun        """Returns the symbol selected (either by the user or through
2736*4882a593Smuzhiyun        defaults), or None if either no symbol is selected or the mode is not
2737*4882a593Smuzhiyun        "y"."""
2738*4882a593Smuzhiyun        if self.cached_selection is not None:
2739*4882a593Smuzhiyun            if self.cached_selection == NO_SELECTION:
2740*4882a593Smuzhiyun                return None
2741*4882a593Smuzhiyun            return self.cached_selection
2742*4882a593Smuzhiyun
2743*4882a593Smuzhiyun        if self.get_mode() != "y":
2744*4882a593Smuzhiyun            return self._cache_ret(None)
2745*4882a593Smuzhiyun
2746*4882a593Smuzhiyun        # User choice available?
2747*4882a593Smuzhiyun        if self.user_val is not None and _get_visibility(self.user_val) == "y":
2748*4882a593Smuzhiyun            return self._cache_ret(self.user_val)
2749*4882a593Smuzhiyun
2750*4882a593Smuzhiyun        if self.optional:
2751*4882a593Smuzhiyun            return self._cache_ret(None)
2752*4882a593Smuzhiyun
2753*4882a593Smuzhiyun        return self._cache_ret(self.get_selection_from_defaults())
2754*4882a593Smuzhiyun
2755*4882a593Smuzhiyun    def get_selection_from_defaults(self):
2756*4882a593Smuzhiyun        """Like Choice.get_selection(), but acts as if no symbol has been
2757*4882a593Smuzhiyun        selected by the user and no 'optional' flag is in effect."""
2758*4882a593Smuzhiyun
2759*4882a593Smuzhiyun        if not self.actual_symbols:
2760*4882a593Smuzhiyun            return None
2761*4882a593Smuzhiyun
2762*4882a593Smuzhiyun        for symbol, cond_expr in self.def_exprs:
2763*4882a593Smuzhiyun            if self.config._eval_expr(cond_expr) != "n":
2764*4882a593Smuzhiyun                chosen_symbol = symbol
2765*4882a593Smuzhiyun                break
2766*4882a593Smuzhiyun        else:
2767*4882a593Smuzhiyun            chosen_symbol = self.actual_symbols[0]
2768*4882a593Smuzhiyun
2769*4882a593Smuzhiyun        # Is the chosen symbol visible?
2770*4882a593Smuzhiyun        if _get_visibility(chosen_symbol) != "n":
2771*4882a593Smuzhiyun            return chosen_symbol
2772*4882a593Smuzhiyun        # Otherwise, pick the first visible symbol
2773*4882a593Smuzhiyun        for sym in self.actual_symbols:
2774*4882a593Smuzhiyun            if _get_visibility(sym) != "n":
2775*4882a593Smuzhiyun                return sym
2776*4882a593Smuzhiyun        return None
2777*4882a593Smuzhiyun
2778*4882a593Smuzhiyun    def get_user_selection(self):
2779*4882a593Smuzhiyun        """If the choice is in "y" mode and has a user-selected symbol, returns
2780*4882a593Smuzhiyun        that symbol. Otherwise, returns None."""
2781*4882a593Smuzhiyun        return self.user_val
2782*4882a593Smuzhiyun
2783*4882a593Smuzhiyun    def get_items(self):
2784*4882a593Smuzhiyun        """Gets all items contained in the choice in the same order as within
2785*4882a593Smuzhiyun        the configuration ("items" instead of "symbols" since choices and
2786*4882a593Smuzhiyun        comments might appear within choices. This only happens in one place as
2787*4882a593Smuzhiyun        of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig)."""
2788*4882a593Smuzhiyun        return self.block
2789*4882a593Smuzhiyun
2790*4882a593Smuzhiyun    def get_symbols(self):
2791*4882a593Smuzhiyun        """Returns a list containing the choice's symbols.
2792*4882a593Smuzhiyun
2793*4882a593Smuzhiyun        A quirk (perhaps a bug) of Kconfig is that you can put items within a
2794*4882a593Smuzhiyun        choice that will not be considered members of the choice insofar as
2795*4882a593Smuzhiyun        selection is concerned. This happens for example if one symbol within a
2796*4882a593Smuzhiyun        choice 'depends on' the symbol preceding it, or if you put non-symbol
2797*4882a593Smuzhiyun        items within choices.
2798*4882a593Smuzhiyun
2799*4882a593Smuzhiyun        As of Linux 3.7.0-rc8, this seems to be used intentionally in one
2800*4882a593Smuzhiyun        place: drivers/usb/gadget/Kconfig.
2801*4882a593Smuzhiyun
2802*4882a593Smuzhiyun        This function returns the "proper" symbols of the choice in the order
2803*4882a593Smuzhiyun        they appear in the choice, excluding such items. If you want all items
2804*4882a593Smuzhiyun        in the choice, use get_items()."""
2805*4882a593Smuzhiyun        return self.actual_symbols
2806*4882a593Smuzhiyun
2807*4882a593Smuzhiyun    def get_referenced_symbols(self, refs_from_enclosing=False):
2808*4882a593Smuzhiyun        """See Symbol.get_referenced_symbols()."""
2809*4882a593Smuzhiyun        return self.all_referenced_syms if refs_from_enclosing else \
2810*4882a593Smuzhiyun               self.referenced_syms
2811*4882a593Smuzhiyun
2812*4882a593Smuzhiyun    def get_visibility(self):
2813*4882a593Smuzhiyun        """Returns the visibility of the choice statement: one of "n", "m" or
2814*4882a593Smuzhiyun        "y". This acts as an upper limit on the mode of the choice (though bool
2815*4882a593Smuzhiyun        choices can only have the mode "y"). See the class documentation for an
2816*4882a593Smuzhiyun        explanation of modes."""
2817*4882a593Smuzhiyun        return _get_visibility(self)
2818*4882a593Smuzhiyun
2819*4882a593Smuzhiyun    def get_mode(self):
2820*4882a593Smuzhiyun        """Returns the mode of the choice. See the class documentation for
2821*4882a593Smuzhiyun        an explanation of modes."""
2822*4882a593Smuzhiyun        minimum_mode = "n" if self.optional else "m"
2823*4882a593Smuzhiyun        mode = self.user_mode if self.user_mode is not None else minimum_mode
2824*4882a593Smuzhiyun        mode = self.config._eval_min(mode, _get_visibility(self))
2825*4882a593Smuzhiyun
2826*4882a593Smuzhiyun        # Promote "m" to "y" for boolean choices
2827*4882a593Smuzhiyun        if mode == "m" and self.type == BOOL:
2828*4882a593Smuzhiyun            return "y"
2829*4882a593Smuzhiyun
2830*4882a593Smuzhiyun        return mode
2831*4882a593Smuzhiyun
2832*4882a593Smuzhiyun    def is_optional(self):
2833*4882a593Smuzhiyun        """Returns True if the choice has the 'optional' flag set (and so will
2834*4882a593Smuzhiyun        default to "n" mode)."""
2835*4882a593Smuzhiyun        return self.optional
2836*4882a593Smuzhiyun
2837*4882a593Smuzhiyun    def __str__(self):
2838*4882a593Smuzhiyun        """Returns a string containing various information about the choice
2839*4882a593Smuzhiyun        statement."""
2840*4882a593Smuzhiyun        return self.config._get_sym_or_choice_str(self)
2841*4882a593Smuzhiyun
2842*4882a593Smuzhiyun    #
2843*4882a593Smuzhiyun    # Private methods
2844*4882a593Smuzhiyun    #
2845*4882a593Smuzhiyun
2846*4882a593Smuzhiyun    def __init__(self):
2847*4882a593Smuzhiyun        """Choice constructor -- not intended to be called directly by
2848*4882a593Smuzhiyun        Kconfiglib clients."""
2849*4882a593Smuzhiyun
2850*4882a593Smuzhiyun        self.name = None # Yes, choices can be named
2851*4882a593Smuzhiyun        self.type = UNKNOWN
2852*4882a593Smuzhiyun        self.prompts = []
2853*4882a593Smuzhiyun        self.def_exprs = [] # 'default' properties
2854*4882a593Smuzhiyun        self.help = None # Help text
2855*4882a593Smuzhiyun        self.block = None # List of contained items
2856*4882a593Smuzhiyun        self.config = None
2857*4882a593Smuzhiyun        self.parent = None
2858*4882a593Smuzhiyun
2859*4882a593Smuzhiyun        self.user_val = None
2860*4882a593Smuzhiyun        self.user_mode = None
2861*4882a593Smuzhiyun
2862*4882a593Smuzhiyun        # We need to filter out symbols that appear within the choice block but
2863*4882a593Smuzhiyun        # are not considered choice items (see
2864*4882a593Smuzhiyun        # Choice._determine_actual_symbols()) This list holds the "actual"
2865*4882a593Smuzhiyun        # choice items.
2866*4882a593Smuzhiyun        self.actual_symbols = []
2867*4882a593Smuzhiyun
2868*4882a593Smuzhiyun        # The prompts and default values without any dependencies from
2869*4882a593Smuzhiyun        # enclosing menus and ifs propagated
2870*4882a593Smuzhiyun        self.orig_prompts = []
2871*4882a593Smuzhiyun        self.orig_def_exprs = []
2872*4882a593Smuzhiyun
2873*4882a593Smuzhiyun        # Dependencies inherited from containing menus and ifs
2874*4882a593Smuzhiyun        self.deps_from_containing = None
2875*4882a593Smuzhiyun        # The set of symbols referenced by this choice (see
2876*4882a593Smuzhiyun        # get_referenced_symbols())
2877*4882a593Smuzhiyun        self.referenced_syms = set()
2878*4882a593Smuzhiyun        # Like 'referenced_syms', but includes symbols from
2879*4882a593Smuzhiyun        # dependencies inherited from enclosing menus and ifs
2880*4882a593Smuzhiyun        self.all_referenced_syms = set()
2881*4882a593Smuzhiyun
2882*4882a593Smuzhiyun        # See Choice.get_def_locations()
2883*4882a593Smuzhiyun        self.def_locations = []
2884*4882a593Smuzhiyun
2885*4882a593Smuzhiyun        # Cached values
2886*4882a593Smuzhiyun        self.cached_selection = None
2887*4882a593Smuzhiyun        self.cached_visibility = None
2888*4882a593Smuzhiyun
2889*4882a593Smuzhiyun        self.optional = False
2890*4882a593Smuzhiyun
2891*4882a593Smuzhiyun    def _determine_actual_symbols(self):
2892*4882a593Smuzhiyun        """If a symbol's visibility depends on the preceding symbol within a
2893*4882a593Smuzhiyun        choice, it is no longer viewed as a choice item. (This is quite
2894*4882a593Smuzhiyun        possibly a bug, but some things consciously use it... ugh. It stems
2895*4882a593Smuzhiyun        from automatic submenu creation.) In addition, it's possible to have
2896*4882a593Smuzhiyun        choices and comments within choices, and those shouldn't be considered
2897*4882a593Smuzhiyun        choice items either. Only drivers/usb/gadget/Kconfig seems to depend on
2898*4882a593Smuzhiyun        any of this. This method computes the "actual" items in the choice and
2899*4882a593Smuzhiyun        sets the is_choice_sym flag on them (retrieved via is_choice_symbol()).
2900*4882a593Smuzhiyun
2901*4882a593Smuzhiyun        Don't let this scare you: an earlier version simply checked for a
2902*4882a593Smuzhiyun        sequence of symbols where all symbols after the first appeared in the
2903*4882a593Smuzhiyun        'depends on' expression of the first, and that worked fine.  The added
2904*4882a593Smuzhiyun        complexity is to be future-proof in the event that
2905*4882a593Smuzhiyun        drivers/usb/gadget/Kconfig turns even more sinister. It might very well
2906*4882a593Smuzhiyun        be overkilling things (especially if that file is refactored ;)."""
2907*4882a593Smuzhiyun
2908*4882a593Smuzhiyun        # Items might depend on each other in a tree structure, so we need a
2909*4882a593Smuzhiyun        # stack to keep track of the current tentative parent
2910*4882a593Smuzhiyun        stack = []
2911*4882a593Smuzhiyun
2912*4882a593Smuzhiyun        for item in self.block:
2913*4882a593Smuzhiyun            if not isinstance(item, Symbol):
2914*4882a593Smuzhiyun                stack = []
2915*4882a593Smuzhiyun                continue
2916*4882a593Smuzhiyun
2917*4882a593Smuzhiyun            while stack:
2918*4882a593Smuzhiyun                if item._has_auto_menu_dep_on(stack[-1]):
2919*4882a593Smuzhiyun                    # The item should not be viewed as a choice item, so don't
2920*4882a593Smuzhiyun                    # set item.is_choice_sym
2921*4882a593Smuzhiyun                    stack.append(item)
2922*4882a593Smuzhiyun                    break
2923*4882a593Smuzhiyun                else:
2924*4882a593Smuzhiyun                    stack.pop()
2925*4882a593Smuzhiyun            else:
2926*4882a593Smuzhiyun                item.is_choice_sym = True
2927*4882a593Smuzhiyun                self.actual_symbols.append(item)
2928*4882a593Smuzhiyun                stack.append(item)
2929*4882a593Smuzhiyun
2930*4882a593Smuzhiyun    def _cache_ret(self, selection):
2931*4882a593Smuzhiyun        # As None is used to indicate the lack of a cached value we can't use
2932*4882a593Smuzhiyun        # that to cache the fact that the choice has no selection. Instead, we
2933*4882a593Smuzhiyun        # use the symbolic constant NO_SELECTION.
2934*4882a593Smuzhiyun        if selection is None:
2935*4882a593Smuzhiyun            self.cached_selection = NO_SELECTION
2936*4882a593Smuzhiyun        else:
2937*4882a593Smuzhiyun            self.cached_selection = selection
2938*4882a593Smuzhiyun
2939*4882a593Smuzhiyun        return selection
2940*4882a593Smuzhiyun
2941*4882a593Smuzhiyun    def _invalidate(self):
2942*4882a593Smuzhiyun        self.cached_selection = None
2943*4882a593Smuzhiyun        self.cached_visibility = None
2944*4882a593Smuzhiyun
2945*4882a593Smuzhiyun    def _unset_user_value(self):
2946*4882a593Smuzhiyun        self._invalidate()
2947*4882a593Smuzhiyun        self.user_val = None
2948*4882a593Smuzhiyun        self.user_mode = None
2949*4882a593Smuzhiyun
2950*4882a593Smuzhiyun    def _make_conf(self, append_fn):
2951*4882a593Smuzhiyun        _make_block_conf(self.block, append_fn)
2952*4882a593Smuzhiyun
2953*4882a593Smuzhiyunclass Comment(Item):
2954*4882a593Smuzhiyun
2955*4882a593Smuzhiyun    """Represents a comment statement."""
2956*4882a593Smuzhiyun
2957*4882a593Smuzhiyun    #
2958*4882a593Smuzhiyun    # Public interface
2959*4882a593Smuzhiyun    #
2960*4882a593Smuzhiyun
2961*4882a593Smuzhiyun    def get_config(self):
2962*4882a593Smuzhiyun        """Returns the Config instance this comment is from."""
2963*4882a593Smuzhiyun        return self.config
2964*4882a593Smuzhiyun
2965*4882a593Smuzhiyun    def get_text(self):
2966*4882a593Smuzhiyun        """Returns the text of the comment."""
2967*4882a593Smuzhiyun        return self.text
2968*4882a593Smuzhiyun
2969*4882a593Smuzhiyun    def get_parent(self):
2970*4882a593Smuzhiyun        """Returns the menu or choice statement that contains the comment, or
2971*4882a593Smuzhiyun        None if the comment is at the top level. Note that if statements are
2972*4882a593Smuzhiyun        treated as syntactic sugar and do not have an explicit class
2973*4882a593Smuzhiyun        representation."""
2974*4882a593Smuzhiyun        return self.parent
2975*4882a593Smuzhiyun
2976*4882a593Smuzhiyun    def get_location(self):
2977*4882a593Smuzhiyun        """Returns the location of the comment as a (filename, linenr) tuple,
2978*4882a593Smuzhiyun        where filename is a string and linenr an int."""
2979*4882a593Smuzhiyun        return (self.filename, self.linenr)
2980*4882a593Smuzhiyun
2981*4882a593Smuzhiyun    def get_visibility(self):
2982*4882a593Smuzhiyun        """Returns the visibility of the comment. See also
2983*4882a593Smuzhiyun        Symbol.get_visibility()."""
2984*4882a593Smuzhiyun        return self.config._eval_expr(self.dep_expr)
2985*4882a593Smuzhiyun
2986*4882a593Smuzhiyun    def get_referenced_symbols(self, refs_from_enclosing=False):
2987*4882a593Smuzhiyun        """See Symbol.get_referenced_symbols()."""
2988*4882a593Smuzhiyun        return self.all_referenced_syms if refs_from_enclosing else \
2989*4882a593Smuzhiyun               self.referenced_syms
2990*4882a593Smuzhiyun
2991*4882a593Smuzhiyun    def __str__(self):
2992*4882a593Smuzhiyun        """Returns a string containing various information about the
2993*4882a593Smuzhiyun        comment."""
2994*4882a593Smuzhiyun        dep_str = self.config._expr_val_str(self.orig_deps,
2995*4882a593Smuzhiyun                                            "(no dependencies)")
2996*4882a593Smuzhiyun
2997*4882a593Smuzhiyun        additional_deps_str = " " + \
2998*4882a593Smuzhiyun          self.config._expr_val_str(self.deps_from_containing,
2999*4882a593Smuzhiyun                                    "(no additional dependencies)")
3000*4882a593Smuzhiyun
3001*4882a593Smuzhiyun        return _lines("Comment",
3002*4882a593Smuzhiyun                      "Text: "         + str(self.text),
3003*4882a593Smuzhiyun                      "Dependencies: " + dep_str,
3004*4882a593Smuzhiyun                      "Additional dependencies from enclosing menus and "
3005*4882a593Smuzhiyun                        "ifs:",
3006*4882a593Smuzhiyun                      additional_deps_str,
3007*4882a593Smuzhiyun                      "Location: {0}:{1}".format(self.filename, self.linenr))
3008*4882a593Smuzhiyun
3009*4882a593Smuzhiyun    #
3010*4882a593Smuzhiyun    # Private methods
3011*4882a593Smuzhiyun    #
3012*4882a593Smuzhiyun
3013*4882a593Smuzhiyun    def __init__(self):
3014*4882a593Smuzhiyun        """Comment constructor -- not intended to be called directly by
3015*4882a593Smuzhiyun        Kconfiglib clients."""
3016*4882a593Smuzhiyun
3017*4882a593Smuzhiyun        self.text = None
3018*4882a593Smuzhiyun        self.dep_expr = None
3019*4882a593Smuzhiyun        self.config = None
3020*4882a593Smuzhiyun        self.parent = None
3021*4882a593Smuzhiyun
3022*4882a593Smuzhiyun        # Dependency expression without dependencies from enclosing menus and
3023*4882a593Smuzhiyun        # ifs propagated
3024*4882a593Smuzhiyun        self.orig_deps = None
3025*4882a593Smuzhiyun
3026*4882a593Smuzhiyun        # Dependencies inherited from containing menus and ifs
3027*4882a593Smuzhiyun        self.deps_from_containing = None
3028*4882a593Smuzhiyun        # The set of symbols referenced by this comment (see
3029*4882a593Smuzhiyun        # get_referenced_symbols())
3030*4882a593Smuzhiyun        self.referenced_syms = set()
3031*4882a593Smuzhiyun        # Like 'referenced_syms', but includes symbols from
3032*4882a593Smuzhiyun        # dependencies inherited from enclosing menus and ifs
3033*4882a593Smuzhiyun        self.all_referenced_syms = None
3034*4882a593Smuzhiyun
3035*4882a593Smuzhiyun        self.filename = None
3036*4882a593Smuzhiyun        self.linenr = None
3037*4882a593Smuzhiyun
3038*4882a593Smuzhiyun    def _make_conf(self, append_fn):
3039*4882a593Smuzhiyun        if self.config._eval_expr(self.dep_expr) != "n":
3040*4882a593Smuzhiyun            append_fn("\n#\n# {0}\n#".format(self.text))
3041*4882a593Smuzhiyun
3042*4882a593Smuzhiyunclass Kconfig_Syntax_Error(Exception):
3043*4882a593Smuzhiyun    """Exception raised for syntax errors."""
3044*4882a593Smuzhiyun    pass
3045*4882a593Smuzhiyun
3046*4882a593Smuzhiyunclass Internal_Error(Exception):
3047*4882a593Smuzhiyun    """Exception raised for internal errors."""
3048*4882a593Smuzhiyun    pass
3049*4882a593Smuzhiyun
3050*4882a593Smuzhiyun#
3051*4882a593Smuzhiyun# Public functions
3052*4882a593Smuzhiyun#
3053*4882a593Smuzhiyun
3054*4882a593Smuzhiyundef tri_less(v1, v2):
3055*4882a593Smuzhiyun    """Returns True if the tristate v1 is less than the tristate v2, where "n",
3056*4882a593Smuzhiyun    "m" and "y" are ordered from lowest to highest."""
3057*4882a593Smuzhiyun    return TRI_TO_INT[v1] < TRI_TO_INT[v2]
3058*4882a593Smuzhiyun
3059*4882a593Smuzhiyundef tri_less_eq(v1, v2):
3060*4882a593Smuzhiyun    """Returns True if the tristate v1 is less than or equal to the tristate
3061*4882a593Smuzhiyun    v2, where "n", "m" and "y" are ordered from lowest to highest."""
3062*4882a593Smuzhiyun    return TRI_TO_INT[v1] <= TRI_TO_INT[v2]
3063*4882a593Smuzhiyun
3064*4882a593Smuzhiyundef tri_greater(v1, v2):
3065*4882a593Smuzhiyun    """Returns True if the tristate v1 is greater than the tristate v2, where
3066*4882a593Smuzhiyun    "n", "m" and "y" are ordered from lowest to highest."""
3067*4882a593Smuzhiyun    return TRI_TO_INT[v1] > TRI_TO_INT[v2]
3068*4882a593Smuzhiyun
3069*4882a593Smuzhiyundef tri_greater_eq(v1, v2):
3070*4882a593Smuzhiyun    """Returns True if the tristate v1 is greater than or equal to the tristate
3071*4882a593Smuzhiyun    v2, where "n", "m" and "y" are ordered from lowest to highest."""
3072*4882a593Smuzhiyun    return TRI_TO_INT[v1] >= TRI_TO_INT[v2]
3073*4882a593Smuzhiyun
3074*4882a593Smuzhiyun#
3075*4882a593Smuzhiyun# Internal classes
3076*4882a593Smuzhiyun#
3077*4882a593Smuzhiyun
3078*4882a593Smuzhiyunclass _Feed(object):
3079*4882a593Smuzhiyun
3080*4882a593Smuzhiyun    """Class for working with sequences in a stream-like fashion; handy for
3081*4882a593Smuzhiyun    tokens."""
3082*4882a593Smuzhiyun
3083*4882a593Smuzhiyun    # This would be more helpful on the item classes, but would remove some
3084*4882a593Smuzhiyun    # flexibility
3085*4882a593Smuzhiyun    __slots__ = ['items', 'length', 'i']
3086*4882a593Smuzhiyun
3087*4882a593Smuzhiyun    def __init__(self, items):
3088*4882a593Smuzhiyun        self.items = items
3089*4882a593Smuzhiyun        self.length = len(self.items)
3090*4882a593Smuzhiyun        self.i = 0
3091*4882a593Smuzhiyun
3092*4882a593Smuzhiyun    def get_next(self):
3093*4882a593Smuzhiyun        if self.i >= self.length:
3094*4882a593Smuzhiyun            return None
3095*4882a593Smuzhiyun        item = self.items[self.i]
3096*4882a593Smuzhiyun        self.i += 1
3097*4882a593Smuzhiyun        return item
3098*4882a593Smuzhiyun
3099*4882a593Smuzhiyun    def peek_next(self):
3100*4882a593Smuzhiyun        return None if self.i >= self.length else self.items[self.i]
3101*4882a593Smuzhiyun
3102*4882a593Smuzhiyun    def check(self, token):
3103*4882a593Smuzhiyun        """Check if the next token is 'token'. If so, remove it from the token
3104*4882a593Smuzhiyun        feed and return True. Otherwise, leave it in and return False."""
3105*4882a593Smuzhiyun        if self.i < self.length and self.items[self.i] == token:
3106*4882a593Smuzhiyun            self.i += 1
3107*4882a593Smuzhiyun            return True
3108*4882a593Smuzhiyun        return False
3109*4882a593Smuzhiyun
3110*4882a593Smuzhiyun    def unget_all(self):
3111*4882a593Smuzhiyun        self.i = 0
3112*4882a593Smuzhiyun
3113*4882a593Smuzhiyunclass _FileFeed(object):
3114*4882a593Smuzhiyun
3115*4882a593Smuzhiyun    """Feeds lines from a file. Keeps track of the filename and current line
3116*4882a593Smuzhiyun    number. Joins any line ending in \\ with the following line. We need to be
3117*4882a593Smuzhiyun    careful to get the line number right in the presence of continuation
3118*4882a593Smuzhiyun    lines."""
3119*4882a593Smuzhiyun
3120*4882a593Smuzhiyun    __slots__ = ['filename', 'lines', 'length', 'linenr']
3121*4882a593Smuzhiyun
3122*4882a593Smuzhiyun    def __init__(self, filename):
3123*4882a593Smuzhiyun        self.filename = _clean_up_path(filename)
3124*4882a593Smuzhiyun        with open(filename, "r") as f:
3125*4882a593Smuzhiyun            # No interleaving of I/O and processing yet. Don't know if it would
3126*4882a593Smuzhiyun            # help.
3127*4882a593Smuzhiyun            self.lines = f.readlines()
3128*4882a593Smuzhiyun        self.length = len(self.lines)
3129*4882a593Smuzhiyun        self.linenr = 0
3130*4882a593Smuzhiyun
3131*4882a593Smuzhiyun    def get_next(self):
3132*4882a593Smuzhiyun        if self.linenr >= self.length:
3133*4882a593Smuzhiyun            return None
3134*4882a593Smuzhiyun        line = self.lines[self.linenr]
3135*4882a593Smuzhiyun        self.linenr += 1
3136*4882a593Smuzhiyun        while line.endswith("\\\n"):
3137*4882a593Smuzhiyun            line = line[:-2] + self.lines[self.linenr]
3138*4882a593Smuzhiyun            self.linenr += 1
3139*4882a593Smuzhiyun        return line
3140*4882a593Smuzhiyun
3141*4882a593Smuzhiyun    def peek_next(self):
3142*4882a593Smuzhiyun        linenr = self.linenr
3143*4882a593Smuzhiyun        if linenr >= self.length:
3144*4882a593Smuzhiyun            return None
3145*4882a593Smuzhiyun        line = self.lines[linenr]
3146*4882a593Smuzhiyun        while line.endswith("\\\n"):
3147*4882a593Smuzhiyun            linenr += 1
3148*4882a593Smuzhiyun            line = line[:-2] + self.lines[linenr]
3149*4882a593Smuzhiyun        return line
3150*4882a593Smuzhiyun
3151*4882a593Smuzhiyun    def unget(self):
3152*4882a593Smuzhiyun        self.linenr -= 1
3153*4882a593Smuzhiyun        while self.lines[self.linenr].endswith("\\\n"):
3154*4882a593Smuzhiyun            self.linenr -= 1
3155*4882a593Smuzhiyun
3156*4882a593Smuzhiyun    def next_nonblank(self):
3157*4882a593Smuzhiyun        """Removes lines up to and including the next non-blank (not all-space)
3158*4882a593Smuzhiyun        line and returns it. Returns None if there are no more non-blank
3159*4882a593Smuzhiyun        lines."""
3160*4882a593Smuzhiyun        while 1:
3161*4882a593Smuzhiyun            line = self.get_next()
3162*4882a593Smuzhiyun            if line is None or not line.isspace():
3163*4882a593Smuzhiyun                return line
3164*4882a593Smuzhiyun
3165*4882a593Smuzhiyun#
3166*4882a593Smuzhiyun# Internal functions
3167*4882a593Smuzhiyun#
3168*4882a593Smuzhiyun
3169*4882a593Smuzhiyundef _get_visibility(sc):
3170*4882a593Smuzhiyun    """Symbols and Choices have a "visibility" that acts as an upper bound on
3171*4882a593Smuzhiyun    the values a user can set for them, corresponding to the visibility in e.g.
3172*4882a593Smuzhiyun    'make menuconfig'. This function calculates the visibility for the Symbol
3173*4882a593Smuzhiyun    or Choice 'sc' -- the logic is nearly identical."""
3174*4882a593Smuzhiyun    if sc.cached_visibility is None:
3175*4882a593Smuzhiyun        vis = "n"
3176*4882a593Smuzhiyun        for _, cond_expr in sc.prompts:
3177*4882a593Smuzhiyun            vis = sc.config._eval_max(vis, cond_expr)
3178*4882a593Smuzhiyun
3179*4882a593Smuzhiyun        if isinstance(sc, Symbol) and sc.is_choice_sym:
3180*4882a593Smuzhiyun            vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
3181*4882a593Smuzhiyun
3182*4882a593Smuzhiyun        # Promote "m" to "y" if we're dealing with a non-tristate
3183*4882a593Smuzhiyun        if vis == "m" and sc.type != TRISTATE:
3184*4882a593Smuzhiyun            vis = "y"
3185*4882a593Smuzhiyun
3186*4882a593Smuzhiyun        sc.cached_visibility = vis
3187*4882a593Smuzhiyun
3188*4882a593Smuzhiyun    return sc.cached_visibility
3189*4882a593Smuzhiyun
3190*4882a593Smuzhiyundef _make_and(e1, e2):
3191*4882a593Smuzhiyun    """Constructs an AND (&&) expression. Performs trivial simplification.
3192*4882a593Smuzhiyun    Nones equate to 'y'.
3193*4882a593Smuzhiyun
3194*4882a593Smuzhiyun    Note: returns None if e1 == e2 == None."""
3195*4882a593Smuzhiyun    if e1 is None or e1 == "y":
3196*4882a593Smuzhiyun        return e2
3197*4882a593Smuzhiyun    if e2 is None or e2 == "y":
3198*4882a593Smuzhiyun        return e1
3199*4882a593Smuzhiyun
3200*4882a593Smuzhiyun    # Prefer to merge argument lists if possible to reduce the number of nodes
3201*4882a593Smuzhiyun
3202*4882a593Smuzhiyun    if isinstance(e1, tuple) and e1[0] == AND:
3203*4882a593Smuzhiyun        if isinstance(e2, tuple) and e2[0] == AND:
3204*4882a593Smuzhiyun            return (AND, e1[1] + e2[1])
3205*4882a593Smuzhiyun        return (AND, e1[1] + [e2])
3206*4882a593Smuzhiyun
3207*4882a593Smuzhiyun    if isinstance(e2, tuple) and e2[0] == AND:
3208*4882a593Smuzhiyun        return (AND, e2[1] + [e1])
3209*4882a593Smuzhiyun
3210*4882a593Smuzhiyun    return (AND, [e1, e2])
3211*4882a593Smuzhiyun
3212*4882a593Smuzhiyundef _make_or(e1, e2):
3213*4882a593Smuzhiyun    """Constructs an OR (||) expression. Performs trivial simplification and
3214*4882a593Smuzhiyun    avoids Nones. Nones equate to 'y', which is usually what we want, but needs
3215*4882a593Smuzhiyun    to be kept in mind."""
3216*4882a593Smuzhiyun
3217*4882a593Smuzhiyun    # Perform trivial simplification and avoid None's (which
3218*4882a593Smuzhiyun    # correspond to y's)
3219*4882a593Smuzhiyun    if e1 is None or e2 is None or e1 == "y" or e2 == "y":
3220*4882a593Smuzhiyun        return "y"
3221*4882a593Smuzhiyun    if e1 == "n":
3222*4882a593Smuzhiyun        return e2
3223*4882a593Smuzhiyun
3224*4882a593Smuzhiyun    # Prefer to merge argument lists if possible to reduce the number of nodes
3225*4882a593Smuzhiyun
3226*4882a593Smuzhiyun    if isinstance(e1, tuple) and e1[0] == OR:
3227*4882a593Smuzhiyun        if isinstance(e2, tuple) and e2[0] == OR:
3228*4882a593Smuzhiyun            return (OR, e1[1] + e2[1])
3229*4882a593Smuzhiyun        return (OR, e1[1] + [e2])
3230*4882a593Smuzhiyun
3231*4882a593Smuzhiyun    if isinstance(e2, tuple) and e2[0] == OR:
3232*4882a593Smuzhiyun        return (OR, e2[1] + [e1])
3233*4882a593Smuzhiyun
3234*4882a593Smuzhiyun    return (OR, [e1, e2])
3235*4882a593Smuzhiyun
3236*4882a593Smuzhiyundef _get_expr_syms_rec(expr, res):
3237*4882a593Smuzhiyun    """_get_expr_syms() helper. Recurses through expressions."""
3238*4882a593Smuzhiyun    if isinstance(expr, Symbol):
3239*4882a593Smuzhiyun        res.add(expr)
3240*4882a593Smuzhiyun    elif isinstance(expr, str):
3241*4882a593Smuzhiyun        return
3242*4882a593Smuzhiyun    elif expr[0] == AND or expr[0] == OR:
3243*4882a593Smuzhiyun        for term in expr[1]:
3244*4882a593Smuzhiyun            _get_expr_syms_rec(term, res)
3245*4882a593Smuzhiyun    elif expr[0] == NOT:
3246*4882a593Smuzhiyun        _get_expr_syms_rec(expr[1], res)
3247*4882a593Smuzhiyun    elif expr[0] == EQUAL or expr[0] == UNEQUAL:
3248*4882a593Smuzhiyun        if isinstance(expr[1], Symbol):
3249*4882a593Smuzhiyun            res.add(expr[1])
3250*4882a593Smuzhiyun        if isinstance(expr[2], Symbol):
3251*4882a593Smuzhiyun            res.add(expr[2])
3252*4882a593Smuzhiyun    else:
3253*4882a593Smuzhiyun        _internal_error("Internal error while fetching symbols from an "
3254*4882a593Smuzhiyun                        "expression with token stream {0}.".format(expr))
3255*4882a593Smuzhiyun
3256*4882a593Smuzhiyundef _get_expr_syms(expr):
3257*4882a593Smuzhiyun    """Returns the set() of symbols appearing in expr."""
3258*4882a593Smuzhiyun    res = set()
3259*4882a593Smuzhiyun    if expr is not None:
3260*4882a593Smuzhiyun        _get_expr_syms_rec(expr, res)
3261*4882a593Smuzhiyun    return res
3262*4882a593Smuzhiyun
3263*4882a593Smuzhiyundef _str_val(obj):
3264*4882a593Smuzhiyun    """Returns the value of obj as a string. If obj is not a string (constant
3265*4882a593Smuzhiyun    symbol), it must be a Symbol."""
3266*4882a593Smuzhiyun    return obj if isinstance(obj, str) else obj.get_value()
3267*4882a593Smuzhiyun
3268*4882a593Smuzhiyundef _make_block_conf(block, append_fn):
3269*4882a593Smuzhiyun    """Returns a list of .config strings for a block (list) of items."""
3270*4882a593Smuzhiyun
3271*4882a593Smuzhiyun    # Collect the substrings in a list and later use join() instead of += to
3272*4882a593Smuzhiyun    # build the final .config contents. With older Python versions, this yields
3273*4882a593Smuzhiyun    # linear instead of quadratic complexity.
3274*4882a593Smuzhiyun    for item in block:
3275*4882a593Smuzhiyun        item._make_conf(append_fn)
3276*4882a593Smuzhiyun
3277*4882a593Smuzhiyundef _sym_str_string(sym_or_str):
3278*4882a593Smuzhiyun    if isinstance(sym_or_str, str):
3279*4882a593Smuzhiyun        return '"' + sym_or_str + '"'
3280*4882a593Smuzhiyun    return sym_or_str.name
3281*4882a593Smuzhiyun
3282*4882a593Smuzhiyundef _intersperse(lst, op):
3283*4882a593Smuzhiyun    """_expr_to_str() helper. Gets the string representation of each expression
3284*4882a593Smuzhiyun    in lst and produces a list where op has been inserted between the
3285*4882a593Smuzhiyun    elements."""
3286*4882a593Smuzhiyun    if not lst:
3287*4882a593Smuzhiyun        return ""
3288*4882a593Smuzhiyun
3289*4882a593Smuzhiyun    res = []
3290*4882a593Smuzhiyun
3291*4882a593Smuzhiyun    def handle_sub_expr(expr):
3292*4882a593Smuzhiyun        no_parens = isinstance(expr, (str, Symbol)) or \
3293*4882a593Smuzhiyun                    expr[0] in (EQUAL, UNEQUAL) or \
3294*4882a593Smuzhiyun                    PRECEDENCE[op] <= PRECEDENCE[expr[0]]
3295*4882a593Smuzhiyun        if not no_parens:
3296*4882a593Smuzhiyun            res.append("(")
3297*4882a593Smuzhiyun        res.extend(_expr_to_str_rec(expr))
3298*4882a593Smuzhiyun        if not no_parens:
3299*4882a593Smuzhiyun            res.append(")")
3300*4882a593Smuzhiyun
3301*4882a593Smuzhiyun    op_str = OP_TO_STR[op]
3302*4882a593Smuzhiyun
3303*4882a593Smuzhiyun    handle_sub_expr(lst[0])
3304*4882a593Smuzhiyun    for expr in lst[1:]:
3305*4882a593Smuzhiyun        res.append(op_str)
3306*4882a593Smuzhiyun        handle_sub_expr(expr)
3307*4882a593Smuzhiyun
3308*4882a593Smuzhiyun    return res
3309*4882a593Smuzhiyun
3310*4882a593Smuzhiyundef _expr_to_str_rec(expr):
3311*4882a593Smuzhiyun    if expr is None:
3312*4882a593Smuzhiyun        return [""]
3313*4882a593Smuzhiyun
3314*4882a593Smuzhiyun    if isinstance(expr, (Symbol, str)):
3315*4882a593Smuzhiyun        return [_sym_str_string(expr)]
3316*4882a593Smuzhiyun
3317*4882a593Smuzhiyun    if expr[0] in (AND, OR):
3318*4882a593Smuzhiyun        return _intersperse(expr[1], expr[0])
3319*4882a593Smuzhiyun
3320*4882a593Smuzhiyun    if expr[0] == NOT:
3321*4882a593Smuzhiyun        need_parens = not isinstance(expr[1], (str, Symbol))
3322*4882a593Smuzhiyun
3323*4882a593Smuzhiyun        res = ["!"]
3324*4882a593Smuzhiyun        if need_parens:
3325*4882a593Smuzhiyun            res.append("(")
3326*4882a593Smuzhiyun        res.extend(_expr_to_str_rec(expr[1]))
3327*4882a593Smuzhiyun        if need_parens:
3328*4882a593Smuzhiyun            res.append(")")
3329*4882a593Smuzhiyun        return res
3330*4882a593Smuzhiyun
3331*4882a593Smuzhiyun    if expr[0] in (EQUAL, UNEQUAL):
3332*4882a593Smuzhiyun        return [_sym_str_string(expr[1]),
3333*4882a593Smuzhiyun                OP_TO_STR[expr[0]],
3334*4882a593Smuzhiyun                _sym_str_string(expr[2])]
3335*4882a593Smuzhiyun
3336*4882a593Smuzhiyundef _expr_to_str(expr):
3337*4882a593Smuzhiyun    return "".join(_expr_to_str_rec(expr))
3338*4882a593Smuzhiyun
3339*4882a593Smuzhiyundef _indentation(line):
3340*4882a593Smuzhiyun    """Returns the length of the line's leading whitespace, treating tab stops
3341*4882a593Smuzhiyun    as being spaced 8 characters apart."""
3342*4882a593Smuzhiyun    line = line.expandtabs()
3343*4882a593Smuzhiyun    return len(line) - len(line.lstrip())
3344*4882a593Smuzhiyun
3345*4882a593Smuzhiyundef _deindent(line, indent):
3346*4882a593Smuzhiyun    """Deindent 'line' by 'indent' spaces."""
3347*4882a593Smuzhiyun    line = line.expandtabs()
3348*4882a593Smuzhiyun    if len(line) <= indent:
3349*4882a593Smuzhiyun        return line
3350*4882a593Smuzhiyun    return line[indent:]
3351*4882a593Smuzhiyun
3352*4882a593Smuzhiyundef _is_base_n(s, n):
3353*4882a593Smuzhiyun    try:
3354*4882a593Smuzhiyun        int(s, n)
3355*4882a593Smuzhiyun        return True
3356*4882a593Smuzhiyun    except ValueError:
3357*4882a593Smuzhiyun        return False
3358*4882a593Smuzhiyun
3359*4882a593Smuzhiyundef _lines(*args):
3360*4882a593Smuzhiyun    """Returns a string consisting of all arguments, with newlines inserted
3361*4882a593Smuzhiyun    between them."""
3362*4882a593Smuzhiyun    return "\n".join(args)
3363*4882a593Smuzhiyun
3364*4882a593Smuzhiyundef _comment(s):
3365*4882a593Smuzhiyun    """Returns a new string with "#" inserted before each line in 's'."""
3366*4882a593Smuzhiyun    if not s:
3367*4882a593Smuzhiyun        return "#"
3368*4882a593Smuzhiyun    res = "".join(["#" + line for line in s.splitlines(True)])
3369*4882a593Smuzhiyun    if s.endswith("\n"):
3370*4882a593Smuzhiyun        return res + "#"
3371*4882a593Smuzhiyun    return res
3372*4882a593Smuzhiyun
3373*4882a593Smuzhiyundef _clean_up_path(path):
3374*4882a593Smuzhiyun    """Strips an initial "./" and any trailing slashes from 'path'."""
3375*4882a593Smuzhiyun    if path.startswith("./"):
3376*4882a593Smuzhiyun        path = path[2:]
3377*4882a593Smuzhiyun    return path.rstrip("/")
3378*4882a593Smuzhiyun
3379*4882a593Smuzhiyundef _build_msg(msg, filename, linenr):
3380*4882a593Smuzhiyun    if filename is not None:
3381*4882a593Smuzhiyun        msg = "{0}:{1}: ".format(_clean_up_path(filename), linenr) + msg
3382*4882a593Smuzhiyun    return msg
3383*4882a593Smuzhiyun
3384*4882a593Smuzhiyundef _stderr_msg(msg, filename, linenr):
3385*4882a593Smuzhiyun    sys.stderr.write(_build_msg(msg, filename, linenr) + "\n")
3386*4882a593Smuzhiyun
3387*4882a593Smuzhiyundef _tokenization_error(s, filename, linenr):
3388*4882a593Smuzhiyun    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
3389*4882a593Smuzhiyun    raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'"
3390*4882a593Smuzhiyun                               .format(loc, s.strip()))
3391*4882a593Smuzhiyun
3392*4882a593Smuzhiyundef _parse_error(s, msg, filename, linenr):
3393*4882a593Smuzhiyun    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
3394*4882a593Smuzhiyun    raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}"
3395*4882a593Smuzhiyun                               .format(loc, s.strip(),
3396*4882a593Smuzhiyun                                       "." if msg is None else ": " + msg))
3397*4882a593Smuzhiyun
3398*4882a593Smuzhiyundef _internal_error(msg):
3399*4882a593Smuzhiyun    raise Internal_Error(msg +
3400*4882a593Smuzhiyun      "\nSorry! You may want to send an email to ulfalizer a.t Google's "
3401*4882a593Smuzhiyun      "email service to tell me about this. Include the message above and the "
3402*4882a593Smuzhiyun      "stack trace and describe what you were doing.")
3403*4882a593Smuzhiyun
3404*4882a593Smuzhiyun#
3405*4882a593Smuzhiyun# Internal global constants
3406*4882a593Smuzhiyun#
3407*4882a593Smuzhiyun
3408*4882a593Smuzhiyun# Tokens
3409*4882a593Smuzhiyun(T_AND, T_OR, T_NOT,
3410*4882a593Smuzhiyun T_OPEN_PAREN, T_CLOSE_PAREN,
3411*4882a593Smuzhiyun T_EQUAL, T_UNEQUAL,
3412*4882a593Smuzhiyun T_MAINMENU, T_MENU, T_ENDMENU,
3413*4882a593Smuzhiyun T_SOURCE, T_CHOICE, T_ENDCHOICE,
3414*4882a593Smuzhiyun T_COMMENT, T_CONFIG, T_MENUCONFIG,
3415*4882a593Smuzhiyun T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON,
3416*4882a593Smuzhiyun T_OPTIONAL, T_PROMPT, T_DEFAULT,
3417*4882a593Smuzhiyun T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING,
3418*4882a593Smuzhiyun T_DEF_BOOL, T_DEF_TRISTATE,
3419*4882a593Smuzhiyun T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV,
3420*4882a593Smuzhiyun T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40)
3421*4882a593Smuzhiyun
3422*4882a593Smuzhiyun# The leading underscore before the function assignments below prevent pydoc
3423*4882a593Smuzhiyun# from listing them. The constants could be hidden too, but they're fairly
3424*4882a593Smuzhiyun# obviously internal anyway, so don't bother spamming the code.
3425*4882a593Smuzhiyun
3426*4882a593Smuzhiyun# Keyword to token map. Note that the get() method is assigned directly as a
3427*4882a593Smuzhiyun# small optimization.
3428*4882a593Smuzhiyun_get_keyword = \
3429*4882a593Smuzhiyun  {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU,
3430*4882a593Smuzhiyun   "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE,
3431*4882a593Smuzhiyun   "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT,
3432*4882a593Smuzhiyun   "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF,
3433*4882a593Smuzhiyun   "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL,
3434*4882a593Smuzhiyun   "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
3435*4882a593Smuzhiyun   "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
3436*4882a593Smuzhiyun   "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
3437*4882a593Smuzhiyun   "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION,
3438*4882a593Smuzhiyun   "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV,
3439*4882a593Smuzhiyun   "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
3440*4882a593Smuzhiyun   "visible": T_VISIBLE}.get
3441*4882a593Smuzhiyun
3442*4882a593Smuzhiyun# Strings to use for True and False
3443*4882a593SmuzhiyunBOOL_STR = {False: "false", True: "true"}
3444*4882a593Smuzhiyun
3445*4882a593Smuzhiyun# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE
3446*4882a593Smuzhiyun# is included to avoid symbols being registered for named choices.
3447*4882a593SmuzhiyunSTRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE,
3448*4882a593Smuzhiyun                        T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU))
3449*4882a593Smuzhiyun
3450*4882a593Smuzhiyun# Matches the initial token on a line; see _tokenize(). Also eats trailing
3451*4882a593Smuzhiyun# whitespace as an optimization.
3452*4882a593Smuzhiyun_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match
3453*4882a593Smuzhiyun
3454*4882a593Smuzhiyun# Matches an identifier/keyword optionally preceded by whitespace. Also eats
3455*4882a593Smuzhiyun# trailing whitespace as an optimization.
3456*4882a593Smuzhiyun_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
3457*4882a593Smuzhiyun
3458*4882a593Smuzhiyun# Regular expressions for parsing .config files
3459*4882a593Smuzhiyun_set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match
3460*4882a593Smuzhiyun_unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match
3461*4882a593Smuzhiyun
3462*4882a593Smuzhiyun# Regular expression for finding $-references to symbols in strings
3463*4882a593Smuzhiyun_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search
3464*4882a593Smuzhiyun
3465*4882a593Smuzhiyun# Integers representing symbol types
3466*4882a593SmuzhiyunUNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6)
3467*4882a593Smuzhiyun
3468*4882a593Smuzhiyun# Strings to use for types
3469*4882a593SmuzhiyunTYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate",
3470*4882a593Smuzhiyun            STRING: "string", HEX: "hex", INT: "int"}
3471*4882a593Smuzhiyun
3472*4882a593Smuzhiyun# Token to type mapping
3473*4882a593SmuzhiyunTOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING,
3474*4882a593Smuzhiyun                 T_INT: INT, T_HEX: HEX}
3475*4882a593Smuzhiyun
3476*4882a593Smuzhiyun# Default values for symbols of different types (the value the symbol gets if
3477*4882a593Smuzhiyun# it is not assigned a user value and none of its 'default' clauses kick in)
3478*4882a593SmuzhiyunDEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""}
3479*4882a593Smuzhiyun
3480*4882a593Smuzhiyun# Indicates that no item is selected in a choice statement
3481*4882a593SmuzhiyunNO_SELECTION = 0
3482*4882a593Smuzhiyun
3483*4882a593Smuzhiyun# Integers representing expression types
3484*4882a593SmuzhiyunAND, OR, NOT, EQUAL, UNEQUAL = range(5)
3485*4882a593Smuzhiyun
3486*4882a593Smuzhiyun# Map from tristate values to integers
3487*4882a593SmuzhiyunTRI_TO_INT = {"n": 0, "m": 1, "y": 2}
3488*4882a593Smuzhiyun
3489*4882a593Smuzhiyun# Printing-related stuff
3490*4882a593Smuzhiyun
3491*4882a593SmuzhiyunOP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "}
3492*4882a593SmuzhiyunPRECEDENCE = {OR: 0, AND: 1, NOT: 2}
3493