1f219e013SMasahiro Yamada# 29170818aSMasahiro Yamada# SPDX-License-Identifier: ISC 3f219e013SMasahiro Yamada# 4f219e013SMasahiro Yamada# Author: Ulf Magnusson 5f219e013SMasahiro Yamada# https://github.com/ulfalizer/Kconfiglib 6f219e013SMasahiro Yamada 7f219e013SMasahiro Yamada# This is Kconfiglib, a Python library for scripting, debugging, and extracting 8f219e013SMasahiro Yamada# information from Kconfig-based configuration systems. To view the 9f219e013SMasahiro Yamada# documentation, run 10f219e013SMasahiro Yamada# 11f219e013SMasahiro Yamada# $ pydoc kconfiglib 12f219e013SMasahiro Yamada# 13f219e013SMasahiro Yamada# or, if you prefer HTML, 14f219e013SMasahiro Yamada# 15f219e013SMasahiro Yamada# $ pydoc -w kconfiglib 16f219e013SMasahiro Yamada# 17f219e013SMasahiro Yamada# The examples/ subdirectory contains examples, to be run with e.g. 18f219e013SMasahiro Yamada# 19f219e013SMasahiro Yamada# $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py 20f219e013SMasahiro Yamada# 21f219e013SMasahiro Yamada# Look in testsuite.py for the test suite. 22f219e013SMasahiro Yamada 23f219e013SMasahiro Yamada""" 24f219e013SMasahiro YamadaKconfiglib is a Python library for scripting and extracting information from 25f219e013SMasahiro YamadaKconfig-based configuration systems. Features include the following: 26f219e013SMasahiro Yamada 27f219e013SMasahiro Yamada - Symbol values and properties can be looked up and values assigned 28f219e013SMasahiro Yamada programmatically. 29f219e013SMasahiro Yamada - .config files can be read and written. 30f219e013SMasahiro Yamada - Expressions can be evaluated in the context of a Kconfig configuration. 31f219e013SMasahiro Yamada - Relations between symbols can be quickly determined, such as finding all 32f219e013SMasahiro Yamada symbols that reference a particular symbol. 33f219e013SMasahiro Yamada - Highly compatible with the scripts/kconfig/*conf utilities. The test suite 34f219e013SMasahiro Yamada automatically compares outputs between Kconfiglib and the C implementation 35f219e013SMasahiro Yamada for a large number of cases. 36f219e013SMasahiro Yamada 37f219e013SMasahiro YamadaFor the Linux kernel, scripts are run using 38f219e013SMasahiro Yamada 3990c36d8aSUlf Magnusson $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>] 40f219e013SMasahiro Yamada 4190c36d8aSUlf MagnussonUsing the 'scriptconfig' target ensures that required environment variables 4290c36d8aSUlf Magnusson(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. 43f219e013SMasahiro Yamada 4490c36d8aSUlf MagnussonScripts receive the name of the Kconfig file to load in sys.argv[1]. As of 4590c36d8aSUlf MagnussonLinux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory. 4690c36d8aSUlf MagnussonIf an argument is provided with SCRIPT_ARG, it appears as sys.argv[2]. 47f219e013SMasahiro Yamada 48f219e013SMasahiro YamadaTo get an interactive Python prompt with Kconfiglib preloaded and a Config 4990c36d8aSUlf Magnussonobject 'c' created, run 50f219e013SMasahiro Yamada 5190c36d8aSUlf Magnusson $ make iscriptconfig [ARCH=<arch>] 52f219e013SMasahiro Yamada 5390c36d8aSUlf MagnussonKconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python 5490c36d8aSUlf Magnussoninterpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy 5590c36d8aSUlf Magnussonworks well too, and might give a nice speedup for long-running jobs. 56f219e013SMasahiro Yamada 5790c36d8aSUlf MagnussonThe examples/ directory contains short example scripts, which can be run with 5890c36d8aSUlf Magnussone.g. 59f219e013SMasahiro Yamada 60f219e013SMasahiro Yamada $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py 61f219e013SMasahiro Yamada 62f219e013SMasahiro Yamadaor 63f219e013SMasahiro Yamada 6490c36d8aSUlf Magnusson $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel 65f219e013SMasahiro Yamada 6690c36d8aSUlf Magnussontestsuite.py contains the test suite. See the top of the script for how to run 6790c36d8aSUlf Magnussonit. 68f219e013SMasahiro Yamada 69f219e013SMasahiro YamadaCredits: Written by Ulf "Ulfalizer" Magnusson 70f219e013SMasahiro Yamada 7190c36d8aSUlf MagnussonSend bug reports, suggestions and other feedback to ulfalizer a.t Google's 7290c36d8aSUlf Magnussonemail service. Don't wrestle with internal APIs. Tell me what you need and I 7390c36d8aSUlf Magnussonmight add it in a safe way as a client API instead.""" 74f219e013SMasahiro Yamada 75f219e013SMasahiro Yamadaimport os 76f219e013SMasahiro Yamadaimport re 77f219e013SMasahiro Yamadaimport sys 78f219e013SMasahiro Yamada 7990c36d8aSUlf Magnusson# File layout: 8090c36d8aSUlf Magnusson# 8190c36d8aSUlf Magnusson# Public classes 8290c36d8aSUlf Magnusson# Public functions 8390c36d8aSUlf Magnusson# Internal classes 8490c36d8aSUlf Magnusson# Internal functions 8590c36d8aSUlf Magnusson# Internal global constants 8690c36d8aSUlf Magnusson 8790c36d8aSUlf Magnusson# Line length: 79 columns 8890c36d8aSUlf Magnusson 8990c36d8aSUlf Magnusson# 9090c36d8aSUlf Magnusson# Public classes 9190c36d8aSUlf Magnusson# 9290c36d8aSUlf Magnusson 9390c36d8aSUlf Magnussonclass Config(object): 94f219e013SMasahiro Yamada 95f219e013SMasahiro Yamada """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the 96f219e013SMasahiro Yamada set of symbols and other items appearing in the configuration together with 97f219e013SMasahiro Yamada their values. Creating any number of Config objects -- including for 98f219e013SMasahiro Yamada different architectures -- is safe; Kconfiglib has no global state.""" 99f219e013SMasahiro Yamada 100f219e013SMasahiro Yamada # 101f219e013SMasahiro Yamada # Public interface 102f219e013SMasahiro Yamada # 103f219e013SMasahiro Yamada 10490c36d8aSUlf Magnusson def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True, 105f219e013SMasahiro Yamada print_undef_assign=False): 106f219e013SMasahiro Yamada """Creates a new Config object, representing a Kconfig configuration. 107f219e013SMasahiro Yamada Raises Kconfig_Syntax_Error on syntax errors. 108f219e013SMasahiro Yamada 10990c36d8aSUlf Magnusson filename (default: "Kconfig"): The base Kconfig file of the 11090c36d8aSUlf Magnusson configuration. For the Linux kernel, you'll probably want "Kconfig" 11190c36d8aSUlf Magnusson from the top-level directory, as environment variables will make 11290c36d8aSUlf Magnusson sure the right Kconfig is included from there 11390c36d8aSUlf Magnusson (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make 11490c36d8aSUlf Magnusson scriptconfig', the filename of the base base Kconfig file will be in 11590c36d8aSUlf Magnusson sys.argv[1]. 116f219e013SMasahiro Yamada 11790c36d8aSUlf Magnusson base_dir (default: None): The base directory relative to which 'source' 11890c36d8aSUlf Magnusson statements within Kconfig files will work. For the Linux kernel this 11990c36d8aSUlf Magnusson should be the top-level directory of the kernel tree. $-references 12090c36d8aSUlf Magnusson to existing environment variables will be expanded. 121f219e013SMasahiro Yamada 12290c36d8aSUlf Magnusson If None (the default), the environment variable 'srctree' will be 12390c36d8aSUlf Magnusson used if set, and the current directory otherwise. 'srctree' is set 12490c36d8aSUlf Magnusson by the Linux makefiles to the top-level kernel directory. A default 12590c36d8aSUlf Magnusson of "." would not work with an alternative build directory. 126f219e013SMasahiro Yamada 12790c36d8aSUlf Magnusson print_warnings (default: True): Set to True if warnings related to this 12890c36d8aSUlf Magnusson configuration should be printed to stderr. This can be changed later 12990c36d8aSUlf Magnusson with Config.set_print_warnings(). It is provided as a constructor 13090c36d8aSUlf Magnusson argument since warnings might be generated during parsing. 131f219e013SMasahiro Yamada 13290c36d8aSUlf Magnusson print_undef_assign (default: False): Set to True if informational 13390c36d8aSUlf Magnusson messages related to assignments to undefined symbols should be 13490c36d8aSUlf Magnusson printed to stderr for this configuration. Can be changed later with 135f219e013SMasahiro Yamada Config.set_print_undef_assign().""" 136f219e013SMasahiro Yamada 137f219e013SMasahiro Yamada # The set of all symbols, indexed by name (a string) 138f219e013SMasahiro Yamada self.syms = {} 13990c36d8aSUlf Magnusson # Python 2/3 compatibility hack. This is the only one needed. 14090c36d8aSUlf Magnusson if sys.version_info[0] >= 3: 14190c36d8aSUlf Magnusson self.syms_iter = self.syms.values 14290c36d8aSUlf Magnusson else: 14390c36d8aSUlf Magnusson self.syms_iter = self.syms.itervalues 144f219e013SMasahiro Yamada 145f219e013SMasahiro Yamada # The set of all defined symbols in the configuration in the order they 146f219e013SMasahiro Yamada # appear in the Kconfig files. This excludes the special symbols n, m, 147f219e013SMasahiro Yamada # and y as well as symbols that are referenced but never defined. 148f219e013SMasahiro Yamada self.kconfig_syms = [] 149f219e013SMasahiro Yamada 150f219e013SMasahiro Yamada # The set of all named choices (yes, choices can have names), indexed 151f219e013SMasahiro Yamada # by name (a string) 152f219e013SMasahiro Yamada self.named_choices = {} 153f219e013SMasahiro Yamada 15490c36d8aSUlf Magnusson # Lists containing all choices, menus and comments in the configuration 15590c36d8aSUlf Magnusson self.choices = [] 15690c36d8aSUlf Magnusson self.menus = [] 15790c36d8aSUlf Magnusson self.comments = [] 15890c36d8aSUlf Magnusson 15990c36d8aSUlf Magnusson def register_special_symbol(type_, name, val): 160f219e013SMasahiro Yamada sym = Symbol() 161f219e013SMasahiro Yamada sym.is_special_ = True 162f219e013SMasahiro Yamada sym.is_defined_ = True 163f219e013SMasahiro Yamada sym.config = self 164f219e013SMasahiro Yamada sym.name = name 16590c36d8aSUlf Magnusson sym.type = type_ 16690c36d8aSUlf Magnusson sym.cached_val = val 167f219e013SMasahiro Yamada self.syms[name] = sym 168f219e013SMasahiro Yamada return sym 169f219e013SMasahiro Yamada 170f219e013SMasahiro Yamada # The special symbols n, m and y, used as shorthand for "n", "m" and 171f219e013SMasahiro Yamada # "y" 172f219e013SMasahiro Yamada self.n = register_special_symbol(TRISTATE, "n", "n") 173f219e013SMasahiro Yamada self.m = register_special_symbol(TRISTATE, "m", "m") 174f219e013SMasahiro Yamada self.y = register_special_symbol(TRISTATE, "y", "y") 175f219e013SMasahiro Yamada # DEFCONFIG_LIST uses this 176f219e013SMasahiro Yamada register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2]) 177f219e013SMasahiro Yamada 178f219e013SMasahiro Yamada # The symbol with "option defconfig_list" set, containing a list of 179f219e013SMasahiro Yamada # default .config files 180f219e013SMasahiro Yamada self.defconfig_sym = None 181f219e013SMasahiro Yamada 182f219e013SMasahiro Yamada # See Symbol.get_(src)arch() 183f219e013SMasahiro Yamada self.arch = os.environ.get("ARCH") 184f219e013SMasahiro Yamada self.srcarch = os.environ.get("SRCARCH") 185f219e013SMasahiro Yamada 186f219e013SMasahiro Yamada # See Config.__init__(). We need this for get_defconfig_filename(). 187f219e013SMasahiro Yamada self.srctree = os.environ.get("srctree") 188f219e013SMasahiro Yamada if self.srctree is None: 189f219e013SMasahiro Yamada self.srctree = "." 190f219e013SMasahiro Yamada 191f219e013SMasahiro Yamada self.filename = filename 19290c36d8aSUlf Magnusson if base_dir is None: 19390c36d8aSUlf Magnusson self.base_dir = self.srctree 19490c36d8aSUlf Magnusson else: 19590c36d8aSUlf Magnusson self.base_dir = os.path.expandvars(base_dir) 196f219e013SMasahiro Yamada 197f219e013SMasahiro Yamada # The 'mainmenu' text 198f219e013SMasahiro Yamada self.mainmenu_text = None 199f219e013SMasahiro Yamada 200f219e013SMasahiro Yamada # The filename of the most recently loaded .config file 201f219e013SMasahiro Yamada self.config_filename = None 202f219e013SMasahiro Yamada # The textual header of the most recently loaded .config, uncommented 203f219e013SMasahiro Yamada self.config_header = None 204f219e013SMasahiro Yamada 205f219e013SMasahiro Yamada self.print_warnings = print_warnings 206f219e013SMasahiro Yamada self.print_undef_assign = print_undef_assign 207f219e013SMasahiro Yamada 208f219e013SMasahiro Yamada # For parsing routines that stop when finding a line belonging to a 209f219e013SMasahiro Yamada # different construct, these holds that line and the tokenized version 210f219e013SMasahiro Yamada # of that line. The purpose is to avoid having to re-tokenize the line, 211f219e013SMasahiro Yamada # which is inefficient and causes problems when recording references to 212f219e013SMasahiro Yamada # symbols. 213f219e013SMasahiro Yamada self.end_line = None 214f219e013SMasahiro Yamada self.end_line_tokens = None 215f219e013SMasahiro Yamada 216f219e013SMasahiro Yamada # See the comment in _parse_expr(). 21790c36d8aSUlf Magnusson self._cur_item = None 21890c36d8aSUlf Magnusson self._line = None 21990c36d8aSUlf Magnusson self._filename = None 22090c36d8aSUlf Magnusson self._linenr = None 22190c36d8aSUlf Magnusson self._transform_m = None 222f219e013SMasahiro Yamada 223f219e013SMasahiro Yamada # Parse the Kconfig files 224f219e013SMasahiro Yamada self.top_block = self._parse_file(filename, None, None, None) 225f219e013SMasahiro Yamada 226f219e013SMasahiro Yamada # Build Symbol.dep for all symbols 227f219e013SMasahiro Yamada self._build_dep() 228f219e013SMasahiro Yamada 229f219e013SMasahiro Yamada def get_arch(self): 230f219e013SMasahiro Yamada """Returns the value the environment variable ARCH had at the time the 231f219e013SMasahiro Yamada Config instance was created, or None if ARCH was not set. For the 232f219e013SMasahiro Yamada kernel, this corresponds to the architecture being built for, with 233f219e013SMasahiro Yamada values such as "i386" or "mips".""" 234f219e013SMasahiro Yamada return self.arch 235f219e013SMasahiro Yamada 236f219e013SMasahiro Yamada def get_srcarch(self): 237f219e013SMasahiro Yamada """Returns the value the environment variable SRCARCH had at the time 238f219e013SMasahiro Yamada the Config instance was created, or None if SRCARCH was not set. For 23990c36d8aSUlf Magnusson the kernel, this corresponds to the particular arch/ subdirectory 24090c36d8aSUlf Magnusson containing architecture-specific code.""" 241f219e013SMasahiro Yamada return self.srcarch 242f219e013SMasahiro Yamada 243f219e013SMasahiro Yamada def get_srctree(self): 244f219e013SMasahiro Yamada """Returns the value the environment variable srctree had at the time 245f219e013SMasahiro Yamada the Config instance was created, or None if srctree was not defined. 246f219e013SMasahiro Yamada This variable points to the source directory and is used when building 247f219e013SMasahiro Yamada in a separate directory.""" 248f219e013SMasahiro Yamada return self.srctree 249f219e013SMasahiro Yamada 25090c36d8aSUlf Magnusson def get_base_dir(self): 25190c36d8aSUlf Magnusson """Returns the base directory relative to which 'source' statements 25290c36d8aSUlf Magnusson will work, passed as an argument to Config.__init__().""" 25390c36d8aSUlf Magnusson return self.base_dir 25490c36d8aSUlf Magnusson 25590c36d8aSUlf Magnusson def get_kconfig_filename(self): 25690c36d8aSUlf Magnusson """Returns the name of the (base) kconfig file this configuration was 25790c36d8aSUlf Magnusson loaded from.""" 25890c36d8aSUlf Magnusson return self.filename 25990c36d8aSUlf Magnusson 260f219e013SMasahiro Yamada def get_config_filename(self): 26190c36d8aSUlf Magnusson """Returns the filename of the most recently loaded configuration file, 26290c36d8aSUlf Magnusson or None if no configuration has been loaded.""" 263f219e013SMasahiro Yamada return self.config_filename 264f219e013SMasahiro Yamada 26590c36d8aSUlf Magnusson def get_config_header(self): 26690c36d8aSUlf Magnusson """Returns the (uncommented) textual header of the .config file most 26790c36d8aSUlf Magnusson recently loaded with load_config(). Returns None if no .config file has 26890c36d8aSUlf Magnusson been loaded or if the most recently loaded .config file has no header. 26990c36d8aSUlf Magnusson The header consists of all lines up to but not including the first line 27090c36d8aSUlf Magnusson that either 27190c36d8aSUlf Magnusson 27290c36d8aSUlf Magnusson 1. Does not start with "#" 27390c36d8aSUlf Magnusson 2. Has the form "# CONFIG_FOO is not set." 27490c36d8aSUlf Magnusson """ 27590c36d8aSUlf Magnusson return self.config_header 27690c36d8aSUlf Magnusson 277f219e013SMasahiro Yamada def get_mainmenu_text(self): 278f219e013SMasahiro Yamada """Returns the text of the 'mainmenu' statement (with $-references to 279f219e013SMasahiro Yamada symbols replaced by symbol values), or None if the configuration has no 280f219e013SMasahiro Yamada 'mainmenu' statement.""" 281f219e013SMasahiro Yamada return None if self.mainmenu_text is None else \ 282f219e013SMasahiro Yamada self._expand_sym_refs(self.mainmenu_text) 283f219e013SMasahiro Yamada 284f219e013SMasahiro Yamada def get_defconfig_filename(self): 285f219e013SMasahiro Yamada """Returns the name of the defconfig file, which is the first existing 286f219e013SMasahiro Yamada file in the list given in a symbol having 'option defconfig_list' set. 287f219e013SMasahiro Yamada $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if 288f219e013SMasahiro Yamada FOO has the value "foo"). Returns None in case of no defconfig file. 289f219e013SMasahiro Yamada Setting 'option defconfig_list' on multiple symbols currently results 290f219e013SMasahiro Yamada in undefined behavior. 291f219e013SMasahiro Yamada 292f219e013SMasahiro Yamada If the environment variable 'srctree' was set when the Config was 293f219e013SMasahiro Yamada created, get_defconfig_filename() will first look relative to that 294f219e013SMasahiro Yamada directory before looking in the current directory; see 2959d01b787SMasahiro Yamada Config.__init__(). 2969d01b787SMasahiro Yamada 29790c36d8aSUlf Magnusson WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses 29890c36d8aSUlf Magnusson the --defconfig=<defconfig> option when calling the C implementation of 29990c36d8aSUlf Magnusson e.g. 'make defconfig'. This option overrides the 'option 30090c36d8aSUlf Magnusson defconfig_list' symbol, meaning the result from 30190c36d8aSUlf Magnusson get_defconfig_filename() might not match what 'make defconfig' would 30290c36d8aSUlf Magnusson use. That probably ought to be worked around somehow, so that this 30390c36d8aSUlf Magnusson function always gives the "expected" result.""" 304f219e013SMasahiro Yamada if self.defconfig_sym is None: 305f219e013SMasahiro Yamada return None 30690c36d8aSUlf Magnusson for filename, cond_expr in self.defconfig_sym.def_exprs: 307f219e013SMasahiro Yamada if self._eval_expr(cond_expr) == "y": 308f219e013SMasahiro Yamada filename = self._expand_sym_refs(filename) 309f219e013SMasahiro Yamada # We first look in $srctree. os.path.join() won't work here as 310f219e013SMasahiro Yamada # an absolute path in filename would override $srctree. 31190c36d8aSUlf Magnusson srctree_filename = os.path.normpath(self.srctree + "/" + 31290c36d8aSUlf Magnusson filename) 313f219e013SMasahiro Yamada if os.path.exists(srctree_filename): 314f219e013SMasahiro Yamada return srctree_filename 315f219e013SMasahiro Yamada if os.path.exists(filename): 316f219e013SMasahiro Yamada return filename 317f219e013SMasahiro Yamada return None 318f219e013SMasahiro Yamada 319f219e013SMasahiro Yamada def get_symbol(self, name): 320f219e013SMasahiro Yamada """Returns the symbol with name 'name', or None if no such symbol 321f219e013SMasahiro Yamada appears in the configuration. An alternative shorthand is conf[name], 322f219e013SMasahiro Yamada where conf is a Config instance, though that will instead raise 323f219e013SMasahiro Yamada KeyError if the symbol does not exist.""" 324f219e013SMasahiro Yamada return self.syms.get(name) 325f219e013SMasahiro Yamada 32690c36d8aSUlf Magnusson def __getitem__(self, name): 32790c36d8aSUlf Magnusson """Returns the symbol with name 'name'. Raises KeyError if the symbol 32890c36d8aSUlf Magnusson does not appear in the configuration.""" 32990c36d8aSUlf Magnusson return self.syms[name] 330f219e013SMasahiro Yamada 331f219e013SMasahiro Yamada def get_symbols(self, all_symbols=True): 332f219e013SMasahiro Yamada """Returns a list of symbols from the configuration. An alternative for 333f219e013SMasahiro Yamada iterating over all defined symbols (in the order of definition) is 334f219e013SMasahiro Yamada 335f219e013SMasahiro Yamada for sym in config: 336f219e013SMasahiro Yamada ... 337f219e013SMasahiro Yamada 338f219e013SMasahiro Yamada which relies on Config implementing __iter__() and is equivalent to 339f219e013SMasahiro Yamada 340f219e013SMasahiro Yamada for sym in config.get_symbols(False): 341f219e013SMasahiro Yamada ... 342f219e013SMasahiro Yamada 34390c36d8aSUlf Magnusson all_symbols (default: True): If True, all symbols -- including special 34490c36d8aSUlf Magnusson and undefined symbols -- will be included in the result, in an 34590c36d8aSUlf Magnusson undefined order. If False, only symbols actually defined and not 34690c36d8aSUlf Magnusson merely referred to in the configuration will be included in the 34790c36d8aSUlf Magnusson result, and will appear in the order that they are defined within 34890c36d8aSUlf Magnusson the Kconfig configuration files.""" 34990c36d8aSUlf Magnusson return list(self.syms.values()) if all_symbols else self.kconfig_syms 35090c36d8aSUlf Magnusson 35190c36d8aSUlf Magnusson def __iter__(self): 35290c36d8aSUlf Magnusson """Convenience function for iterating over the set of all defined 35390c36d8aSUlf Magnusson symbols in the configuration, used like 35490c36d8aSUlf Magnusson 35590c36d8aSUlf Magnusson for sym in conf: 35690c36d8aSUlf Magnusson ... 35790c36d8aSUlf Magnusson 35890c36d8aSUlf Magnusson The iteration happens in the order of definition within the Kconfig 35990c36d8aSUlf Magnusson configuration files. Symbols only referred to but not defined will not 36090c36d8aSUlf Magnusson be included, nor will the special symbols n, m, and y. If you want to 36190c36d8aSUlf Magnusson include such symbols as well, see config.get_symbols().""" 36290c36d8aSUlf Magnusson return iter(self.kconfig_syms) 363f219e013SMasahiro Yamada 364f219e013SMasahiro Yamada def get_choices(self): 365f219e013SMasahiro Yamada """Returns a list containing all choice statements in the 366f219e013SMasahiro Yamada configuration, in the order they appear in the Kconfig files.""" 367f219e013SMasahiro Yamada return self.choices 368f219e013SMasahiro Yamada 369f219e013SMasahiro Yamada def get_menus(self): 370f219e013SMasahiro Yamada """Returns a list containing all menus in the configuration, in the 371f219e013SMasahiro Yamada order they appear in the Kconfig files.""" 372f219e013SMasahiro Yamada return self.menus 373f219e013SMasahiro Yamada 374f219e013SMasahiro Yamada def get_comments(self): 375f219e013SMasahiro Yamada """Returns a list containing all comments in the configuration, in the 376f219e013SMasahiro Yamada order they appear in the Kconfig files.""" 377f219e013SMasahiro Yamada return self.comments 378f219e013SMasahiro Yamada 37990c36d8aSUlf Magnusson def get_top_level_items(self): 38090c36d8aSUlf Magnusson """Returns a list containing the items (symbols, menus, choices, and 38190c36d8aSUlf Magnusson comments) at the top level of the configuration -- that is, all items 38290c36d8aSUlf Magnusson that do not appear within a menu or choice. The items appear in the 38390c36d8aSUlf Magnusson same order as within the configuration.""" 38490c36d8aSUlf Magnusson return self.top_block 38590c36d8aSUlf Magnusson 38690c36d8aSUlf Magnusson def load_config(self, filename, replace=True): 38790c36d8aSUlf Magnusson """Loads symbol values from a file in the familiar .config format. 38890c36d8aSUlf Magnusson Equivalent to calling Symbol.set_user_value() to set each of the 38990c36d8aSUlf Magnusson values. 39090c36d8aSUlf Magnusson 39190c36d8aSUlf Magnusson "# CONFIG_FOO is not set" within a .config file is treated specially 39290c36d8aSUlf Magnusson and sets the user value of FOO to 'n'. The C implementation works the 39390c36d8aSUlf Magnusson same way. 39490c36d8aSUlf Magnusson 39590c36d8aSUlf Magnusson filename: The .config file to load. $-references to existing 39690c36d8aSUlf Magnusson environment variables will be expanded. For scripts to work even when 39790c36d8aSUlf Magnusson an alternative build directory is used with the Linux kernel, you 39890c36d8aSUlf Magnusson need to refer to the top-level kernel directory with "$srctree". 39990c36d8aSUlf Magnusson 40090c36d8aSUlf Magnusson replace (default: True): True if the configuration should replace the 40190c36d8aSUlf Magnusson old configuration; False if it should add to it.""" 40290c36d8aSUlf Magnusson 40390c36d8aSUlf Magnusson # Put this first so that a missing file doesn't screw up our state 40490c36d8aSUlf Magnusson filename = os.path.expandvars(filename) 40590c36d8aSUlf Magnusson line_feeder = _FileFeed(filename) 40690c36d8aSUlf Magnusson 40790c36d8aSUlf Magnusson self.config_filename = filename 40890c36d8aSUlf Magnusson 40990c36d8aSUlf Magnusson # 41090c36d8aSUlf Magnusson # Read header 41190c36d8aSUlf Magnusson # 41290c36d8aSUlf Magnusson 41390c36d8aSUlf Magnusson def is_header_line(line): 41490c36d8aSUlf Magnusson return line is not None and line.startswith("#") and \ 41590c36d8aSUlf Magnusson not _unset_re_match(line) 41690c36d8aSUlf Magnusson 41790c36d8aSUlf Magnusson self.config_header = None 41890c36d8aSUlf Magnusson 41990c36d8aSUlf Magnusson line = line_feeder.peek_next() 42090c36d8aSUlf Magnusson if is_header_line(line): 42190c36d8aSUlf Magnusson self.config_header = "" 42290c36d8aSUlf Magnusson while is_header_line(line_feeder.peek_next()): 42390c36d8aSUlf Magnusson self.config_header += line_feeder.get_next()[1:] 42490c36d8aSUlf Magnusson # Remove trailing newline 42590c36d8aSUlf Magnusson if self.config_header.endswith("\n"): 42690c36d8aSUlf Magnusson self.config_header = self.config_header[:-1] 42790c36d8aSUlf Magnusson 42890c36d8aSUlf Magnusson # 42990c36d8aSUlf Magnusson # Read assignments. Hotspot for some workloads. 43090c36d8aSUlf Magnusson # 43190c36d8aSUlf Magnusson 43290c36d8aSUlf Magnusson def warn_override(filename, linenr, name, old_user_val, new_user_val): 43390c36d8aSUlf Magnusson self._warn('overriding the value of {0}. ' 43490c36d8aSUlf Magnusson 'Old value: "{1}", new value: "{2}".' 43590c36d8aSUlf Magnusson .format(name, old_user_val, new_user_val), 43690c36d8aSUlf Magnusson filename, linenr) 43790c36d8aSUlf Magnusson 43890c36d8aSUlf Magnusson # Invalidate everything to keep things simple. It might be possible to 43990c36d8aSUlf Magnusson # improve performance for the case where multiple configurations are 44090c36d8aSUlf Magnusson # loaded by only invalidating a symbol (and its dependent symbols) if 44190c36d8aSUlf Magnusson # the new user value differs from the old. One complication would be 44290c36d8aSUlf Magnusson # that symbols not mentioned in the .config must lose their user value 44390c36d8aSUlf Magnusson # when replace = True, which is the usual case. 44490c36d8aSUlf Magnusson if replace: 44590c36d8aSUlf Magnusson self.unset_user_values() 44690c36d8aSUlf Magnusson else: 44790c36d8aSUlf Magnusson self._invalidate_all() 44890c36d8aSUlf Magnusson 44990c36d8aSUlf Magnusson while 1: 45090c36d8aSUlf Magnusson line = line_feeder.get_next() 45190c36d8aSUlf Magnusson if line is None: 45290c36d8aSUlf Magnusson return 45390c36d8aSUlf Magnusson 45490c36d8aSUlf Magnusson line = line.rstrip() 45590c36d8aSUlf Magnusson 45690c36d8aSUlf Magnusson set_match = _set_re_match(line) 45790c36d8aSUlf Magnusson if set_match: 45890c36d8aSUlf Magnusson name, val = set_match.groups() 45990c36d8aSUlf Magnusson 46090c36d8aSUlf Magnusson if val.startswith('"'): 46190c36d8aSUlf Magnusson if len(val) < 2 or val[-1] != '"': 46290c36d8aSUlf Magnusson _parse_error(line, "malformed string literal", 46390c36d8aSUlf Magnusson line_feeder.filename, line_feeder.linenr) 46490c36d8aSUlf Magnusson # Strip quotes and remove escapings. The unescaping 46590c36d8aSUlf Magnusson # procedure should be safe since " can only appear as \" 46690c36d8aSUlf Magnusson # inside the string. 46790c36d8aSUlf Magnusson val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\") 46890c36d8aSUlf Magnusson 46990c36d8aSUlf Magnusson if name in self.syms: 47090c36d8aSUlf Magnusson sym = self.syms[name] 47190c36d8aSUlf Magnusson if sym.user_val is not None: 47290c36d8aSUlf Magnusson warn_override(line_feeder.filename, line_feeder.linenr, 47390c36d8aSUlf Magnusson name, sym.user_val, val) 47490c36d8aSUlf Magnusson 47590c36d8aSUlf Magnusson if sym.is_choice_sym: 47690c36d8aSUlf Magnusson user_mode = sym.parent.user_mode 47790c36d8aSUlf Magnusson if user_mode is not None and user_mode != val: 47890c36d8aSUlf Magnusson self._warn("assignment to {0} changes mode of " 47990c36d8aSUlf Magnusson 'containing choice from "{1}" to "{2}".' 48090c36d8aSUlf Magnusson .format(name, val, user_mode), 48190c36d8aSUlf Magnusson line_feeder.filename, 48290c36d8aSUlf Magnusson line_feeder.linenr) 48390c36d8aSUlf Magnusson 48490c36d8aSUlf Magnusson sym._set_user_value_no_invalidate(val, True) 48590c36d8aSUlf Magnusson else: 48690c36d8aSUlf Magnusson if self.print_undef_assign: 48790c36d8aSUlf Magnusson _stderr_msg('note: attempt to assign the value "{0}" ' 48890c36d8aSUlf Magnusson "to the undefined symbol {1}." 48990c36d8aSUlf Magnusson .format(val, name), 49090c36d8aSUlf Magnusson line_feeder.filename, line_feeder.linenr) 49190c36d8aSUlf Magnusson else: 49290c36d8aSUlf Magnusson unset_match = _unset_re_match(line) 49390c36d8aSUlf Magnusson if unset_match: 49490c36d8aSUlf Magnusson name = unset_match.group(1) 49590c36d8aSUlf Magnusson if name in self.syms: 49690c36d8aSUlf Magnusson sym = self.syms[name] 49790c36d8aSUlf Magnusson if sym.user_val is not None: 49890c36d8aSUlf Magnusson warn_override(line_feeder.filename, 49990c36d8aSUlf Magnusson line_feeder.linenr, 50090c36d8aSUlf Magnusson name, sym.user_val, "n") 50190c36d8aSUlf Magnusson 50290c36d8aSUlf Magnusson sym._set_user_value_no_invalidate("n", True) 50390c36d8aSUlf Magnusson 50490c36d8aSUlf Magnusson def write_config(self, filename, header=None): 50590c36d8aSUlf Magnusson """Writes out symbol values in the familiar .config format. 50690c36d8aSUlf Magnusson 50790c36d8aSUlf Magnusson Kconfiglib makes sure the format matches what the C implementation 50890c36d8aSUlf Magnusson would generate, down to whitespace. This eases testing. 50990c36d8aSUlf Magnusson 51090c36d8aSUlf Magnusson filename: The filename under which to save the configuration. 51190c36d8aSUlf Magnusson 51290c36d8aSUlf Magnusson header (default: None): A textual header that will appear at the 51390c36d8aSUlf Magnusson beginning of the file, with each line commented out automatically. 51490c36d8aSUlf Magnusson None means no header.""" 51590c36d8aSUlf Magnusson 51690c36d8aSUlf Magnusson for sym in self.syms_iter(): 51790c36d8aSUlf Magnusson sym.already_written = False 51890c36d8aSUlf Magnusson 51990c36d8aSUlf Magnusson with open(filename, "w") as f: 52090c36d8aSUlf Magnusson # Write header 52190c36d8aSUlf Magnusson if header is not None: 52290c36d8aSUlf Magnusson f.write(_comment(header)) 52390c36d8aSUlf Magnusson f.write("\n") 52490c36d8aSUlf Magnusson 52590c36d8aSUlf Magnusson # Build and write configuration 52690c36d8aSUlf Magnusson conf_strings = [] 52790c36d8aSUlf Magnusson _make_block_conf(self.top_block, conf_strings.append) 52890c36d8aSUlf Magnusson f.write("\n".join(conf_strings)) 52990c36d8aSUlf Magnusson f.write("\n") 53090c36d8aSUlf Magnusson 531f219e013SMasahiro Yamada def eval(self, s): 532f219e013SMasahiro Yamada """Returns the value of the expression 's' -- where 's' is represented 533f219e013SMasahiro Yamada as a string -- in the context of the configuration. Raises 534f219e013SMasahiro Yamada Kconfig_Syntax_Error if syntax errors are detected in 's'. 535f219e013SMasahiro Yamada 536f219e013SMasahiro Yamada For example, if FOO and BAR are tristate symbols at least one of which 537f219e013SMasahiro Yamada has the value "y", then config.eval("y && (FOO || BAR)") => "y" 538f219e013SMasahiro Yamada 5399d01b787SMasahiro Yamada This function always yields a tristate value. To get the value of 540f219e013SMasahiro Yamada non-bool, non-tristate symbols, use Symbol.get_value(). 541f219e013SMasahiro Yamada 542f219e013SMasahiro Yamada The result of this function is consistent with how evaluation works for 543f219e013SMasahiro Yamada conditional expressions in the configuration as well as in the C 544f219e013SMasahiro Yamada implementation. "m" and m are rewritten as '"m" && MODULES' and 'm && 545f219e013SMasahiro Yamada MODULES', respectively, and a result of "m" will get promoted to "y" if 54690c36d8aSUlf Magnusson we're running without modules. 54790c36d8aSUlf Magnusson 54890c36d8aSUlf Magnusson Syntax checking is somewhat lax, partly to be compatible with lax 54990c36d8aSUlf Magnusson parsing in the C implementation.""" 550f219e013SMasahiro Yamada return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed 55190c36d8aSUlf Magnusson None, # Current symbol/choice 552f219e013SMasahiro Yamada s)) # line 553f219e013SMasahiro Yamada 55490c36d8aSUlf Magnusson def unset_user_values(self): 55590c36d8aSUlf Magnusson """Resets the values of all symbols, as if Config.load_config() or 55690c36d8aSUlf Magnusson Symbol.set_user_value() had never been called.""" 55790c36d8aSUlf Magnusson for sym in self.syms_iter(): 55890c36d8aSUlf Magnusson sym._unset_user_value_no_recursive_invalidate() 559f219e013SMasahiro Yamada 560f219e013SMasahiro Yamada def set_print_warnings(self, print_warnings): 561f219e013SMasahiro Yamada """Determines whether warnings related to this configuration (for 562f219e013SMasahiro Yamada things like attempting to assign illegal values to symbols with 563f219e013SMasahiro Yamada Symbol.set_user_value()) should be printed to stderr. 564f219e013SMasahiro Yamada 56590c36d8aSUlf Magnusson print_warnings: True if warnings should be printed.""" 566f219e013SMasahiro Yamada self.print_warnings = print_warnings 567f219e013SMasahiro Yamada 568f219e013SMasahiro Yamada def set_print_undef_assign(self, print_undef_assign): 569f219e013SMasahiro Yamada """Determines whether informational messages related to assignments to 570f219e013SMasahiro Yamada undefined symbols should be printed to stderr for this configuration. 571f219e013SMasahiro Yamada 57290c36d8aSUlf Magnusson print_undef_assign: If True, such messages will be printed.""" 573f219e013SMasahiro Yamada self.print_undef_assign = print_undef_assign 574f219e013SMasahiro Yamada 575f219e013SMasahiro Yamada def __str__(self): 576f219e013SMasahiro Yamada """Returns a string containing various information about the Config.""" 57790c36d8aSUlf Magnusson return _lines("Configuration", 57890c36d8aSUlf Magnusson "File : " + 57990c36d8aSUlf Magnusson self.filename, 58090c36d8aSUlf Magnusson "Base directory : " + 58190c36d8aSUlf Magnusson self.base_dir, 582f219e013SMasahiro Yamada "Value of $ARCH at creation time : " + 583f219e013SMasahiro Yamada ("(not set)" if self.arch is None else self.arch), 584f219e013SMasahiro Yamada "Value of $SRCARCH at creation time : " + 58590c36d8aSUlf Magnusson ("(not set)" if self.srcarch is None else 58690c36d8aSUlf Magnusson self.srcarch), 587f219e013SMasahiro Yamada "Source tree (derived from $srctree;", 58890c36d8aSUlf Magnusson "defaults to '.' if $srctree isn't set) : " + 58990c36d8aSUlf Magnusson self.srctree, 590f219e013SMasahiro Yamada "Most recently loaded .config : " + 59190c36d8aSUlf Magnusson ("(no .config loaded)" 59290c36d8aSUlf Magnusson if self.config_filename is None else 593f219e013SMasahiro Yamada self.config_filename), 594f219e013SMasahiro Yamada "Print warnings : " + 59590c36d8aSUlf Magnusson BOOL_STR[self.print_warnings], 596f219e013SMasahiro Yamada "Print assignments to undefined symbols : " + 59790c36d8aSUlf Magnusson BOOL_STR[self.print_undef_assign]) 598f219e013SMasahiro Yamada 599f219e013SMasahiro Yamada # 600f219e013SMasahiro Yamada # Private methods 601f219e013SMasahiro Yamada # 602f219e013SMasahiro Yamada 603f219e013SMasahiro Yamada # 60490c36d8aSUlf Magnusson # Kconfig parsing 605f219e013SMasahiro Yamada # 606f219e013SMasahiro Yamada 607f219e013SMasahiro Yamada def _parse_file(self, filename, parent, deps, visible_if_deps, res=None): 60890c36d8aSUlf Magnusson """Parses the Kconfig file 'filename'. Returns a list with the Items in 60990c36d8aSUlf Magnusson the file. See _parse_block() for the meaning of the parameters.""" 61090c36d8aSUlf Magnusson return self._parse_block(_FileFeed(filename), None, parent, deps, 61190c36d8aSUlf Magnusson visible_if_deps, res) 612f219e013SMasahiro Yamada 613f219e013SMasahiro Yamada def _parse_block(self, line_feeder, end_marker, parent, deps, 61490c36d8aSUlf Magnusson visible_if_deps, res=None): 615f219e013SMasahiro Yamada """Parses a block, which is the contents of either a file or an if, 61690c36d8aSUlf Magnusson menu, or choice statement. Returns a list with the Items in the block. 617f219e013SMasahiro Yamada 61890c36d8aSUlf Magnusson line_feeder: A _FileFeed instance feeding lines from a file. The 61990c36d8aSUlf Magnusson Kconfig language is line-based in practice. 620f219e013SMasahiro Yamada 62190c36d8aSUlf Magnusson end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for 62290c36d8aSUlf Magnusson ifs. None for files. 62390c36d8aSUlf Magnusson 62490c36d8aSUlf Magnusson parent: The enclosing menu or choice, or None if we're at the top 625f219e013SMasahiro Yamada level. 626f219e013SMasahiro Yamada 62790c36d8aSUlf Magnusson deps: Dependencies from enclosing menus, choices and ifs. 628f219e013SMasahiro Yamada 62990c36d8aSUlf Magnusson visible_if_deps (default: None): 'visible if' dependencies from 630f219e013SMasahiro Yamada enclosing menus. 631f219e013SMasahiro Yamada 63290c36d8aSUlf Magnusson res (default: None): The list to add items to. If None, a new list is 63390c36d8aSUlf Magnusson created to hold the items.""" 634f219e013SMasahiro Yamada 63590c36d8aSUlf Magnusson block = [] if res is None else res 636f219e013SMasahiro Yamada 637f219e013SMasahiro Yamada while 1: 638f219e013SMasahiro Yamada # Do we already have a tokenized line that we determined wasn't 639f219e013SMasahiro Yamada # part of whatever we were parsing earlier? See comment in 640f219e013SMasahiro Yamada # Config.__init__(). 641f219e013SMasahiro Yamada if self.end_line is not None: 642f219e013SMasahiro Yamada line = self.end_line 64390c36d8aSUlf Magnusson tokens = self.end_line_tokens 64490c36d8aSUlf Magnusson tokens.unget_all() 645f219e013SMasahiro Yamada 646f219e013SMasahiro Yamada self.end_line = None 647f219e013SMasahiro Yamada self.end_line_tokens = None 648f219e013SMasahiro Yamada else: 649f219e013SMasahiro Yamada line = line_feeder.get_next() 650f219e013SMasahiro Yamada if line is None: 651f219e013SMasahiro Yamada if end_marker is not None: 65290c36d8aSUlf Magnusson raise Kconfig_Syntax_Error("Unexpected end of file {0}" 65390c36d8aSUlf Magnusson .format(line_feeder.filename)) 654f219e013SMasahiro Yamada return block 655f219e013SMasahiro Yamada 65690c36d8aSUlf Magnusson tokens = self._tokenize(line, False, line_feeder.filename, 65790c36d8aSUlf Magnusson line_feeder.linenr) 658f219e013SMasahiro Yamada 659f219e013SMasahiro Yamada t0 = tokens.get_next() 66090c36d8aSUlf Magnusson if t0 is None: 66190c36d8aSUlf Magnusson continue 662f219e013SMasahiro Yamada 66390c36d8aSUlf Magnusson # Cases are ordered roughly by frequency, which speeds things up a 66490c36d8aSUlf Magnusson # bit 665f219e013SMasahiro Yamada 666f219e013SMasahiro Yamada if t0 == T_CONFIG or t0 == T_MENUCONFIG: 667f219e013SMasahiro Yamada # The tokenizer will automatically allocate a new Symbol object 668f219e013SMasahiro Yamada # for any new names it encounters, so we don't need to worry 669f219e013SMasahiro Yamada # about that here. 670f219e013SMasahiro Yamada sym = tokens.get_next() 671f219e013SMasahiro Yamada 672f219e013SMasahiro Yamada # Symbols defined in multiple places get the parent of their 67390c36d8aSUlf Magnusson # first definition. However, for symbols whose parents are 67490c36d8aSUlf Magnusson # choice statements, the choice statement takes precedence. 675f219e013SMasahiro Yamada if not sym.is_defined_ or isinstance(parent, Choice): 676f219e013SMasahiro Yamada sym.parent = parent 677f219e013SMasahiro Yamada 678f219e013SMasahiro Yamada sym.is_defined_ = True 679f219e013SMasahiro Yamada 680f219e013SMasahiro Yamada self.kconfig_syms.append(sym) 68190c36d8aSUlf Magnusson block.append(sym) 682f219e013SMasahiro Yamada 683f219e013SMasahiro Yamada self._parse_properties(line_feeder, sym, deps, visible_if_deps) 684f219e013SMasahiro Yamada 68590c36d8aSUlf Magnusson elif t0 == T_SOURCE: 68690c36d8aSUlf Magnusson kconfig_file = tokens.get_next() 68790c36d8aSUlf Magnusson exp_kconfig_file = self._expand_sym_refs(kconfig_file) 68890c36d8aSUlf Magnusson f = os.path.join(self.base_dir, exp_kconfig_file) 68990c36d8aSUlf Magnusson if not os.path.exists(f): 69090c36d8aSUlf Magnusson raise IOError('{0}:{1}: sourced file "{2}" (expands to ' 69190c36d8aSUlf Magnusson '"{3}") not found. Perhaps base_dir ' 69290c36d8aSUlf Magnusson '(argument to Config.__init__(), currently ' 69390c36d8aSUlf Magnusson '"{4}") is set to the wrong value.' 69490c36d8aSUlf Magnusson .format(line_feeder.filename, 69590c36d8aSUlf Magnusson line_feeder.linenr, 69690c36d8aSUlf Magnusson kconfig_file, exp_kconfig_file, 69790c36d8aSUlf Magnusson self.base_dir)) 69890c36d8aSUlf Magnusson # Add items to the same block 69990c36d8aSUlf Magnusson self._parse_file(f, parent, deps, visible_if_deps, block) 700f219e013SMasahiro Yamada 70190c36d8aSUlf Magnusson elif t0 == end_marker: 70290c36d8aSUlf Magnusson # We have reached the end of the block 70390c36d8aSUlf Magnusson return block 704f219e013SMasahiro Yamada 705f219e013SMasahiro Yamada elif t0 == T_IF: 706f219e013SMasahiro Yamada # If statements are treated as syntactic sugar for adding 707f219e013SMasahiro Yamada # dependencies to enclosed items and do not have an explicit 708f219e013SMasahiro Yamada # object representation. 709f219e013SMasahiro Yamada 71090c36d8aSUlf Magnusson dep_expr = self._parse_expr(tokens, None, line, 71190c36d8aSUlf Magnusson line_feeder.filename, 71290c36d8aSUlf Magnusson line_feeder.linenr) 71390c36d8aSUlf Magnusson # Add items to the same block 71490c36d8aSUlf Magnusson self._parse_block(line_feeder, T_ENDIF, parent, 715f219e013SMasahiro Yamada _make_and(dep_expr, deps), 71690c36d8aSUlf Magnusson visible_if_deps, block) 71790c36d8aSUlf Magnusson 71890c36d8aSUlf Magnusson elif t0 == T_COMMENT: 71990c36d8aSUlf Magnusson comment = Comment() 72090c36d8aSUlf Magnusson 72190c36d8aSUlf Magnusson comment.config = self 72290c36d8aSUlf Magnusson comment.parent = parent 72390c36d8aSUlf Magnusson comment.filename = line_feeder.filename 72490c36d8aSUlf Magnusson comment.linenr = line_feeder.linenr 72590c36d8aSUlf Magnusson comment.text = tokens.get_next() 72690c36d8aSUlf Magnusson 72790c36d8aSUlf Magnusson self.comments.append(comment) 72890c36d8aSUlf Magnusson block.append(comment) 72990c36d8aSUlf Magnusson 73090c36d8aSUlf Magnusson self._parse_properties(line_feeder, comment, deps, 73190c36d8aSUlf Magnusson visible_if_deps) 73290c36d8aSUlf Magnusson 73390c36d8aSUlf Magnusson elif t0 == T_MENU: 73490c36d8aSUlf Magnusson menu = Menu() 73590c36d8aSUlf Magnusson 73690c36d8aSUlf Magnusson menu.config = self 73790c36d8aSUlf Magnusson menu.parent = parent 73890c36d8aSUlf Magnusson menu.filename = line_feeder.filename 73990c36d8aSUlf Magnusson menu.linenr = line_feeder.linenr 74090c36d8aSUlf Magnusson menu.title = tokens.get_next() 74190c36d8aSUlf Magnusson 74290c36d8aSUlf Magnusson self.menus.append(menu) 74390c36d8aSUlf Magnusson block.append(menu) 74490c36d8aSUlf Magnusson 74590c36d8aSUlf Magnusson # Parse properties and contents 74690c36d8aSUlf Magnusson self._parse_properties(line_feeder, menu, deps, 74790c36d8aSUlf Magnusson visible_if_deps) 74890c36d8aSUlf Magnusson menu.block = self._parse_block(line_feeder, T_ENDMENU, menu, 74990c36d8aSUlf Magnusson menu.dep_expr, 75090c36d8aSUlf Magnusson _make_and(visible_if_deps, 75190c36d8aSUlf Magnusson menu.visible_if_expr)) 752f219e013SMasahiro Yamada 753f219e013SMasahiro Yamada elif t0 == T_CHOICE: 75490c36d8aSUlf Magnusson name = tokens.get_next() 75590c36d8aSUlf Magnusson if name is None: 756f219e013SMasahiro Yamada choice = Choice() 757f219e013SMasahiro Yamada self.choices.append(choice) 75890c36d8aSUlf Magnusson else: 75990c36d8aSUlf Magnusson # Named choice 76090c36d8aSUlf Magnusson choice = self.named_choices.get(name) 76190c36d8aSUlf Magnusson if choice is None: 76290c36d8aSUlf Magnusson choice = Choice() 763f219e013SMasahiro Yamada choice.name = name 764f219e013SMasahiro Yamada self.named_choices[name] = choice 76590c36d8aSUlf Magnusson self.choices.append(choice) 766f219e013SMasahiro Yamada 767f219e013SMasahiro Yamada choice.config = self 768f219e013SMasahiro Yamada choice.parent = parent 769f219e013SMasahiro Yamada 77090c36d8aSUlf Magnusson choice.def_locations.append((line_feeder.filename, 77190c36d8aSUlf Magnusson line_feeder.linenr)) 772f219e013SMasahiro Yamada 773f219e013SMasahiro Yamada # Parse properties and contents 77490c36d8aSUlf Magnusson self._parse_properties(line_feeder, choice, deps, 775f219e013SMasahiro Yamada visible_if_deps) 77690c36d8aSUlf Magnusson choice.block = self._parse_block(line_feeder, T_ENDCHOICE, 77790c36d8aSUlf Magnusson choice, deps, visible_if_deps) 778f219e013SMasahiro Yamada 779f219e013SMasahiro Yamada choice._determine_actual_symbols() 780f219e013SMasahiro Yamada 78190c36d8aSUlf Magnusson # If no type is specified for the choice, its type is that of 78290c36d8aSUlf Magnusson # the first choice item with a specified type 783f219e013SMasahiro Yamada if choice.type == UNKNOWN: 78490c36d8aSUlf Magnusson for item in choice.actual_symbols: 785f219e013SMasahiro Yamada if item.type != UNKNOWN: 786f219e013SMasahiro Yamada choice.type = item.type 787f219e013SMasahiro Yamada break 788f219e013SMasahiro Yamada 789f219e013SMasahiro Yamada # Each choice item of UNKNOWN type gets the type of the choice 79090c36d8aSUlf Magnusson for item in choice.actual_symbols: 791f219e013SMasahiro Yamada if item.type == UNKNOWN: 792f219e013SMasahiro Yamada item.type = choice.type 793f219e013SMasahiro Yamada 79490c36d8aSUlf Magnusson block.append(choice) 795f219e013SMasahiro Yamada 796f219e013SMasahiro Yamada elif t0 == T_MAINMENU: 797f219e013SMasahiro Yamada text = tokens.get_next() 798f219e013SMasahiro Yamada if self.mainmenu_text is not None: 799f219e013SMasahiro Yamada self._warn("overriding 'mainmenu' text. " 800f219e013SMasahiro Yamada 'Old value: "{0}", new value: "{1}".' 801f219e013SMasahiro Yamada .format(self.mainmenu_text, text), 80290c36d8aSUlf Magnusson line_feeder.filename, line_feeder.linenr) 803f219e013SMasahiro Yamada self.mainmenu_text = text 804f219e013SMasahiro Yamada 805f219e013SMasahiro Yamada else: 80690c36d8aSUlf Magnusson _parse_error(line, "unrecognized construct", 80790c36d8aSUlf Magnusson line_feeder.filename, line_feeder.linenr) 808f219e013SMasahiro Yamada 809f219e013SMasahiro Yamada def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps): 81090c36d8aSUlf Magnusson """Parsing of properties for symbols, menus, choices, and comments. 81190c36d8aSUlf Magnusson Takes care of propagating dependencies from enclosing menus and ifs.""" 812f219e013SMasahiro Yamada 813f219e013SMasahiro Yamada def parse_val_and_cond(tokens, line, filename, linenr): 814f219e013SMasahiro Yamada """Parses '<expr1> if <expr2>' constructs, where the 'if' part is 815f219e013SMasahiro Yamada optional. Returns a tuple containing the parsed expressions, with 816f219e013SMasahiro Yamada None as the second element if the 'if' part is missing.""" 817f219e013SMasahiro Yamada val = self._parse_expr(tokens, stmt, line, filename, linenr, False) 818f219e013SMasahiro Yamada if tokens.check(T_IF): 81990c36d8aSUlf Magnusson return (val, self._parse_expr(tokens, stmt, line, filename, 82090c36d8aSUlf Magnusson linenr)) 821f219e013SMasahiro Yamada return (val, None) 822f219e013SMasahiro Yamada 823f219e013SMasahiro Yamada # In case the symbol is defined in multiple locations, we need to 824f219e013SMasahiro Yamada # remember what prompts, defaults, and selects are new for this 825f219e013SMasahiro Yamada # definition, as "depends on" should only apply to the local 826f219e013SMasahiro Yamada # definition. 827f219e013SMasahiro Yamada new_prompt = None 828f219e013SMasahiro Yamada new_def_exprs = [] 829f219e013SMasahiro Yamada new_selects = [] 830f219e013SMasahiro Yamada 831f219e013SMasahiro Yamada # Dependencies from 'depends on' statements 832f219e013SMasahiro Yamada depends_on_expr = None 833f219e013SMasahiro Yamada 834f219e013SMasahiro Yamada while 1: 835f219e013SMasahiro Yamada line = line_feeder.get_next() 836f219e013SMasahiro Yamada if line is None: 837f219e013SMasahiro Yamada break 838f219e013SMasahiro Yamada 83990c36d8aSUlf Magnusson filename = line_feeder.filename 84090c36d8aSUlf Magnusson linenr = line_feeder.linenr 841f219e013SMasahiro Yamada 842f219e013SMasahiro Yamada tokens = self._tokenize(line, False, filename, linenr) 843f219e013SMasahiro Yamada 84490c36d8aSUlf Magnusson t0 = tokens.get_next() 84590c36d8aSUlf Magnusson if t0 is None: 846f219e013SMasahiro Yamada continue 847f219e013SMasahiro Yamada 84890c36d8aSUlf Magnusson # Cases are ordered roughly by frequency, which speeds things up a 84990c36d8aSUlf Magnusson # bit 850f219e013SMasahiro Yamada 85190c36d8aSUlf Magnusson if t0 == T_DEPENDS: 85290c36d8aSUlf Magnusson if not tokens.check(T_ON): 85390c36d8aSUlf Magnusson _parse_error(line, 'expected "on" after "depends"', 85490c36d8aSUlf Magnusson filename, linenr) 855f219e013SMasahiro Yamada 85690c36d8aSUlf Magnusson parsed_deps = self._parse_expr(tokens, stmt, line, filename, 85790c36d8aSUlf Magnusson linenr) 858f219e013SMasahiro Yamada 85990c36d8aSUlf Magnusson if isinstance(stmt, (Menu, Comment)): 86090c36d8aSUlf Magnusson stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps) 86190c36d8aSUlf Magnusson else: 86290c36d8aSUlf Magnusson depends_on_expr = _make_and(depends_on_expr, parsed_deps) 86390c36d8aSUlf Magnusson 86490c36d8aSUlf Magnusson elif t0 == T_HELP: 86590c36d8aSUlf Magnusson # Find first non-blank (not all-space) line and get its 86690c36d8aSUlf Magnusson # indentation 86790c36d8aSUlf Magnusson line = line_feeder.next_nonblank() 868f219e013SMasahiro Yamada if line is None: 869f219e013SMasahiro Yamada stmt.help = "" 870f219e013SMasahiro Yamada break 871f219e013SMasahiro Yamada indent = _indentation(line) 87290c36d8aSUlf Magnusson if indent == 0: 873f219e013SMasahiro Yamada # If the first non-empty lines has zero indent, there is no 874f219e013SMasahiro Yamada # help text 875f219e013SMasahiro Yamada stmt.help = "" 87690c36d8aSUlf Magnusson line_feeder.unget() 877f219e013SMasahiro Yamada break 878f219e013SMasahiro Yamada 879f219e013SMasahiro Yamada # The help text goes on till the first non-empty line with less 880f219e013SMasahiro Yamada # indent 88190c36d8aSUlf Magnusson help_lines = [_deindent(line, indent)] 882f219e013SMasahiro Yamada while 1: 883f219e013SMasahiro Yamada line = line_feeder.get_next() 88490c36d8aSUlf Magnusson if line is None or \ 885f219e013SMasahiro Yamada (not line.isspace() and _indentation(line) < indent): 886f219e013SMasahiro Yamada stmt.help = "".join(help_lines) 887f219e013SMasahiro Yamada break 888f219e013SMasahiro Yamada help_lines.append(_deindent(line, indent)) 889f219e013SMasahiro Yamada 890f219e013SMasahiro Yamada if line is None: 891f219e013SMasahiro Yamada break 892f219e013SMasahiro Yamada 89390c36d8aSUlf Magnusson line_feeder.unget() 894f219e013SMasahiro Yamada 895*d036107aSTom Rini elif t0 == T_SELECT or t0 == T_IMPLY: 896f219e013SMasahiro Yamada target = tokens.get_next() 897f219e013SMasahiro Yamada 898f219e013SMasahiro Yamada stmt.referenced_syms.add(target) 899f219e013SMasahiro Yamada stmt.selected_syms.add(target) 900f219e013SMasahiro Yamada 901f219e013SMasahiro Yamada if tokens.check(T_IF): 902f219e013SMasahiro Yamada new_selects.append((target, 90390c36d8aSUlf Magnusson self._parse_expr(tokens, stmt, line, 90490c36d8aSUlf Magnusson filename, linenr))) 905f219e013SMasahiro Yamada else: 906f219e013SMasahiro Yamada new_selects.append((target, None)) 907f219e013SMasahiro Yamada 908f219e013SMasahiro Yamada elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING): 90990c36d8aSUlf Magnusson stmt.type = TOKEN_TO_TYPE[t0] 91090c36d8aSUlf Magnusson if tokens.peek_next() is not None: 91190c36d8aSUlf Magnusson new_prompt = parse_val_and_cond(tokens, line, filename, 91290c36d8aSUlf Magnusson linenr) 913f219e013SMasahiro Yamada 91490c36d8aSUlf Magnusson elif t0 == T_DEFAULT: 91590c36d8aSUlf Magnusson new_def_exprs.append(parse_val_and_cond(tokens, line, filename, 91690c36d8aSUlf Magnusson linenr)) 917f219e013SMasahiro Yamada 918f219e013SMasahiro Yamada elif t0 == T_DEF_BOOL: 919f219e013SMasahiro Yamada stmt.type = BOOL 92090c36d8aSUlf Magnusson if tokens.peek_next() is not None: 92190c36d8aSUlf Magnusson new_def_exprs.append(parse_val_and_cond(tokens, line, 92290c36d8aSUlf Magnusson filename, linenr)) 923f219e013SMasahiro Yamada 92490c36d8aSUlf Magnusson elif t0 == T_PROMPT: 92590c36d8aSUlf Magnusson # 'prompt' properties override each other within a single 92690c36d8aSUlf Magnusson # definition of a symbol, but additional prompts can be added 92790c36d8aSUlf Magnusson # by defining the symbol multiple times; hence 'new_prompt' 92890c36d8aSUlf Magnusson # instead of 'prompt'. 92990c36d8aSUlf Magnusson new_prompt = parse_val_and_cond(tokens, line, filename, linenr) 93090c36d8aSUlf Magnusson 93190c36d8aSUlf Magnusson elif t0 == T_RANGE: 93290c36d8aSUlf Magnusson low = tokens.get_next() 93390c36d8aSUlf Magnusson high = tokens.get_next() 93490c36d8aSUlf Magnusson stmt.referenced_syms.add(low) 93590c36d8aSUlf Magnusson stmt.referenced_syms.add(high) 93690c36d8aSUlf Magnusson 93790c36d8aSUlf Magnusson if tokens.check(T_IF): 93890c36d8aSUlf Magnusson stmt.ranges.append((low, high, 93990c36d8aSUlf Magnusson self._parse_expr(tokens, stmt, line, 94090c36d8aSUlf Magnusson filename, linenr))) 94190c36d8aSUlf Magnusson else: 94290c36d8aSUlf Magnusson stmt.ranges.append((low, high, None)) 943f219e013SMasahiro Yamada 944f219e013SMasahiro Yamada elif t0 == T_DEF_TRISTATE: 945f219e013SMasahiro Yamada stmt.type = TRISTATE 94690c36d8aSUlf Magnusson if tokens.peek_next() is not None: 94790c36d8aSUlf Magnusson new_def_exprs.append(parse_val_and_cond(tokens, line, 94890c36d8aSUlf Magnusson filename, linenr)) 949f219e013SMasahiro Yamada 950f219e013SMasahiro Yamada elif t0 == T_OPTION: 951f219e013SMasahiro Yamada if tokens.check(T_ENV) and tokens.check(T_EQUAL): 952f219e013SMasahiro Yamada env_var = tokens.get_next() 953f219e013SMasahiro Yamada 954f219e013SMasahiro Yamada stmt.is_special_ = True 955f219e013SMasahiro Yamada stmt.is_from_env = True 956f219e013SMasahiro Yamada 957f219e013SMasahiro Yamada if env_var not in os.environ: 95890c36d8aSUlf Magnusson self._warn("The symbol {0} references the " 95990c36d8aSUlf Magnusson "non-existent environment variable {1} and " 96090c36d8aSUlf Magnusson "will get the empty string as its value. " 96190c36d8aSUlf Magnusson "If you're using Kconfiglib via " 96290c36d8aSUlf Magnusson "'make (i)scriptconfig', it should have " 96390c36d8aSUlf Magnusson "set up the environment correctly for you. " 96490c36d8aSUlf Magnusson "If you still got this message, that " 96590c36d8aSUlf Magnusson "might be an error, and you should email " 96690c36d8aSUlf Magnusson "ulfalizer a.t Google's email service.""" 96790c36d8aSUlf Magnusson .format(stmt.name, env_var), 96890c36d8aSUlf Magnusson filename, linenr) 969f219e013SMasahiro Yamada 97090c36d8aSUlf Magnusson stmt.cached_val = "" 971f219e013SMasahiro Yamada else: 97290c36d8aSUlf Magnusson stmt.cached_val = os.environ[env_var] 973f219e013SMasahiro Yamada 974f219e013SMasahiro Yamada elif tokens.check(T_DEFCONFIG_LIST): 975f219e013SMasahiro Yamada self.defconfig_sym = stmt 976f219e013SMasahiro Yamada 977f219e013SMasahiro Yamada elif tokens.check(T_MODULES): 97890c36d8aSUlf Magnusson # To reduce warning spam, only warn if 'option modules' is 97990c36d8aSUlf Magnusson # set on some symbol that isn't MODULES, which should be 98090c36d8aSUlf Magnusson # safe. I haven't run into any projects that make use 98190c36d8aSUlf Magnusson # modules besides the kernel yet, and there it's likely to 98290c36d8aSUlf Magnusson # keep being called "MODULES". 98390c36d8aSUlf Magnusson if stmt.name != "MODULES": 984f219e013SMasahiro Yamada self._warn("the 'modules' option is not supported. " 985f219e013SMasahiro Yamada "Let me know if this is a problem for you; " 9869d01b787SMasahiro Yamada "it shouldn't be that hard to implement. " 9879d01b787SMasahiro Yamada "(Note that modules are still supported -- " 9889d01b787SMasahiro Yamada "Kconfiglib just assumes the symbol name " 98990c36d8aSUlf Magnusson "MODULES, like older versions of the C " 99090c36d8aSUlf Magnusson "implementation did when 'option modules' " 99190c36d8aSUlf Magnusson "wasn't used.)", 99290c36d8aSUlf Magnusson filename, linenr) 993f219e013SMasahiro Yamada 9949d01b787SMasahiro Yamada elif tokens.check(T_ALLNOCONFIG_Y): 9959d01b787SMasahiro Yamada if not isinstance(stmt, Symbol): 9969d01b787SMasahiro Yamada _parse_error(line, 99790c36d8aSUlf Magnusson "the 'allnoconfig_y' option is only " 99890c36d8aSUlf Magnusson "valid for symbols", 99990c36d8aSUlf Magnusson filename, linenr) 10009d01b787SMasahiro Yamada stmt.allnoconfig_y = True 10019d01b787SMasahiro Yamada 1002f219e013SMasahiro Yamada else: 100390c36d8aSUlf Magnusson _parse_error(line, "unrecognized option", filename, linenr) 100490c36d8aSUlf Magnusson 100590c36d8aSUlf Magnusson elif t0 == T_VISIBLE: 100690c36d8aSUlf Magnusson if not tokens.check(T_IF): 100790c36d8aSUlf Magnusson _parse_error(line, 'expected "if" after "visible"', 100890c36d8aSUlf Magnusson filename, linenr) 100990c36d8aSUlf Magnusson if not isinstance(stmt, Menu): 101090c36d8aSUlf Magnusson _parse_error(line, 101190c36d8aSUlf Magnusson "'visible if' is only valid for menus", 101290c36d8aSUlf Magnusson filename, linenr) 101390c36d8aSUlf Magnusson 101490c36d8aSUlf Magnusson parsed_deps = self._parse_expr(tokens, stmt, line, filename, 101590c36d8aSUlf Magnusson linenr) 101690c36d8aSUlf Magnusson stmt.visible_if_expr = _make_and(stmt.visible_if_expr, 101790c36d8aSUlf Magnusson parsed_deps) 101890c36d8aSUlf Magnusson 101990c36d8aSUlf Magnusson elif t0 == T_OPTIONAL: 102090c36d8aSUlf Magnusson if not isinstance(stmt, Choice): 102190c36d8aSUlf Magnusson _parse_error(line, 102290c36d8aSUlf Magnusson '"optional" is only valid for choices', 102390c36d8aSUlf Magnusson filename, 102490c36d8aSUlf Magnusson linenr) 102590c36d8aSUlf Magnusson stmt.optional = True 1026f219e013SMasahiro Yamada 1027f219e013SMasahiro Yamada else: 1028f219e013SMasahiro Yamada # See comment in Config.__init__() 1029f219e013SMasahiro Yamada self.end_line = line 1030f219e013SMasahiro Yamada self.end_line_tokens = tokens 1031f219e013SMasahiro Yamada break 1032f219e013SMasahiro Yamada 103390c36d8aSUlf Magnusson # Done parsing properties. Now propagate 'depends on' and enclosing 103490c36d8aSUlf Magnusson # menu/if dependencies to expressions. 1035f219e013SMasahiro Yamada 103690c36d8aSUlf Magnusson # The set of symbols referenced directly by the statement plus all 103790c36d8aSUlf Magnusson # symbols referenced by enclosing menus and ifs 103890c36d8aSUlf Magnusson stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps) 103990c36d8aSUlf Magnusson 104090c36d8aSUlf Magnusson # Save original dependencies from enclosing menus and ifs 1041f219e013SMasahiro Yamada stmt.deps_from_containing = deps 1042f219e013SMasahiro Yamada 104390c36d8aSUlf Magnusson if isinstance(stmt, (Menu, Comment)): 104490c36d8aSUlf Magnusson stmt.dep_expr = _make_and(stmt.orig_deps, deps) 1045f219e013SMasahiro Yamada else: 104690c36d8aSUlf Magnusson # Symbol or Choice 1047f219e013SMasahiro Yamada 1048f219e013SMasahiro Yamada # See comment for 'menu_dep' 1049f219e013SMasahiro Yamada stmt.menu_dep = depends_on_expr 1050f219e013SMasahiro Yamada 105190c36d8aSUlf Magnusson # Propagate dependencies to prompts 1052f219e013SMasahiro Yamada 1053f219e013SMasahiro Yamada if new_prompt is not None: 105490c36d8aSUlf Magnusson # Propagate 'visible if' dependencies from enclosing menus 1055f219e013SMasahiro Yamada prompt, cond_expr = new_prompt 1056f219e013SMasahiro Yamada cond_expr = _make_and(cond_expr, visible_if_deps) 105790c36d8aSUlf Magnusson # Propagate 'depends on' dependencies 1058f219e013SMasahiro Yamada new_prompt = (prompt, _make_and(cond_expr, depends_on_expr)) 105990c36d8aSUlf Magnusson # Save original 1060f219e013SMasahiro Yamada stmt.orig_prompts.append(new_prompt) 106190c36d8aSUlf Magnusson # Finalize with dependencies from enclosing menus and ifs 106290c36d8aSUlf Magnusson stmt.prompts.append((new_prompt[0], 106390c36d8aSUlf Magnusson _make_and(new_prompt[1], deps))) 106490c36d8aSUlf Magnusson 106590c36d8aSUlf Magnusson # Propagate dependencies to defaults 106690c36d8aSUlf Magnusson 106790c36d8aSUlf Magnusson # Propagate 'depends on' dependencies 106890c36d8aSUlf Magnusson new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr)) 106990c36d8aSUlf Magnusson for val_expr, cond_expr in new_def_exprs] 107090c36d8aSUlf Magnusson # Save original 107190c36d8aSUlf Magnusson stmt.orig_def_exprs.extend(new_def_exprs) 107290c36d8aSUlf Magnusson # Finalize with dependencies from enclosing menus and ifs 107390c36d8aSUlf Magnusson stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps)) 107490c36d8aSUlf Magnusson for val_expr, cond_expr in new_def_exprs]) 107590c36d8aSUlf Magnusson 107690c36d8aSUlf Magnusson # Propagate dependencies to selects 1077f219e013SMasahiro Yamada 1078f219e013SMasahiro Yamada # Only symbols can select 1079f219e013SMasahiro Yamada if isinstance(stmt, Symbol): 108090c36d8aSUlf Magnusson # Propagate 'depends on' dependencies 108190c36d8aSUlf Magnusson new_selects = [(target, _make_and(cond_expr, depends_on_expr)) 108290c36d8aSUlf Magnusson for target, cond_expr in new_selects] 108390c36d8aSUlf Magnusson # Save original 1084f219e013SMasahiro Yamada stmt.orig_selects.extend(new_selects) 108590c36d8aSUlf Magnusson # Finalize with dependencies from enclosing menus and ifs 108690c36d8aSUlf Magnusson for target, cond in new_selects: 1087f219e013SMasahiro Yamada target.rev_dep = _make_or(target.rev_dep, 1088f219e013SMasahiro Yamada _make_and(stmt, 1089f219e013SMasahiro Yamada _make_and(cond, deps))) 1090f219e013SMasahiro Yamada 109190c36d8aSUlf Magnusson def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None, 109290c36d8aSUlf Magnusson transform_m=True): 109390c36d8aSUlf Magnusson """Parses an expression from the tokens in 'feed' using a simple 109490c36d8aSUlf Magnusson top-down approach. The result has the form 109590c36d8aSUlf Magnusson '(<operator>, [<parsed operands>])', where <operator> is e.g. 109690c36d8aSUlf Magnusson kconfiglib.AND. If there is only one operand (i.e., no && or ||), then 109790c36d8aSUlf Magnusson the operand is returned directly. This also goes for subexpressions. 1098f219e013SMasahiro Yamada 109990c36d8aSUlf Magnusson feed: _Feed instance containing the tokens for the expression. 1100f219e013SMasahiro Yamada 110190c36d8aSUlf Magnusson cur_item: The item (Symbol, Choice, Menu, or Comment) currently being 110290c36d8aSUlf Magnusson parsed, or None if we're not parsing an item. Used for recording 110390c36d8aSUlf Magnusson references to symbols. 110490c36d8aSUlf Magnusson 110590c36d8aSUlf Magnusson line: The line containing the expression being parsed. 110690c36d8aSUlf Magnusson 110790c36d8aSUlf Magnusson filename (default: None): The file containing the expression. 110890c36d8aSUlf Magnusson 110990c36d8aSUlf Magnusson linenr (default: None): The line number containing the expression. 111090c36d8aSUlf Magnusson 111190c36d8aSUlf Magnusson transform_m (default: False): Determines if 'm' should be rewritten to 111290c36d8aSUlf Magnusson 'm && MODULES' -- see parse_val_and_cond(). 111390c36d8aSUlf Magnusson 111490c36d8aSUlf Magnusson Expression grammar, in decreasing order of precedence: 111590c36d8aSUlf Magnusson 111690c36d8aSUlf Magnusson <expr> -> <symbol> 111790c36d8aSUlf Magnusson <symbol> '=' <symbol> 111890c36d8aSUlf Magnusson <symbol> '!=' <symbol> 111990c36d8aSUlf Magnusson '(' <expr> ')' 112090c36d8aSUlf Magnusson '!' <expr> 112190c36d8aSUlf Magnusson <expr> '&&' <expr> 112290c36d8aSUlf Magnusson <expr> '||' <expr>""" 112390c36d8aSUlf Magnusson 112490c36d8aSUlf Magnusson # Use instance variables to avoid having to pass these as arguments 112590c36d8aSUlf Magnusson # through the top-down parser in _parse_expr_rec(), which is tedious 112690c36d8aSUlf Magnusson # and obfuscates the code. A profiler run shows no noticeable 112790c36d8aSUlf Magnusson # performance difference. 112890c36d8aSUlf Magnusson self._cur_item = cur_item 112990c36d8aSUlf Magnusson self._transform_m = transform_m 113090c36d8aSUlf Magnusson self._line = line 113190c36d8aSUlf Magnusson self._filename = filename 113290c36d8aSUlf Magnusson self._linenr = linenr 113390c36d8aSUlf Magnusson 113490c36d8aSUlf Magnusson return self._parse_expr_rec(feed) 113590c36d8aSUlf Magnusson 113690c36d8aSUlf Magnusson def _parse_expr_rec(self, feed): 113790c36d8aSUlf Magnusson or_term = self._parse_or_term(feed) 113890c36d8aSUlf Magnusson if not feed.check(T_OR): 113990c36d8aSUlf Magnusson # Common case -- no need for an OR node since it's just a single 114090c36d8aSUlf Magnusson # operand 114190c36d8aSUlf Magnusson return or_term 114290c36d8aSUlf Magnusson or_terms = [or_term, self._parse_or_term(feed)] 114390c36d8aSUlf Magnusson while feed.check(T_OR): 114490c36d8aSUlf Magnusson or_terms.append(self._parse_or_term(feed)) 114590c36d8aSUlf Magnusson return (OR, or_terms) 114690c36d8aSUlf Magnusson 114790c36d8aSUlf Magnusson def _parse_or_term(self, feed): 114890c36d8aSUlf Magnusson and_term = self._parse_factor(feed) 114990c36d8aSUlf Magnusson if not feed.check(T_AND): 115090c36d8aSUlf Magnusson # Common case -- no need for an AND node since it's just a single 115190c36d8aSUlf Magnusson # operand 115290c36d8aSUlf Magnusson return and_term 115390c36d8aSUlf Magnusson and_terms = [and_term, self._parse_factor(feed)] 115490c36d8aSUlf Magnusson while feed.check(T_AND): 115590c36d8aSUlf Magnusson and_terms.append(self._parse_factor(feed)) 115690c36d8aSUlf Magnusson return (AND, and_terms) 115790c36d8aSUlf Magnusson 115890c36d8aSUlf Magnusson def _parse_factor(self, feed): 115990c36d8aSUlf Magnusson token = feed.get_next() 116090c36d8aSUlf Magnusson 116190c36d8aSUlf Magnusson if isinstance(token, (Symbol, str)): 116290c36d8aSUlf Magnusson if self._cur_item is not None and isinstance(token, Symbol): 116390c36d8aSUlf Magnusson self._cur_item.referenced_syms.add(token) 116490c36d8aSUlf Magnusson 116590c36d8aSUlf Magnusson next_token = feed.peek_next() 116690c36d8aSUlf Magnusson # For conditional expressions ('depends on <expr>', 116790c36d8aSUlf Magnusson # '... if <expr>', # etc.), "m" and m are rewritten to 116890c36d8aSUlf Magnusson # "m" && MODULES. 116990c36d8aSUlf Magnusson if next_token != T_EQUAL and next_token != T_UNEQUAL: 117090c36d8aSUlf Magnusson if self._transform_m and (token is self.m or token == "m"): 117190c36d8aSUlf Magnusson return (AND, ["m", self._sym_lookup("MODULES")]) 117290c36d8aSUlf Magnusson return token 117390c36d8aSUlf Magnusson 117490c36d8aSUlf Magnusson relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL 117590c36d8aSUlf Magnusson token_2 = feed.get_next() 117690c36d8aSUlf Magnusson if self._cur_item is not None and isinstance(token_2, Symbol): 117790c36d8aSUlf Magnusson self._cur_item.referenced_syms.add(token_2) 117890c36d8aSUlf Magnusson return (relation, token, token_2) 117990c36d8aSUlf Magnusson 118090c36d8aSUlf Magnusson if token == T_NOT: 118190c36d8aSUlf Magnusson return (NOT, self._parse_factor(feed)) 118290c36d8aSUlf Magnusson 118390c36d8aSUlf Magnusson if token == T_OPEN_PAREN: 118490c36d8aSUlf Magnusson expr_parse = self._parse_expr_rec(feed) 118590c36d8aSUlf Magnusson if not feed.check(T_CLOSE_PAREN): 118690c36d8aSUlf Magnusson _parse_error(self._line, "missing end parenthesis", 118790c36d8aSUlf Magnusson self._filename, self._linenr) 118890c36d8aSUlf Magnusson return expr_parse 118990c36d8aSUlf Magnusson 119090c36d8aSUlf Magnusson _parse_error(self._line, "malformed expression", self._filename, 119190c36d8aSUlf Magnusson self._linenr) 119290c36d8aSUlf Magnusson 119390c36d8aSUlf Magnusson def _tokenize(self, s, for_eval, filename=None, linenr=None): 119490c36d8aSUlf Magnusson """Returns a _Feed instance containing tokens derived from the string 119590c36d8aSUlf Magnusson 's'. Registers any new symbols encountered (via _sym_lookup()). 119690c36d8aSUlf Magnusson 119790c36d8aSUlf Magnusson (I experimented with a pure regular expression implementation, but it 119890c36d8aSUlf Magnusson came out slower, less readable, and wouldn't have been as flexible.) 119990c36d8aSUlf Magnusson 120090c36d8aSUlf Magnusson for_eval: True when parsing an expression for a call to Config.eval(), 120190c36d8aSUlf Magnusson in which case we should not treat the first token specially nor 120290c36d8aSUlf Magnusson register new symbols.""" 120390c36d8aSUlf Magnusson 120490c36d8aSUlf Magnusson s = s.strip() 120590c36d8aSUlf Magnusson if s == "" or s[0] == "#": 120690c36d8aSUlf Magnusson return _Feed([]) 120790c36d8aSUlf Magnusson 120890c36d8aSUlf Magnusson if for_eval: 120990c36d8aSUlf Magnusson previous = None # The previous token seen 121090c36d8aSUlf Magnusson tokens = [] 121190c36d8aSUlf Magnusson i = 0 # The current index in the string being tokenized 121290c36d8aSUlf Magnusson 121390c36d8aSUlf Magnusson else: 121490c36d8aSUlf Magnusson # The initial word on a line is parsed specially. Let 121590c36d8aSUlf Magnusson # command_chars = [A-Za-z0-9_]. Then 121690c36d8aSUlf Magnusson # - leading non-command_chars characters are ignored, and 121790c36d8aSUlf Magnusson # - the first token consists the following one or more 121890c36d8aSUlf Magnusson # command_chars characters. 121990c36d8aSUlf Magnusson # This is why things like "----help--" are accepted. 122090c36d8aSUlf Magnusson initial_token_match = _initial_token_re_match(s) 122190c36d8aSUlf Magnusson if initial_token_match is None: 122290c36d8aSUlf Magnusson return _Feed([]) 122390c36d8aSUlf Magnusson keyword = _get_keyword(initial_token_match.group(1)) 122490c36d8aSUlf Magnusson if keyword == T_HELP: 122590c36d8aSUlf Magnusson # Avoid junk after "help", e.g. "---", being registered as a 122690c36d8aSUlf Magnusson # symbol 122790c36d8aSUlf Magnusson return _Feed([T_HELP]) 122890c36d8aSUlf Magnusson if keyword is None: 122990c36d8aSUlf Magnusson # We expect a keyword as the first token 123090c36d8aSUlf Magnusson _tokenization_error(s, filename, linenr) 123190c36d8aSUlf Magnusson 123290c36d8aSUlf Magnusson previous = keyword 123390c36d8aSUlf Magnusson tokens = [keyword] 123490c36d8aSUlf Magnusson # The current index in the string being tokenized 123590c36d8aSUlf Magnusson i = initial_token_match.end() 123690c36d8aSUlf Magnusson 123790c36d8aSUlf Magnusson # _tokenize() is a hotspot during parsing, and this speeds things up a 123890c36d8aSUlf Magnusson # bit 123990c36d8aSUlf Magnusson strlen = len(s) 124090c36d8aSUlf Magnusson append = tokens.append 124190c36d8aSUlf Magnusson 124290c36d8aSUlf Magnusson # Main tokenization loop. (Handles tokens past the first one.) 124390c36d8aSUlf Magnusson while i < strlen: 124490c36d8aSUlf Magnusson # Test for an identifier/keyword preceded by whitespace first; this 124590c36d8aSUlf Magnusson # is the most common case. 124690c36d8aSUlf Magnusson id_keyword_match = _id_keyword_re_match(s, i) 124790c36d8aSUlf Magnusson if id_keyword_match: 124890c36d8aSUlf Magnusson # We have an identifier or keyword. The above also stripped any 124990c36d8aSUlf Magnusson # whitespace for us. 125090c36d8aSUlf Magnusson name = id_keyword_match.group(1) 125190c36d8aSUlf Magnusson # Jump past it 125290c36d8aSUlf Magnusson i = id_keyword_match.end() 125390c36d8aSUlf Magnusson 125490c36d8aSUlf Magnusson keyword = _get_keyword(name) 125590c36d8aSUlf Magnusson if keyword is not None: 125690c36d8aSUlf Magnusson # It's a keyword 125790c36d8aSUlf Magnusson append(keyword) 125890c36d8aSUlf Magnusson elif previous in STRING_LEX: 125990c36d8aSUlf Magnusson # What would ordinarily be considered an identifier is 126090c36d8aSUlf Magnusson # treated as a string after certain tokens 126190c36d8aSUlf Magnusson append(name) 126290c36d8aSUlf Magnusson else: 126390c36d8aSUlf Magnusson # It's a symbol name. _sym_lookup() will take care of 126490c36d8aSUlf Magnusson # allocating a new Symbol instance if it's the first time 126590c36d8aSUlf Magnusson # we see it. 126690c36d8aSUlf Magnusson sym = self._sym_lookup(name, for_eval) 126790c36d8aSUlf Magnusson 126890c36d8aSUlf Magnusson if previous == T_CONFIG or previous == T_MENUCONFIG: 126990c36d8aSUlf Magnusson # If the previous token is T_(MENU)CONFIG 127090c36d8aSUlf Magnusson # ("(menu)config"), we're tokenizing the first line of 127190c36d8aSUlf Magnusson # a symbol definition, and should remember this as a 127290c36d8aSUlf Magnusson # location where the symbol is defined 127390c36d8aSUlf Magnusson sym.def_locations.append((filename, linenr)) 127490c36d8aSUlf Magnusson else: 127590c36d8aSUlf Magnusson # Otherwise, it's a reference to the symbol 127690c36d8aSUlf Magnusson sym.ref_locations.append((filename, linenr)) 127790c36d8aSUlf Magnusson 127890c36d8aSUlf Magnusson append(sym) 127990c36d8aSUlf Magnusson 128090c36d8aSUlf Magnusson else: 128190c36d8aSUlf Magnusson # Not an identifier/keyword 128290c36d8aSUlf Magnusson 128390c36d8aSUlf Magnusson while i < strlen and s[i].isspace(): 128490c36d8aSUlf Magnusson i += 1 128590c36d8aSUlf Magnusson if i == strlen: 128690c36d8aSUlf Magnusson break 128790c36d8aSUlf Magnusson c = s[i] 128890c36d8aSUlf Magnusson i += 1 128990c36d8aSUlf Magnusson 129090c36d8aSUlf Magnusson # String literal (constant symbol) 129190c36d8aSUlf Magnusson if c == '"' or c == "'": 129290c36d8aSUlf Magnusson if "\\" in s: 129390c36d8aSUlf Magnusson # Slow path: This could probably be sped up, but it's a 129490c36d8aSUlf Magnusson # very unusual case anyway. 129590c36d8aSUlf Magnusson quote = c 129690c36d8aSUlf Magnusson val = "" 129790c36d8aSUlf Magnusson while 1: 129890c36d8aSUlf Magnusson if i >= len(s): 129990c36d8aSUlf Magnusson _tokenization_error(s, filename, linenr) 130090c36d8aSUlf Magnusson c = s[i] 130190c36d8aSUlf Magnusson if c == quote: 130290c36d8aSUlf Magnusson break 130390c36d8aSUlf Magnusson if c == "\\": 130490c36d8aSUlf Magnusson if i + 1 >= len(s): 130590c36d8aSUlf Magnusson _tokenization_error(s, filename, linenr) 130690c36d8aSUlf Magnusson val += s[i + 1] 130790c36d8aSUlf Magnusson i += 2 130890c36d8aSUlf Magnusson else: 130990c36d8aSUlf Magnusson val += c 131090c36d8aSUlf Magnusson i += 1 131190c36d8aSUlf Magnusson i += 1 131290c36d8aSUlf Magnusson append(val) 131390c36d8aSUlf Magnusson else: 131490c36d8aSUlf Magnusson # Fast path: If the string contains no backslashes 131590c36d8aSUlf Magnusson # (almost always) we can simply look for the matching 131690c36d8aSUlf Magnusson # quote. 131790c36d8aSUlf Magnusson end = s.find(c, i) 131890c36d8aSUlf Magnusson if end == -1: 131990c36d8aSUlf Magnusson _tokenization_error(s, filename, linenr) 132090c36d8aSUlf Magnusson append(s[i:end]) 132190c36d8aSUlf Magnusson i = end + 1 132290c36d8aSUlf Magnusson 132390c36d8aSUlf Magnusson elif c == "&": 132490c36d8aSUlf Magnusson # Invalid characters are ignored 132590c36d8aSUlf Magnusson if i >= len(s) or s[i] != "&": continue 132690c36d8aSUlf Magnusson append(T_AND) 132790c36d8aSUlf Magnusson i += 1 132890c36d8aSUlf Magnusson 132990c36d8aSUlf Magnusson elif c == "|": 133090c36d8aSUlf Magnusson # Invalid characters are ignored 133190c36d8aSUlf Magnusson if i >= len(s) or s[i] != "|": continue 133290c36d8aSUlf Magnusson append(T_OR) 133390c36d8aSUlf Magnusson i += 1 133490c36d8aSUlf Magnusson 133590c36d8aSUlf Magnusson elif c == "!": 133690c36d8aSUlf Magnusson if i < len(s) and s[i] == "=": 133790c36d8aSUlf Magnusson append(T_UNEQUAL) 133890c36d8aSUlf Magnusson i += 1 133990c36d8aSUlf Magnusson else: 134090c36d8aSUlf Magnusson append(T_NOT) 134190c36d8aSUlf Magnusson 134290c36d8aSUlf Magnusson elif c == "=": append(T_EQUAL) 134390c36d8aSUlf Magnusson elif c == "(": append(T_OPEN_PAREN) 134490c36d8aSUlf Magnusson elif c == ")": append(T_CLOSE_PAREN) 134590c36d8aSUlf Magnusson elif c == "#": break # Comment 134690c36d8aSUlf Magnusson 134790c36d8aSUlf Magnusson else: continue # Invalid characters are ignored 134890c36d8aSUlf Magnusson 134990c36d8aSUlf Magnusson previous = tokens[-1] 135090c36d8aSUlf Magnusson 135190c36d8aSUlf Magnusson return _Feed(tokens) 135290c36d8aSUlf Magnusson 135390c36d8aSUlf Magnusson def _sym_lookup(self, name, for_eval=False): 135490c36d8aSUlf Magnusson """Fetches the symbol 'name' from the symbol table, creating and 135590c36d8aSUlf Magnusson registering it if it does not exist. If 'for_eval' is True, the symbol 135690c36d8aSUlf Magnusson won't be added to the symbol table if it does not exist -- this is for 135790c36d8aSUlf Magnusson Config.eval().""" 1358f219e013SMasahiro Yamada if name in self.syms: 1359f219e013SMasahiro Yamada return self.syms[name] 1360f219e013SMasahiro Yamada 1361f219e013SMasahiro Yamada new_sym = Symbol() 1362f219e013SMasahiro Yamada new_sym.config = self 1363f219e013SMasahiro Yamada new_sym.name = name 136490c36d8aSUlf Magnusson if for_eval: 1365f219e013SMasahiro Yamada self._warn("no symbol {0} in configuration".format(name)) 136690c36d8aSUlf Magnusson else: 136790c36d8aSUlf Magnusson self.syms[name] = new_sym 1368f219e013SMasahiro Yamada return new_sym 1369f219e013SMasahiro Yamada 1370f219e013SMasahiro Yamada # 137190c36d8aSUlf Magnusson # Expression evaluation 1372f219e013SMasahiro Yamada # 1373f219e013SMasahiro Yamada 1374f219e013SMasahiro Yamada def _eval_expr(self, expr): 137590c36d8aSUlf Magnusson """Evaluates an expression to "n", "m", or "y".""" 1376f219e013SMasahiro Yamada 137790c36d8aSUlf Magnusson # Handles e.g. an "x if y" condition where the "if y" part is missing. 1378f219e013SMasahiro Yamada if expr is None: 1379f219e013SMasahiro Yamada return "y" 1380f219e013SMasahiro Yamada 138190c36d8aSUlf Magnusson res = self._eval_expr_rec(expr) 138290c36d8aSUlf Magnusson if res == "m": 138390c36d8aSUlf Magnusson # Promote "m" to "y" if we're running without modules. 138490c36d8aSUlf Magnusson # 138590c36d8aSUlf Magnusson # Internally, "m" is often rewritten to "m" && MODULES by both the 138690c36d8aSUlf Magnusson # C implementation and Kconfiglib, which takes care of cases where 138790c36d8aSUlf Magnusson # "m" should be demoted to "n" instead. 138890c36d8aSUlf Magnusson modules_sym = self.syms.get("MODULES") 138990c36d8aSUlf Magnusson if modules_sym is None or modules_sym.get_value() != "y": 139090c36d8aSUlf Magnusson return "y" 139190c36d8aSUlf Magnusson return res 139290c36d8aSUlf Magnusson 139390c36d8aSUlf Magnusson def _eval_expr_rec(self, expr): 1394f219e013SMasahiro Yamada if isinstance(expr, Symbol): 1395f219e013SMasahiro Yamada # Non-bool/tristate symbols are always "n" in a tristate sense, 1396f219e013SMasahiro Yamada # regardless of their value 1397f219e013SMasahiro Yamada if expr.type != BOOL and expr.type != TRISTATE: 1398f219e013SMasahiro Yamada return "n" 1399f219e013SMasahiro Yamada return expr.get_value() 1400f219e013SMasahiro Yamada 1401f219e013SMasahiro Yamada if isinstance(expr, str): 1402f219e013SMasahiro Yamada return expr if (expr == "y" or expr == "m") else "n" 1403f219e013SMasahiro Yamada 140490c36d8aSUlf Magnusson # Ordered by frequency 1405f219e013SMasahiro Yamada 140690c36d8aSUlf Magnusson if expr[0] == AND: 1407f219e013SMasahiro Yamada res = "y" 1408f219e013SMasahiro Yamada for subexpr in expr[1]: 140990c36d8aSUlf Magnusson ev = self._eval_expr_rec(subexpr) 1410f219e013SMasahiro Yamada # Return immediately upon discovering an "n" term 1411f219e013SMasahiro Yamada if ev == "n": 1412f219e013SMasahiro Yamada return "n" 1413f219e013SMasahiro Yamada if ev == "m": 1414f219e013SMasahiro Yamada res = "m" 1415f219e013SMasahiro Yamada # 'res' is either "m" or "y" here; we already handled the 1416f219e013SMasahiro Yamada # short-circuiting "n" case in the loop. 1417f219e013SMasahiro Yamada return res 1418f219e013SMasahiro Yamada 141990c36d8aSUlf Magnusson if expr[0] == NOT: 142090c36d8aSUlf Magnusson ev = self._eval_expr_rec(expr[1]) 1421f219e013SMasahiro Yamada if ev == "y": 1422f219e013SMasahiro Yamada return "n" 1423f219e013SMasahiro Yamada return "y" if (ev == "n") else "m" 1424f219e013SMasahiro Yamada 142590c36d8aSUlf Magnusson if expr[0] == OR: 142690c36d8aSUlf Magnusson res = "n" 142790c36d8aSUlf Magnusson for subexpr in expr[1]: 142890c36d8aSUlf Magnusson ev = self._eval_expr_rec(subexpr) 142990c36d8aSUlf Magnusson # Return immediately upon discovering a "y" term 143090c36d8aSUlf Magnusson if ev == "y": 143190c36d8aSUlf Magnusson return "y" 143290c36d8aSUlf Magnusson if ev == "m": 143390c36d8aSUlf Magnusson res = "m" 143490c36d8aSUlf Magnusson # 'res' is either "n" or "m" here; we already handled the 143590c36d8aSUlf Magnusson # short-circuiting "y" case in the loop. 143690c36d8aSUlf Magnusson return res 1437f219e013SMasahiro Yamada 143890c36d8aSUlf Magnusson if expr[0] == EQUAL: 143990c36d8aSUlf Magnusson return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n" 144090c36d8aSUlf Magnusson 144190c36d8aSUlf Magnusson if expr[0] == UNEQUAL: 144290c36d8aSUlf Magnusson return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n" 1443f219e013SMasahiro Yamada 1444f219e013SMasahiro Yamada _internal_error("Internal error while evaluating expression: " 144590c36d8aSUlf Magnusson "unknown operation {0}.".format(expr[0])) 1446f219e013SMasahiro Yamada 1447f219e013SMasahiro Yamada def _eval_min(self, e1, e2): 144890c36d8aSUlf Magnusson """Returns the minimum value of the two expressions. Equates None with 144990c36d8aSUlf Magnusson 'y'.""" 1450f219e013SMasahiro Yamada e1_eval = self._eval_expr(e1) 1451f219e013SMasahiro Yamada e2_eval = self._eval_expr(e2) 1452f219e013SMasahiro Yamada return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval 1453f219e013SMasahiro Yamada 1454f219e013SMasahiro Yamada def _eval_max(self, e1, e2): 145590c36d8aSUlf Magnusson """Returns the maximum value of the two expressions. Equates None with 145690c36d8aSUlf Magnusson 'y'.""" 1457f219e013SMasahiro Yamada e1_eval = self._eval_expr(e1) 1458f219e013SMasahiro Yamada e2_eval = self._eval_expr(e2) 1459f219e013SMasahiro Yamada return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval 1460f219e013SMasahiro Yamada 1461f219e013SMasahiro Yamada # 146290c36d8aSUlf Magnusson # Dependency tracking (for caching and invalidation) 1463f219e013SMasahiro Yamada # 1464f219e013SMasahiro Yamada 1465f219e013SMasahiro Yamada def _build_dep(self): 1466f219e013SMasahiro Yamada """Populates the Symbol.dep sets, linking the symbol to the symbols 1467f219e013SMasahiro Yamada that immediately depend on it in the sense that changing the value of 1468f219e013SMasahiro Yamada the symbol might affect the values of those other symbols. This is used 1469f219e013SMasahiro Yamada for caching/invalidation purposes. The calculated sets might be larger 1470f219e013SMasahiro Yamada than necessary as we don't do any complicated analysis of the 1471f219e013SMasahiro Yamada expressions.""" 1472f219e013SMasahiro Yamada 1473f219e013SMasahiro Yamada # Adds 'sym' as a directly dependent symbol to all symbols that appear 1474f219e013SMasahiro Yamada # in the expression 'e' 1475f219e013SMasahiro Yamada def add_expr_deps(e, sym): 1476f219e013SMasahiro Yamada for s in _get_expr_syms(e): 1477f219e013SMasahiro Yamada s.dep.add(sym) 1478f219e013SMasahiro Yamada 1479f219e013SMasahiro Yamada # The directly dependent symbols of a symbol are: 1480f219e013SMasahiro Yamada # - Any symbols whose prompts, default values, rev_dep (select 1481f219e013SMasahiro Yamada # condition), or ranges depend on the symbol 1482f219e013SMasahiro Yamada # - Any symbols that belong to the same choice statement as the symbol 1483f219e013SMasahiro Yamada # (these won't be included in 'dep' as that makes the dependency 1484f219e013SMasahiro Yamada # graph unwieldy, but Symbol._get_dependent() will include them) 1485f219e013SMasahiro Yamada # - Any symbols in a choice statement that depends on the symbol 148690c36d8aSUlf Magnusson for sym in self.syms_iter(): 148790c36d8aSUlf Magnusson for _, e in sym.prompts: 1488f219e013SMasahiro Yamada add_expr_deps(e, sym) 1489f219e013SMasahiro Yamada 149090c36d8aSUlf Magnusson for v, e in sym.def_exprs: 1491f219e013SMasahiro Yamada add_expr_deps(v, sym) 1492f219e013SMasahiro Yamada add_expr_deps(e, sym) 1493f219e013SMasahiro Yamada 1494f219e013SMasahiro Yamada add_expr_deps(sym.rev_dep, sym) 1495f219e013SMasahiro Yamada 149690c36d8aSUlf Magnusson for l, u, e in sym.ranges: 1497f219e013SMasahiro Yamada add_expr_deps(l, sym) 1498f219e013SMasahiro Yamada add_expr_deps(u, sym) 1499f219e013SMasahiro Yamada add_expr_deps(e, sym) 1500f219e013SMasahiro Yamada 150190c36d8aSUlf Magnusson if sym.is_choice_sym: 1502f219e013SMasahiro Yamada choice = sym.parent 150390c36d8aSUlf Magnusson for _, e in choice.prompts: 150490c36d8aSUlf Magnusson add_expr_deps(e, sym) 150590c36d8aSUlf Magnusson for _, e in choice.def_exprs: 1506f219e013SMasahiro Yamada add_expr_deps(e, sym) 1507f219e013SMasahiro Yamada 150890c36d8aSUlf Magnusson def _eq_to_sym(self, eq): 150990c36d8aSUlf Magnusson """_expr_depends_on() helper. For (in)equalities of the form sym = y/m 151090c36d8aSUlf Magnusson or sym != n, returns sym. For other (in)equalities, returns None.""" 151190c36d8aSUlf Magnusson relation, left, right = eq 1512f219e013SMasahiro Yamada 151390c36d8aSUlf Magnusson def transform_y_m_n(item): 151490c36d8aSUlf Magnusson if item is self.y: return "y" 151590c36d8aSUlf Magnusson if item is self.m: return "m" 151690c36d8aSUlf Magnusson if item is self.n: return "n" 151790c36d8aSUlf Magnusson return item 151890c36d8aSUlf Magnusson 151990c36d8aSUlf Magnusson left = transform_y_m_n(left) 152090c36d8aSUlf Magnusson right = transform_y_m_n(right) 152190c36d8aSUlf Magnusson 152290c36d8aSUlf Magnusson # Make sure the symbol (if any) appears to the left 152390c36d8aSUlf Magnusson if not isinstance(left, Symbol): 152490c36d8aSUlf Magnusson left, right = right, left 152590c36d8aSUlf Magnusson if not isinstance(left, Symbol): 152690c36d8aSUlf Magnusson return None 152790c36d8aSUlf Magnusson if (relation == EQUAL and (right == "y" or right == "m")) or \ 152890c36d8aSUlf Magnusson (relation == UNEQUAL and right == "n"): 152990c36d8aSUlf Magnusson return left 153090c36d8aSUlf Magnusson return None 153190c36d8aSUlf Magnusson 153290c36d8aSUlf Magnusson def _expr_depends_on(self, expr, sym): 153390c36d8aSUlf Magnusson """Reimplementation of expr_depends_symbol() from mconf.c. Used to 153490c36d8aSUlf Magnusson determine if a submenu should be implicitly created, which influences 153590c36d8aSUlf Magnusson what items inside choice statements are considered choice items.""" 153690c36d8aSUlf Magnusson if expr is None: 153790c36d8aSUlf Magnusson return False 153890c36d8aSUlf Magnusson 153990c36d8aSUlf Magnusson def rec(expr): 154090c36d8aSUlf Magnusson if isinstance(expr, str): 154190c36d8aSUlf Magnusson return False 154290c36d8aSUlf Magnusson if isinstance(expr, Symbol): 154390c36d8aSUlf Magnusson return expr is sym 154490c36d8aSUlf Magnusson 154590c36d8aSUlf Magnusson if expr[0] in (EQUAL, UNEQUAL): 154690c36d8aSUlf Magnusson return self._eq_to_sym(expr) is sym 154790c36d8aSUlf Magnusson if expr[0] == AND: 154890c36d8aSUlf Magnusson for and_expr in expr[1]: 154990c36d8aSUlf Magnusson if rec(and_expr): 155090c36d8aSUlf Magnusson return True 155190c36d8aSUlf Magnusson return False 155290c36d8aSUlf Magnusson 155390c36d8aSUlf Magnusson return rec(expr) 155490c36d8aSUlf Magnusson 155590c36d8aSUlf Magnusson def _invalidate_all(self): 155690c36d8aSUlf Magnusson for sym in self.syms_iter(): 155790c36d8aSUlf Magnusson sym._invalidate() 155890c36d8aSUlf Magnusson 155990c36d8aSUlf Magnusson # 156090c36d8aSUlf Magnusson # Printing and misc. 156190c36d8aSUlf Magnusson # 156290c36d8aSUlf Magnusson 156390c36d8aSUlf Magnusson def _expand_sym_refs(self, s): 156490c36d8aSUlf Magnusson """Expands $-references to symbols in 's' to symbol values, or to the 156590c36d8aSUlf Magnusson empty string for undefined symbols.""" 156690c36d8aSUlf Magnusson 156790c36d8aSUlf Magnusson while 1: 156890c36d8aSUlf Magnusson sym_ref_match = _sym_ref_re_search(s) 156990c36d8aSUlf Magnusson if sym_ref_match is None: 157090c36d8aSUlf Magnusson return s 157190c36d8aSUlf Magnusson 157290c36d8aSUlf Magnusson sym_name = sym_ref_match.group(0)[1:] 157390c36d8aSUlf Magnusson sym = self.syms.get(sym_name) 157490c36d8aSUlf Magnusson expansion = "" if sym is None else sym.get_value() 157590c36d8aSUlf Magnusson 157690c36d8aSUlf Magnusson s = s[:sym_ref_match.start()] + \ 157790c36d8aSUlf Magnusson expansion + \ 157890c36d8aSUlf Magnusson s[sym_ref_match.end():] 157990c36d8aSUlf Magnusson 158090c36d8aSUlf Magnusson def _expr_val_str(self, expr, no_value_str="(none)", 158190c36d8aSUlf Magnusson get_val_instead_of_eval=False): 158290c36d8aSUlf Magnusson """Printing helper. Returns a string with 'expr' and its value. 158390c36d8aSUlf Magnusson 158490c36d8aSUlf Magnusson no_value_str: String to return when 'expr' is missing (None). 158590c36d8aSUlf Magnusson 158690c36d8aSUlf Magnusson get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant 158790c36d8aSUlf Magnusson symbol) and get its value directly instead of evaluating it to a 158890c36d8aSUlf Magnusson tristate value.""" 1589f219e013SMasahiro Yamada 1590f219e013SMasahiro Yamada if expr is None: 1591f219e013SMasahiro Yamada return no_value_str 1592f219e013SMasahiro Yamada 1593f219e013SMasahiro Yamada if get_val_instead_of_eval: 1594f219e013SMasahiro Yamada if isinstance(expr, str): 1595f219e013SMasahiro Yamada return _expr_to_str(expr) 1596f219e013SMasahiro Yamada val = expr.get_value() 1597f219e013SMasahiro Yamada else: 1598f219e013SMasahiro Yamada val = self._eval_expr(expr) 1599f219e013SMasahiro Yamada 1600f219e013SMasahiro Yamada return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val)) 1601f219e013SMasahiro Yamada 1602f219e013SMasahiro Yamada def _get_sym_or_choice_str(self, sc): 1603f219e013SMasahiro Yamada """Symbols and choices have many properties in common, so we factor out 1604f219e013SMasahiro Yamada common __str__() stuff here. "sc" is short for "symbol or choice".""" 1605f219e013SMasahiro Yamada 1606f219e013SMasahiro Yamada # As we deal a lot with string representations here, use some 1607f219e013SMasahiro Yamada # convenient shorthand: 1608f219e013SMasahiro Yamada s = _expr_to_str 1609f219e013SMasahiro Yamada 1610f219e013SMasahiro Yamada # 1611f219e013SMasahiro Yamada # Common symbol/choice properties 1612f219e013SMasahiro Yamada # 1613f219e013SMasahiro Yamada 161490c36d8aSUlf Magnusson user_val_str = "(no user value)" if sc.user_val is None else \ 161590c36d8aSUlf Magnusson s(sc.user_val) 1616f219e013SMasahiro Yamada 1617f219e013SMasahiro Yamada # Build prompts string 161890c36d8aSUlf Magnusson if not sc.prompts: 1619f219e013SMasahiro Yamada prompts_str = " (no prompts)" 1620f219e013SMasahiro Yamada else: 1621f219e013SMasahiro Yamada prompts_str_rows = [] 162290c36d8aSUlf Magnusson for prompt, cond_expr in sc.orig_prompts: 1623f219e013SMasahiro Yamada if cond_expr is None: 1624f219e013SMasahiro Yamada prompts_str_rows.append(' "{0}"'.format(prompt)) 1625f219e013SMasahiro Yamada else: 162690c36d8aSUlf Magnusson prompts_str_rows.append( 162790c36d8aSUlf Magnusson ' "{0}" if {1}'.format(prompt, 162890c36d8aSUlf Magnusson self._expr_val_str(cond_expr))) 1629f219e013SMasahiro Yamada prompts_str = "\n".join(prompts_str_rows) 1630f219e013SMasahiro Yamada 1631f219e013SMasahiro Yamada # Build locations string 163290c36d8aSUlf Magnusson if not sc.def_locations: 1633f219e013SMasahiro Yamada locations_str = "(no locations)" 1634f219e013SMasahiro Yamada else: 1635f219e013SMasahiro Yamada locations_str = " ".join(["{0}:{1}".format(filename, linenr) for 1636f219e013SMasahiro Yamada (filename, linenr) in sc.def_locations]) 1637f219e013SMasahiro Yamada 163890c36d8aSUlf Magnusson # Build additional-dependencies-from-menus-and-ifs string 163990c36d8aSUlf Magnusson additional_deps_str = " " + \ 164090c36d8aSUlf Magnusson self._expr_val_str(sc.deps_from_containing, 1641f219e013SMasahiro Yamada "(no additional dependencies)") 1642f219e013SMasahiro Yamada 1643f219e013SMasahiro Yamada # 1644f219e013SMasahiro Yamada # Symbol-specific stuff 1645f219e013SMasahiro Yamada # 1646f219e013SMasahiro Yamada 1647f219e013SMasahiro Yamada if isinstance(sc, Symbol): 1648f219e013SMasahiro Yamada # Build ranges string 1649f219e013SMasahiro Yamada if isinstance(sc, Symbol): 165090c36d8aSUlf Magnusson if not sc.ranges: 1651f219e013SMasahiro Yamada ranges_str = " (no ranges)" 1652f219e013SMasahiro Yamada else: 1653f219e013SMasahiro Yamada ranges_str_rows = [] 165490c36d8aSUlf Magnusson for l, u, cond_expr in sc.ranges: 1655f219e013SMasahiro Yamada if cond_expr is None: 165690c36d8aSUlf Magnusson ranges_str_rows.append(" [{0}, {1}]".format(s(l), 165790c36d8aSUlf Magnusson s(u))) 1658f219e013SMasahiro Yamada else: 1659f219e013SMasahiro Yamada ranges_str_rows.append(" [{0}, {1}] if {2}" 166090c36d8aSUlf Magnusson .format(s(l), s(u), 166190c36d8aSUlf Magnusson self._expr_val_str(cond_expr))) 1662f219e013SMasahiro Yamada ranges_str = "\n".join(ranges_str_rows) 1663f219e013SMasahiro Yamada 1664f219e013SMasahiro Yamada # Build default values string 166590c36d8aSUlf Magnusson if not sc.def_exprs: 1666f219e013SMasahiro Yamada defaults_str = " (no default values)" 1667f219e013SMasahiro Yamada else: 1668f219e013SMasahiro Yamada defaults_str_rows = [] 166990c36d8aSUlf Magnusson for val_expr, cond_expr in sc.orig_def_exprs: 167090c36d8aSUlf Magnusson row_str = " " + self._expr_val_str(val_expr, "(none)", 167190c36d8aSUlf Magnusson sc.type == STRING) 1672f219e013SMasahiro Yamada defaults_str_rows.append(row_str) 167390c36d8aSUlf Magnusson defaults_str_rows.append(" Condition: " + 167490c36d8aSUlf Magnusson self._expr_val_str(cond_expr)) 1675f219e013SMasahiro Yamada defaults_str = "\n".join(defaults_str_rows) 1676f219e013SMasahiro Yamada 1677f219e013SMasahiro Yamada # Build selects string 167890c36d8aSUlf Magnusson if not sc.orig_selects: 1679f219e013SMasahiro Yamada selects_str = " (no selects)" 1680f219e013SMasahiro Yamada else: 1681f219e013SMasahiro Yamada selects_str_rows = [] 168290c36d8aSUlf Magnusson for target, cond_expr in sc.orig_selects: 1683f219e013SMasahiro Yamada if cond_expr is None: 1684f219e013SMasahiro Yamada selects_str_rows.append(" {0}".format(target.name)) 1685f219e013SMasahiro Yamada else: 168690c36d8aSUlf Magnusson selects_str_rows.append( 168790c36d8aSUlf Magnusson " {0} if {1}".format(target.name, 168890c36d8aSUlf Magnusson self._expr_val_str(cond_expr))) 1689f219e013SMasahiro Yamada selects_str = "\n".join(selects_str_rows) 1690f219e013SMasahiro Yamada 169190c36d8aSUlf Magnusson res = _lines("Symbol " + 169290c36d8aSUlf Magnusson ("(no name)" if sc.name is None else sc.name), 169390c36d8aSUlf Magnusson "Type : " + TYPENAME[sc.type], 169490c36d8aSUlf Magnusson "Value : " + s(sc.get_value()), 169590c36d8aSUlf Magnusson "User value : " + user_val_str, 169690c36d8aSUlf Magnusson "Visibility : " + s(_get_visibility(sc)), 169790c36d8aSUlf Magnusson "Is choice item : " + BOOL_STR[sc.is_choice_sym], 169890c36d8aSUlf Magnusson "Is defined : " + BOOL_STR[sc.is_defined_], 169990c36d8aSUlf Magnusson "Is from env. : " + BOOL_STR[sc.is_from_env], 170090c36d8aSUlf Magnusson "Is special : " + BOOL_STR[sc.is_special_] + "\n") 170190c36d8aSUlf Magnusson if sc.ranges: 170290c36d8aSUlf Magnusson res += _lines("Ranges:", ranges_str + "\n") 170390c36d8aSUlf Magnusson res += _lines("Prompts:", 1704f219e013SMasahiro Yamada prompts_str, 1705f219e013SMasahiro Yamada "Default values:", 1706f219e013SMasahiro Yamada defaults_str, 1707f219e013SMasahiro Yamada "Selects:", 1708f219e013SMasahiro Yamada selects_str, 170990c36d8aSUlf Magnusson "Reverse (select-related) dependencies:", 171090c36d8aSUlf Magnusson " (no reverse dependencies)" if sc.rev_dep == "n" 171190c36d8aSUlf Magnusson else " " + self._expr_val_str(sc.rev_dep), 171290c36d8aSUlf Magnusson "Additional dependencies from enclosing menus " 171390c36d8aSUlf Magnusson "and ifs:", 1714f219e013SMasahiro Yamada additional_deps_str, 1715f219e013SMasahiro Yamada "Locations: " + locations_str) 1716f219e013SMasahiro Yamada 1717f219e013SMasahiro Yamada return res 1718f219e013SMasahiro Yamada 1719f219e013SMasahiro Yamada # 1720f219e013SMasahiro Yamada # Choice-specific stuff 1721f219e013SMasahiro Yamada # 1722f219e013SMasahiro Yamada 1723f219e013SMasahiro Yamada # Build selected symbol string 1724f219e013SMasahiro Yamada sel = sc.get_selection() 172590c36d8aSUlf Magnusson sel_str = "(no selection)" if sel is None else sel.name 1726f219e013SMasahiro Yamada 1727f219e013SMasahiro Yamada # Build default values string 172890c36d8aSUlf Magnusson if not sc.def_exprs: 1729f219e013SMasahiro Yamada defaults_str = " (no default values)" 1730f219e013SMasahiro Yamada else: 1731f219e013SMasahiro Yamada defaults_str_rows = [] 173290c36d8aSUlf Magnusson for sym, cond_expr in sc.orig_def_exprs: 1733f219e013SMasahiro Yamada if cond_expr is None: 1734f219e013SMasahiro Yamada defaults_str_rows.append(" {0}".format(sym.name)) 1735f219e013SMasahiro Yamada else: 173690c36d8aSUlf Magnusson defaults_str_rows.append(" {0} if {1}".format(sym.name, 173790c36d8aSUlf Magnusson self._expr_val_str(cond_expr))) 1738f219e013SMasahiro Yamada defaults_str = "\n".join(defaults_str_rows) 1739f219e013SMasahiro Yamada 1740f219e013SMasahiro Yamada # Build contained symbols string 174190c36d8aSUlf Magnusson names = [sym.name for sym in sc.actual_symbols] 174290c36d8aSUlf Magnusson syms_string = " ".join(names) if names else "(empty)" 1743f219e013SMasahiro Yamada 174490c36d8aSUlf Magnusson return _lines("Choice", 174590c36d8aSUlf Magnusson "Name (for named choices): " + 174690c36d8aSUlf Magnusson ("(no name)" if sc.name is None else sc.name), 174790c36d8aSUlf Magnusson "Type : " + TYPENAME[sc.type], 1748f219e013SMasahiro Yamada "Selected symbol : " + sel_str, 174990c36d8aSUlf Magnusson "User value : " + user_val_str, 175090c36d8aSUlf Magnusson "Mode : " + s(sc.get_mode()), 175190c36d8aSUlf Magnusson "Visibility : " + s(_get_visibility(sc)), 175290c36d8aSUlf Magnusson "Optional : " + BOOL_STR[sc.optional], 1753f219e013SMasahiro Yamada "Prompts:", 1754f219e013SMasahiro Yamada prompts_str, 1755f219e013SMasahiro Yamada "Defaults:", 1756f219e013SMasahiro Yamada defaults_str, 1757f219e013SMasahiro Yamada "Choice symbols:", 1758f219e013SMasahiro Yamada " " + syms_string, 175990c36d8aSUlf Magnusson "Additional dependencies from enclosing menus and " 176090c36d8aSUlf Magnusson "ifs:", 1761f219e013SMasahiro Yamada additional_deps_str, 1762f219e013SMasahiro Yamada "Locations: " + locations_str) 1763f219e013SMasahiro Yamada 1764f219e013SMasahiro Yamada def _warn(self, msg, filename=None, linenr=None): 1765f219e013SMasahiro Yamada """For printing warnings to stderr.""" 1766f219e013SMasahiro Yamada if self.print_warnings: 176790c36d8aSUlf Magnusson _stderr_msg("warning: " + msg, filename, linenr) 1768f219e013SMasahiro Yamada 176990c36d8aSUlf Magnussonclass Item(object): 1770f219e013SMasahiro Yamada 1771f219e013SMasahiro Yamada """Base class for symbols and other Kconfig constructs. Subclasses are 1772f219e013SMasahiro Yamada Symbol, Choice, Menu, and Comment.""" 1773f219e013SMasahiro Yamada 1774f219e013SMasahiro Yamada def is_symbol(self): 177590c36d8aSUlf Magnusson """Returns True if the item is a symbol. Short for 1776f219e013SMasahiro Yamada isinstance(item, kconfiglib.Symbol).""" 1777f219e013SMasahiro Yamada return isinstance(self, Symbol) 1778f219e013SMasahiro Yamada 1779f219e013SMasahiro Yamada def is_choice(self): 178090c36d8aSUlf Magnusson """Returns True if the item is a choice. Short for 1781f219e013SMasahiro Yamada isinstance(item, kconfiglib.Choice).""" 1782f219e013SMasahiro Yamada return isinstance(self, Choice) 1783f219e013SMasahiro Yamada 1784f219e013SMasahiro Yamada def is_menu(self): 178590c36d8aSUlf Magnusson """Returns True if the item is a menu. Short for 1786f219e013SMasahiro Yamada isinstance(item, kconfiglib.Menu).""" 1787f219e013SMasahiro Yamada return isinstance(self, Menu) 1788f219e013SMasahiro Yamada 1789f219e013SMasahiro Yamada def is_comment(self): 179090c36d8aSUlf Magnusson """Returns True if the item is a comment. Short for 1791f219e013SMasahiro Yamada isinstance(item, kconfiglib.Comment).""" 1792f219e013SMasahiro Yamada return isinstance(self, Comment) 1793f219e013SMasahiro Yamada 179490c36d8aSUlf Magnussonclass Symbol(Item): 1795f219e013SMasahiro Yamada 1796f219e013SMasahiro Yamada """Represents a configuration symbol - e.g. FOO for 1797f219e013SMasahiro Yamada 1798f219e013SMasahiro Yamada config FOO 1799f219e013SMasahiro Yamada ...""" 1800f219e013SMasahiro Yamada 1801f219e013SMasahiro Yamada # 1802f219e013SMasahiro Yamada # Public interface 1803f219e013SMasahiro Yamada # 1804f219e013SMasahiro Yamada 180590c36d8aSUlf Magnusson def get_config(self): 180690c36d8aSUlf Magnusson """Returns the Config instance this symbol is from.""" 180790c36d8aSUlf Magnusson return self.config 180890c36d8aSUlf Magnusson 180990c36d8aSUlf Magnusson def get_name(self): 181090c36d8aSUlf Magnusson """Returns the name of the symbol.""" 181190c36d8aSUlf Magnusson return self.name 181290c36d8aSUlf Magnusson 181390c36d8aSUlf Magnusson def get_type(self): 181490c36d8aSUlf Magnusson """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE, 181590c36d8aSUlf Magnusson STRING, HEX, or INT. These are defined at the top level of the module, 181690c36d8aSUlf Magnusson so you'd do something like 181790c36d8aSUlf Magnusson 181890c36d8aSUlf Magnusson if sym.get_type() == kconfiglib.STRING: 181990c36d8aSUlf Magnusson ...""" 182090c36d8aSUlf Magnusson return self.type 182190c36d8aSUlf Magnusson 182290c36d8aSUlf Magnusson def get_prompts(self): 182390c36d8aSUlf Magnusson """Returns a list of prompts defined for the symbol, in the order they 182490c36d8aSUlf Magnusson appear in the configuration files. Returns the empty list for symbols 182590c36d8aSUlf Magnusson with no prompt. 182690c36d8aSUlf Magnusson 182790c36d8aSUlf Magnusson This list will have a single entry for the vast majority of symbols 182890c36d8aSUlf Magnusson having prompts, but having multiple prompts for a single symbol is 182990c36d8aSUlf Magnusson possible through having multiple 'config' entries for it.""" 183090c36d8aSUlf Magnusson return [prompt for prompt, _ in self.orig_prompts] 183190c36d8aSUlf Magnusson 183290c36d8aSUlf Magnusson def get_help(self): 183390c36d8aSUlf Magnusson """Returns the help text of the symbol, or None if the symbol has no 183490c36d8aSUlf Magnusson help text.""" 183590c36d8aSUlf Magnusson return self.help 183690c36d8aSUlf Magnusson 183790c36d8aSUlf Magnusson def get_parent(self): 183890c36d8aSUlf Magnusson """Returns the menu or choice statement that contains the symbol, or 183990c36d8aSUlf Magnusson None if the symbol is at the top level. Note that if statements are 184090c36d8aSUlf Magnusson treated as syntactic and do not have an explicit class 184190c36d8aSUlf Magnusson representation.""" 184290c36d8aSUlf Magnusson return self.parent 184390c36d8aSUlf Magnusson 184490c36d8aSUlf Magnusson def get_def_locations(self): 184590c36d8aSUlf Magnusson """Returns a list of (filename, linenr) tuples, where filename (string) 184690c36d8aSUlf Magnusson and linenr (int) represent a location where the symbol is defined. For 184790c36d8aSUlf Magnusson the vast majority of symbols this list will only contain one element. 184890c36d8aSUlf Magnusson For the following Kconfig, FOO would get two entries: the lines marked 184990c36d8aSUlf Magnusson with *. 185090c36d8aSUlf Magnusson 185190c36d8aSUlf Magnusson config FOO * 185290c36d8aSUlf Magnusson bool "foo prompt 1" 185390c36d8aSUlf Magnusson 185490c36d8aSUlf Magnusson config FOO * 185590c36d8aSUlf Magnusson bool "foo prompt 2" 185690c36d8aSUlf Magnusson """ 185790c36d8aSUlf Magnusson return self.def_locations 185890c36d8aSUlf Magnusson 185990c36d8aSUlf Magnusson def get_ref_locations(self): 186090c36d8aSUlf Magnusson """Returns a list of (filename, linenr) tuples, where filename (string) 186190c36d8aSUlf Magnusson and linenr (int) represent a location where the symbol is referenced in 186290c36d8aSUlf Magnusson the configuration. For example, the lines marked by * would be included 186390c36d8aSUlf Magnusson for FOO below: 186490c36d8aSUlf Magnusson 186590c36d8aSUlf Magnusson config A 186690c36d8aSUlf Magnusson bool 186790c36d8aSUlf Magnusson default BAR || FOO * 186890c36d8aSUlf Magnusson 186990c36d8aSUlf Magnusson config B 187090c36d8aSUlf Magnusson tristate 187190c36d8aSUlf Magnusson depends on FOO * 187290c36d8aSUlf Magnusson default m if FOO * 187390c36d8aSUlf Magnusson 187490c36d8aSUlf Magnusson if FOO * 187590c36d8aSUlf Magnusson config A 187690c36d8aSUlf Magnusson bool "A" 187790c36d8aSUlf Magnusson endif 187890c36d8aSUlf Magnusson 187990c36d8aSUlf Magnusson config FOO (definition not included) 188090c36d8aSUlf Magnusson bool 188190c36d8aSUlf Magnusson """ 188290c36d8aSUlf Magnusson return self.ref_locations 188390c36d8aSUlf Magnusson 1884f219e013SMasahiro Yamada def get_value(self): 1885f219e013SMasahiro Yamada """Calculate and return the value of the symbol. See also 1886f219e013SMasahiro Yamada Symbol.set_user_value().""" 1887f219e013SMasahiro Yamada 188890c36d8aSUlf Magnusson if self.cached_val is not None: 188990c36d8aSUlf Magnusson return self.cached_val 1890f219e013SMasahiro Yamada 1891f219e013SMasahiro Yamada # As a quirk of Kconfig, undefined symbols get their name as their 1892f219e013SMasahiro Yamada # value. This is why things like "FOO = bar" work for seeing if FOO has 1893f219e013SMasahiro Yamada # the value "bar". 1894f219e013SMasahiro Yamada if self.type == UNKNOWN: 189590c36d8aSUlf Magnusson self.cached_val = self.name 1896f219e013SMasahiro Yamada return self.name 1897f219e013SMasahiro Yamada 189890c36d8aSUlf Magnusson new_val = DEFAULT_VALUE[self.type] 189990c36d8aSUlf Magnusson vis = _get_visibility(self) 1900f219e013SMasahiro Yamada 190190c36d8aSUlf Magnusson # This is easiest to calculate together with the value 190290c36d8aSUlf Magnusson self.write_to_conf = False 1903f219e013SMasahiro Yamada 1904f219e013SMasahiro Yamada if self.type == BOOL or self.type == TRISTATE: 1905f219e013SMasahiro Yamada # The visibility and mode (modules-only or single-selection) of 190690c36d8aSUlf Magnusson # choice items will be taken into account in _get_visibility() 190790c36d8aSUlf Magnusson if self.is_choice_sym: 1908f219e013SMasahiro Yamada if vis != "n": 1909f219e013SMasahiro Yamada choice = self.parent 1910f219e013SMasahiro Yamada mode = choice.get_mode() 1911f219e013SMasahiro Yamada 1912f219e013SMasahiro Yamada self.write_to_conf = (mode != "n") 1913f219e013SMasahiro Yamada 1914f219e013SMasahiro Yamada if mode == "y": 191590c36d8aSUlf Magnusson if choice.get_selection() is self: 191690c36d8aSUlf Magnusson new_val = "y" 191790c36d8aSUlf Magnusson else: 191890c36d8aSUlf Magnusson new_val = "n" 1919f219e013SMasahiro Yamada elif mode == "m": 1920f219e013SMasahiro Yamada if self.user_val == "m" or self.user_val == "y": 1921f219e013SMasahiro Yamada new_val = "m" 1922f219e013SMasahiro Yamada 1923f219e013SMasahiro Yamada else: 192490c36d8aSUlf Magnusson # If the symbol is visible and has a user value, use that. 192590c36d8aSUlf Magnusson # Otherwise, look at defaults. 1926f219e013SMasahiro Yamada use_defaults = True 1927f219e013SMasahiro Yamada 1928f219e013SMasahiro Yamada if vis != "n": 1929f219e013SMasahiro Yamada self.write_to_conf = True 1930f219e013SMasahiro Yamada if self.user_val is not None: 1931f219e013SMasahiro Yamada new_val = self.config._eval_min(self.user_val, vis) 1932f219e013SMasahiro Yamada use_defaults = False 1933f219e013SMasahiro Yamada 1934f219e013SMasahiro Yamada if use_defaults: 193590c36d8aSUlf Magnusson for val_expr, cond_expr in self.def_exprs: 1936f219e013SMasahiro Yamada cond_eval = self.config._eval_expr(cond_expr) 1937f219e013SMasahiro Yamada if cond_eval != "n": 1938f219e013SMasahiro Yamada self.write_to_conf = True 193990c36d8aSUlf Magnusson new_val = self.config._eval_min(val_expr, 194090c36d8aSUlf Magnusson cond_eval) 1941f219e013SMasahiro Yamada break 1942f219e013SMasahiro Yamada 194390c36d8aSUlf Magnusson # Reverse (select-related) dependencies take precedence 1944f219e013SMasahiro Yamada rev_dep_val = self.config._eval_expr(self.rev_dep) 1945f219e013SMasahiro Yamada if rev_dep_val != "n": 1946f219e013SMasahiro Yamada self.write_to_conf = True 1947f219e013SMasahiro Yamada new_val = self.config._eval_max(new_val, rev_dep_val) 1948f219e013SMasahiro Yamada 1949f219e013SMasahiro Yamada # Promote "m" to "y" for booleans 1950f219e013SMasahiro Yamada if new_val == "m" and self.type == BOOL: 1951f219e013SMasahiro Yamada new_val = "y" 1952f219e013SMasahiro Yamada 195390c36d8aSUlf Magnusson elif self.type == INT or self.type == HEX: 1954f219e013SMasahiro Yamada has_active_range = False 1955f219e013SMasahiro Yamada low = None 1956f219e013SMasahiro Yamada high = None 1957f219e013SMasahiro Yamada use_defaults = True 1958f219e013SMasahiro Yamada 1959f219e013SMasahiro Yamada base = 16 if self.type == HEX else 10 1960f219e013SMasahiro Yamada 196190c36d8aSUlf Magnusson for l, h, cond_expr in self.ranges: 1962f219e013SMasahiro Yamada if self.config._eval_expr(cond_expr) != "n": 1963f219e013SMasahiro Yamada has_active_range = True 1964f219e013SMasahiro Yamada 196590c36d8aSUlf Magnusson low_str = _str_val(l) 196690c36d8aSUlf Magnusson high_str = _str_val(h) 1967f219e013SMasahiro Yamada low = int(low_str, base) if \ 1968f219e013SMasahiro Yamada _is_base_n(low_str, base) else 0 1969f219e013SMasahiro Yamada high = int(high_str, base) if \ 1970f219e013SMasahiro Yamada _is_base_n(high_str, base) else 0 1971f219e013SMasahiro Yamada 1972f219e013SMasahiro Yamada break 1973f219e013SMasahiro Yamada 1974f219e013SMasahiro Yamada if vis != "n": 1975f219e013SMasahiro Yamada self.write_to_conf = True 1976f219e013SMasahiro Yamada 1977f219e013SMasahiro Yamada if self.user_val is not None and \ 1978f219e013SMasahiro Yamada _is_base_n(self.user_val, base) and \ 1979f219e013SMasahiro Yamada (not has_active_range or 1980f219e013SMasahiro Yamada low <= int(self.user_val, base) <= high): 1981f219e013SMasahiro Yamada 1982f219e013SMasahiro Yamada # If the user value is OK, it is stored in exactly the same 1983f219e013SMasahiro Yamada # form as specified in the assignment (with or without 1984f219e013SMasahiro Yamada # "0x", etc). 1985f219e013SMasahiro Yamada 1986f219e013SMasahiro Yamada use_defaults = False 1987f219e013SMasahiro Yamada new_val = self.user_val 1988f219e013SMasahiro Yamada 1989f219e013SMasahiro Yamada if use_defaults: 199090c36d8aSUlf Magnusson for val_expr, cond_expr in self.def_exprs: 1991f219e013SMasahiro Yamada if self.config._eval_expr(cond_expr) != "n": 1992f219e013SMasahiro Yamada self.write_to_conf = True 1993f219e013SMasahiro Yamada 1994f219e013SMasahiro Yamada # If the default value is OK, it is stored in exactly 1995f219e013SMasahiro Yamada # the same form as specified. Otherwise, it is clamped 1996f219e013SMasahiro Yamada # to the range, and the output has "0x" as appropriate 1997f219e013SMasahiro Yamada # for the type. 1998f219e013SMasahiro Yamada 199990c36d8aSUlf Magnusson new_val = _str_val(val_expr) 2000f219e013SMasahiro Yamada 2001f219e013SMasahiro Yamada if _is_base_n(new_val, base): 2002f219e013SMasahiro Yamada new_val_num = int(new_val, base) 2003f219e013SMasahiro Yamada if has_active_range: 2004f219e013SMasahiro Yamada clamped_val = None 2005f219e013SMasahiro Yamada 2006f219e013SMasahiro Yamada if new_val_num < low: 2007f219e013SMasahiro Yamada clamped_val = low 2008f219e013SMasahiro Yamada elif new_val_num > high: 2009f219e013SMasahiro Yamada clamped_val = high 2010f219e013SMasahiro Yamada 2011f219e013SMasahiro Yamada if clamped_val is not None: 2012f219e013SMasahiro Yamada new_val = (hex(clamped_val) if \ 2013f219e013SMasahiro Yamada self.type == HEX else str(clamped_val)) 2014f219e013SMasahiro Yamada 2015f219e013SMasahiro Yamada break 2016f219e013SMasahiro Yamada else: # For the for loop 2017f219e013SMasahiro Yamada # If no user value or default kicks in but the hex/int has 2018f219e013SMasahiro Yamada # an active range, then the low end of the range is used, 2019f219e013SMasahiro Yamada # provided it's > 0, with "0x" prepended as appropriate. 2020f219e013SMasahiro Yamada if has_active_range and low > 0: 2021f219e013SMasahiro Yamada new_val = (hex(low) if self.type == HEX else str(low)) 2022f219e013SMasahiro Yamada 202390c36d8aSUlf Magnusson elif self.type == STRING: 202490c36d8aSUlf Magnusson use_defaults = True 202590c36d8aSUlf Magnusson 202690c36d8aSUlf Magnusson if vis != "n": 202790c36d8aSUlf Magnusson self.write_to_conf = True 202890c36d8aSUlf Magnusson if self.user_val is not None: 202990c36d8aSUlf Magnusson new_val = self.user_val 203090c36d8aSUlf Magnusson use_defaults = False 203190c36d8aSUlf Magnusson 203290c36d8aSUlf Magnusson if use_defaults: 203390c36d8aSUlf Magnusson for val_expr, cond_expr in self.def_exprs: 203490c36d8aSUlf Magnusson if self.config._eval_expr(cond_expr) != "n": 203590c36d8aSUlf Magnusson self.write_to_conf = True 203690c36d8aSUlf Magnusson new_val = _str_val(val_expr) 203790c36d8aSUlf Magnusson break 203890c36d8aSUlf Magnusson 203990c36d8aSUlf Magnusson self.cached_val = new_val 2040f219e013SMasahiro Yamada return new_val 2041f219e013SMasahiro Yamada 2042f219e013SMasahiro Yamada def get_user_value(self): 2043f219e013SMasahiro Yamada """Returns the value assigned to the symbol in a .config or via 204490c36d8aSUlf Magnusson Symbol.set_user_value() (provided the value was valid for the type of 204590c36d8aSUlf Magnusson the symbol). Returns None in case of no user value.""" 2046f219e013SMasahiro Yamada return self.user_val 2047f219e013SMasahiro Yamada 2048f219e013SMasahiro Yamada def get_upper_bound(self): 2049f219e013SMasahiro Yamada """For string/hex/int symbols and for bool and tristate symbols that 2050f219e013SMasahiro Yamada cannot be modified (see is_modifiable()), returns None. 2051f219e013SMasahiro Yamada 2052f219e013SMasahiro Yamada Otherwise, returns the highest value the symbol can be set to with 205390c36d8aSUlf Magnusson Symbol.set_user_value() (that will not be truncated): one of "m" or 205490c36d8aSUlf Magnusson "y", arranged from lowest to highest. This corresponds to the highest 205590c36d8aSUlf Magnusson value the symbol could be given in e.g. the 'make menuconfig' 205690c36d8aSUlf Magnusson interface. 2057f219e013SMasahiro Yamada 2058f219e013SMasahiro Yamada See also the tri_less*() and tri_greater*() functions, which could come 2059f219e013SMasahiro Yamada in handy.""" 2060f219e013SMasahiro Yamada if self.type != BOOL and self.type != TRISTATE: 2061f219e013SMasahiro Yamada return None 2062f219e013SMasahiro Yamada rev_dep = self.config._eval_expr(self.rev_dep) 206390c36d8aSUlf Magnusson # A bool selected to "m" gets promoted to "y", pinning it 206490c36d8aSUlf Magnusson if rev_dep == "m" and self.type == BOOL: 206590c36d8aSUlf Magnusson return None 206690c36d8aSUlf Magnusson vis = _get_visibility(self) 206790c36d8aSUlf Magnusson if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]: 2068f219e013SMasahiro Yamada return vis 2069f219e013SMasahiro Yamada return None 2070f219e013SMasahiro Yamada 2071f219e013SMasahiro Yamada def get_lower_bound(self): 2072f219e013SMasahiro Yamada """For string/hex/int symbols and for bool and tristate symbols that 2073f219e013SMasahiro Yamada cannot be modified (see is_modifiable()), returns None. 2074f219e013SMasahiro Yamada 2075f219e013SMasahiro Yamada Otherwise, returns the lowest value the symbol can be set to with 207690c36d8aSUlf Magnusson Symbol.set_user_value() (that will not be truncated): one of "n" or 207790c36d8aSUlf Magnusson "m", arranged from lowest to highest. This corresponds to the lowest 207890c36d8aSUlf Magnusson value the symbol could be given in e.g. the 'make menuconfig' 207990c36d8aSUlf Magnusson interface. 2080f219e013SMasahiro Yamada 2081f219e013SMasahiro Yamada See also the tri_less*() and tri_greater*() functions, which could come 2082f219e013SMasahiro Yamada in handy.""" 2083f219e013SMasahiro Yamada if self.type != BOOL and self.type != TRISTATE: 2084f219e013SMasahiro Yamada return None 2085f219e013SMasahiro Yamada rev_dep = self.config._eval_expr(self.rev_dep) 208690c36d8aSUlf Magnusson # A bool selected to "m" gets promoted to "y", pinning it 208790c36d8aSUlf Magnusson if rev_dep == "m" and self.type == BOOL: 208890c36d8aSUlf Magnusson return None 208990c36d8aSUlf Magnusson if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]: 2090f219e013SMasahiro Yamada return rev_dep 2091f219e013SMasahiro Yamada return None 2092f219e013SMasahiro Yamada 2093f219e013SMasahiro Yamada def get_assignable_values(self): 2094f219e013SMasahiro Yamada """For string/hex/int symbols and for bool and tristate symbols that 2095f219e013SMasahiro Yamada cannot be modified (see is_modifiable()), returns the empty list. 2096f219e013SMasahiro Yamada 2097f219e013SMasahiro Yamada Otherwise, returns a list containing the user values that can be 2098f219e013SMasahiro Yamada assigned to the symbol (that won't be truncated). Usage example: 2099f219e013SMasahiro Yamada 2100f219e013SMasahiro Yamada if "m" in sym.get_assignable_values(): 2101f219e013SMasahiro Yamada sym.set_user_value("m") 2102f219e013SMasahiro Yamada 2103f219e013SMasahiro Yamada This is basically a more convenient interface to 2104f219e013SMasahiro Yamada get_lower/upper_bound() when wanting to test if a particular tristate 2105f219e013SMasahiro Yamada value can be assigned.""" 2106f219e013SMasahiro Yamada if self.type != BOOL and self.type != TRISTATE: 2107f219e013SMasahiro Yamada return [] 2108f219e013SMasahiro Yamada rev_dep = self.config._eval_expr(self.rev_dep) 210990c36d8aSUlf Magnusson # A bool selected to "m" gets promoted to "y", pinning it 211090c36d8aSUlf Magnusson if rev_dep == "m" and self.type == BOOL: 211190c36d8aSUlf Magnusson return [] 211290c36d8aSUlf Magnusson res = ["n", "m", "y"][TRI_TO_INT[rev_dep] : 211390c36d8aSUlf Magnusson TRI_TO_INT[_get_visibility(self)] + 1] 2114f219e013SMasahiro Yamada return res if len(res) > 1 else [] 2115f219e013SMasahiro Yamada 2116f219e013SMasahiro Yamada def get_visibility(self): 2117f219e013SMasahiro Yamada """Returns the visibility of the symbol: one of "n", "m" or "y". For 2118f219e013SMasahiro Yamada bool and tristate symbols, this is an upper bound on the value users 2119f219e013SMasahiro Yamada can set for the symbol. For other types of symbols, a visibility of "n" 2120f219e013SMasahiro Yamada means the user value will be ignored. A visibility of "n" corresponds 2121f219e013SMasahiro Yamada to not being visible in the 'make *config' interfaces. 2122f219e013SMasahiro Yamada 2123f219e013SMasahiro Yamada Example (assuming we're running with modules enabled -- i.e., MODULES 2124f219e013SMasahiro Yamada set to 'y'): 2125f219e013SMasahiro Yamada 2126f219e013SMasahiro Yamada # Assume this has been assigned 'n' 2127f219e013SMasahiro Yamada config N_SYM 2128f219e013SMasahiro Yamada tristate "N_SYM" 2129f219e013SMasahiro Yamada 2130f219e013SMasahiro Yamada # Assume this has been assigned 'm' 2131f219e013SMasahiro Yamada config M_SYM 2132f219e013SMasahiro Yamada tristate "M_SYM" 2133f219e013SMasahiro Yamada 2134f219e013SMasahiro Yamada # Has visibility 'n' 2135f219e013SMasahiro Yamada config A 2136f219e013SMasahiro Yamada tristate "A" 2137f219e013SMasahiro Yamada depends on N_SYM 2138f219e013SMasahiro Yamada 2139f219e013SMasahiro Yamada # Has visibility 'm' 2140f219e013SMasahiro Yamada config B 2141f219e013SMasahiro Yamada tristate "B" 2142f219e013SMasahiro Yamada depends on M_SYM 2143f219e013SMasahiro Yamada 2144f219e013SMasahiro Yamada # Has visibility 'y' 2145f219e013SMasahiro Yamada config C 2146f219e013SMasahiro Yamada tristate "C" 2147f219e013SMasahiro Yamada 2148f219e013SMasahiro Yamada # Has no prompt, and hence visibility 'n' 2149f219e013SMasahiro Yamada config D 2150f219e013SMasahiro Yamada tristate 2151f219e013SMasahiro Yamada 2152f219e013SMasahiro Yamada Having visibility be tri-valued ensures that e.g. a symbol cannot be 2153f219e013SMasahiro Yamada set to "y" by the user if it depends on a symbol with value "m", which 2154f219e013SMasahiro Yamada wouldn't be safe. 2155f219e013SMasahiro Yamada 2156f219e013SMasahiro Yamada You should probably look at get_lower/upper_bound(), 2157f219e013SMasahiro Yamada get_assignable_values() and is_modifiable() before using this.""" 215890c36d8aSUlf Magnusson return _get_visibility(self) 2159f219e013SMasahiro Yamada 2160f219e013SMasahiro Yamada def get_referenced_symbols(self, refs_from_enclosing=False): 2161f219e013SMasahiro Yamada """Returns the set() of all symbols referenced by this symbol. For 2162f219e013SMasahiro Yamada example, the symbol defined by 2163f219e013SMasahiro Yamada 2164f219e013SMasahiro Yamada config FOO 2165f219e013SMasahiro Yamada bool 2166f219e013SMasahiro Yamada prompt "foo" if A && B 2167f219e013SMasahiro Yamada default C if D 2168f219e013SMasahiro Yamada depends on E 2169f219e013SMasahiro Yamada select F if G 2170f219e013SMasahiro Yamada 2171f219e013SMasahiro Yamada references the symbols A through G. 2172f219e013SMasahiro Yamada 217390c36d8aSUlf Magnusson refs_from_enclosing (default: False): If True, the symbols referenced 217490c36d8aSUlf Magnusson by enclosing menus and ifs will be included in the result.""" 217590c36d8aSUlf Magnusson return self.all_referenced_syms if refs_from_enclosing else \ 217690c36d8aSUlf Magnusson self.referenced_syms 2177f219e013SMasahiro Yamada 2178f219e013SMasahiro Yamada def get_selected_symbols(self): 2179f219e013SMasahiro Yamada """Returns the set() of all symbols X for which this symbol has a 2180f219e013SMasahiro Yamada 'select X' or 'select X if Y' (regardless of whether Y is satisfied or 2181f219e013SMasahiro Yamada not). This is a subset of the symbols returned by 2182f219e013SMasahiro Yamada get_referenced_symbols().""" 2183f219e013SMasahiro Yamada return self.selected_syms 2184f219e013SMasahiro Yamada 218590c36d8aSUlf Magnusson def set_user_value(self, v): 218690c36d8aSUlf Magnusson """Sets the user value of the symbol. 2187f219e013SMasahiro Yamada 218890c36d8aSUlf Magnusson Equal in effect to assigning the value to the symbol within a .config 218990c36d8aSUlf Magnusson file. Use get_lower/upper_bound() or get_assignable_values() to find 219090c36d8aSUlf Magnusson the range of currently assignable values for bool and tristate symbols; 219190c36d8aSUlf Magnusson setting values outside this range will cause the user value to differ 219290c36d8aSUlf Magnusson from the result of Symbol.get_value() (be truncated). Values that are 219390c36d8aSUlf Magnusson invalid for the type (such as a_bool.set_user_value("foo")) are 219490c36d8aSUlf Magnusson ignored, and a warning is emitted if an attempt is made to assign such 219590c36d8aSUlf Magnusson a value. 2196f219e013SMasahiro Yamada 219790c36d8aSUlf Magnusson For any type of symbol, is_modifiable() can be used to check if a user 219890c36d8aSUlf Magnusson value will currently have any effect on the symbol, as determined by 219990c36d8aSUlf Magnusson its visibility and range of assignable values. Any value that is valid 220090c36d8aSUlf Magnusson for the type (bool, tristate, etc.) will end up being reflected in 220190c36d8aSUlf Magnusson get_user_value() though, and might have an effect later if conditions 220290c36d8aSUlf Magnusson change. To get rid of the user value, use unset_user_value(). 2203f219e013SMasahiro Yamada 220490c36d8aSUlf Magnusson Any symbols dependent on the symbol are (recursively) invalidated, so 220590c36d8aSUlf Magnusson things will just work with regards to dependencies. 2206f219e013SMasahiro Yamada 220790c36d8aSUlf Magnusson v: The user value to give to the symbol.""" 220890c36d8aSUlf Magnusson self._set_user_value_no_invalidate(v, False) 2209f219e013SMasahiro Yamada 221090c36d8aSUlf Magnusson # There might be something more efficient you could do here, but play 221190c36d8aSUlf Magnusson # it safe. 221290c36d8aSUlf Magnusson if self.name == "MODULES": 221390c36d8aSUlf Magnusson self.config._invalidate_all() 221490c36d8aSUlf Magnusson return 2215f219e013SMasahiro Yamada 221690c36d8aSUlf Magnusson self._invalidate() 221790c36d8aSUlf Magnusson self._invalidate_dependent() 2218f219e013SMasahiro Yamada 221990c36d8aSUlf Magnusson def unset_user_value(self): 222090c36d8aSUlf Magnusson """Resets the user value of the symbol, as if the symbol had never 222190c36d8aSUlf Magnusson gotten a user value via Config.load_config() or 222290c36d8aSUlf Magnusson Symbol.set_user_value().""" 222390c36d8aSUlf Magnusson self._unset_user_value_no_recursive_invalidate() 222490c36d8aSUlf Magnusson self._invalidate_dependent() 2225f219e013SMasahiro Yamada 2226f219e013SMasahiro Yamada def is_modifiable(self): 2227f219e013SMasahiro Yamada """Returns True if the value of the symbol could be modified by calling 222890c36d8aSUlf Magnusson Symbol.set_user_value(). 2229f219e013SMasahiro Yamada 2230f219e013SMasahiro Yamada For bools and tristates, this corresponds to the symbol being visible 2231f219e013SMasahiro Yamada in the 'make menuconfig' interface and not already being pinned to a 2232f219e013SMasahiro Yamada specific value (e.g. because it is selected by another symbol). 2233f219e013SMasahiro Yamada 2234f219e013SMasahiro Yamada For strings and numbers, this corresponds to just being visible. (See 2235f219e013SMasahiro Yamada Symbol.get_visibility().)""" 2236f219e013SMasahiro Yamada if self.is_special_: 2237f219e013SMasahiro Yamada return False 2238f219e013SMasahiro Yamada if self.type == BOOL or self.type == TRISTATE: 2239f219e013SMasahiro Yamada rev_dep = self.config._eval_expr(self.rev_dep) 224090c36d8aSUlf Magnusson # A bool selected to "m" gets promoted to "y", pinning it 224190c36d8aSUlf Magnusson if rev_dep == "m" and self.type == BOOL: 224290c36d8aSUlf Magnusson return False 224390c36d8aSUlf Magnusson return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep] 224490c36d8aSUlf Magnusson return _get_visibility(self) != "n" 2245f219e013SMasahiro Yamada 2246f219e013SMasahiro Yamada def is_defined(self): 2247f219e013SMasahiro Yamada """Returns False if the symbol is referred to in the Kconfig but never 224890c36d8aSUlf Magnusson actually defined.""" 2249f219e013SMasahiro Yamada return self.is_defined_ 2250f219e013SMasahiro Yamada 2251f219e013SMasahiro Yamada def is_special(self): 2252f219e013SMasahiro Yamada """Returns True if the symbol is one of the special symbols n, m, y, or 225390c36d8aSUlf Magnusson UNAME_RELEASE, or gets its value from the environment.""" 2254f219e013SMasahiro Yamada return self.is_special_ 2255f219e013SMasahiro Yamada 2256f219e013SMasahiro Yamada def is_from_environment(self): 225790c36d8aSUlf Magnusson """Returns True if the symbol gets its value from the environment.""" 2258f219e013SMasahiro Yamada return self.is_from_env 2259f219e013SMasahiro Yamada 2260f219e013SMasahiro Yamada def has_ranges(self): 2261f219e013SMasahiro Yamada """Returns True if the symbol is of type INT or HEX and has ranges that 226290c36d8aSUlf Magnusson limit what values it can take on.""" 226390c36d8aSUlf Magnusson return bool(self.ranges) 2264f219e013SMasahiro Yamada 2265f219e013SMasahiro Yamada def is_choice_symbol(self): 2266f219e013SMasahiro Yamada """Returns True if the symbol is in a choice statement and is an actual 226790c36d8aSUlf Magnusson choice symbol (see Choice.get_symbols()).""" 226890c36d8aSUlf Magnusson return self.is_choice_sym 2269f219e013SMasahiro Yamada 2270f219e013SMasahiro Yamada def is_choice_selection(self): 2271f219e013SMasahiro Yamada """Returns True if the symbol is contained in a choice statement and is 227290c36d8aSUlf Magnusson the selected item. Equivalent to 227390c36d8aSUlf Magnusson 227490c36d8aSUlf Magnusson sym.is_choice_symbol() and sym.get_parent().get_selection() is sym""" 227590c36d8aSUlf Magnusson return self.is_choice_sym and self.parent.get_selection() is self 2276f219e013SMasahiro Yamada 22779d01b787SMasahiro Yamada def is_allnoconfig_y(self): 227890c36d8aSUlf Magnusson """Returns True if the symbol has the 'allnoconfig_y' option set.""" 22799d01b787SMasahiro Yamada return self.allnoconfig_y 22809d01b787SMasahiro Yamada 2281f219e013SMasahiro Yamada def __str__(self): 2282f219e013SMasahiro Yamada """Returns a string containing various information about the symbol.""" 2283f219e013SMasahiro Yamada return self.config._get_sym_or_choice_str(self) 2284f219e013SMasahiro Yamada 2285f219e013SMasahiro Yamada # 2286f219e013SMasahiro Yamada # Private methods 2287f219e013SMasahiro Yamada # 2288f219e013SMasahiro Yamada 2289f219e013SMasahiro Yamada def __init__(self): 2290f219e013SMasahiro Yamada """Symbol constructor -- not intended to be called directly by 229190c36d8aSUlf Magnusson Kconfiglib clients.""" 2292f219e013SMasahiro Yamada 2293f219e013SMasahiro Yamada self.name = None 2294f219e013SMasahiro Yamada self.type = UNKNOWN 229590c36d8aSUlf Magnusson self.prompts = [] 229690c36d8aSUlf Magnusson self.def_exprs = [] # 'default' properties 229790c36d8aSUlf Magnusson self.ranges = [] # 'range' properties (for int and hex) 229890c36d8aSUlf Magnusson self.help = None # Help text 229990c36d8aSUlf Magnusson self.rev_dep = "n" # Reverse (select-related) dependencies 230090c36d8aSUlf Magnusson self.config = None 230190c36d8aSUlf Magnusson self.parent = None 2302f219e013SMasahiro Yamada 230390c36d8aSUlf Magnusson self.user_val = None # Value set by user 2304f219e013SMasahiro Yamada 2305f219e013SMasahiro Yamada # The prompt, default value and select conditions without any 230690c36d8aSUlf Magnusson # dependencies from menus and ifs propagated to them 2307f219e013SMasahiro Yamada self.orig_prompts = [] 2308f219e013SMasahiro Yamada self.orig_def_exprs = [] 2309f219e013SMasahiro Yamada self.orig_selects = [] 2310f219e013SMasahiro Yamada 231190c36d8aSUlf Magnusson # Dependencies inherited from containing menus and ifs 2312f219e013SMasahiro Yamada self.deps_from_containing = None 2313f219e013SMasahiro Yamada # The set of symbols referenced by this symbol (see 2314f219e013SMasahiro Yamada # get_referenced_symbols()) 2315f219e013SMasahiro Yamada self.referenced_syms = set() 2316f219e013SMasahiro Yamada # The set of symbols selected by this symbol (see 2317f219e013SMasahiro Yamada # get_selected_symbols()) 2318f219e013SMasahiro Yamada self.selected_syms = set() 2319f219e013SMasahiro Yamada # Like 'referenced_syms', but includes symbols from 232090c36d8aSUlf Magnusson # dependencies inherited from enclosing menus and ifs 2321f219e013SMasahiro Yamada self.all_referenced_syms = set() 2322f219e013SMasahiro Yamada 2323f219e013SMasahiro Yamada # This records only dependencies specified with 'depends on'. Needed 2324f219e013SMasahiro Yamada # when determining actual choice items (hrrrr...). See also 2325f219e013SMasahiro Yamada # Choice._determine_actual_symbols(). 2326f219e013SMasahiro Yamada self.menu_dep = None 2327f219e013SMasahiro Yamada 2328f219e013SMasahiro Yamada # See Symbol.get_ref/def_locations(). 2329f219e013SMasahiro Yamada self.def_locations = [] 2330f219e013SMasahiro Yamada self.ref_locations = [] 2331f219e013SMasahiro Yamada 233290c36d8aSUlf Magnusson # Populated in Config._build_dep() after parsing. Links the symbol to 233390c36d8aSUlf Magnusson # the symbols that immediately depend on it (in a caching/invalidation 233490c36d8aSUlf Magnusson # sense). The total set of dependent symbols for the symbol (the 233590c36d8aSUlf Magnusson # transitive closure) is calculated on an as-needed basis in 233690c36d8aSUlf Magnusson # _get_dependent(). 233790c36d8aSUlf Magnusson self.dep = set() 2338f219e013SMasahiro Yamada 233990c36d8aSUlf Magnusson # Cached values 2340f219e013SMasahiro Yamada 2341f219e013SMasahiro Yamada # Caches the calculated value 234290c36d8aSUlf Magnusson self.cached_val = None 234390c36d8aSUlf Magnusson # Caches the visibility, which acts as an upper bound on the value 234490c36d8aSUlf Magnusson self.cached_visibility = None 2345f219e013SMasahiro Yamada # Caches the total list of dependent symbols. Calculated in 2346f219e013SMasahiro Yamada # _get_dependent(). 2347f219e013SMasahiro Yamada self.cached_deps = None 2348f219e013SMasahiro Yamada 234990c36d8aSUlf Magnusson # Flags 235090c36d8aSUlf Magnusson 2351f219e013SMasahiro Yamada # Does the symbol have an entry in the Kconfig file? The trailing 2352f219e013SMasahiro Yamada # underscore avoids a collision with is_defined(). 2353f219e013SMasahiro Yamada self.is_defined_ = False 235490c36d8aSUlf Magnusson # Should the symbol get an entry in .config? 235590c36d8aSUlf Magnusson self.write_to_conf = False 235690c36d8aSUlf Magnusson # Set to true when _make_conf() is called on a symbol, so that symbols 235790c36d8aSUlf Magnusson # defined in multiple locations only get one .config entry. We need to 235890c36d8aSUlf Magnusson # reset it prior to writing out a new .config. 235990c36d8aSUlf Magnusson self.already_written = False 236090c36d8aSUlf Magnusson # This is set to True for "actual" choice symbols; see 236190c36d8aSUlf Magnusson # Choice._determine_actual_symbols(). 236290c36d8aSUlf Magnusson self.is_choice_sym = False 2363f219e013SMasahiro Yamada # Does the symbol get its value in some special way, e.g. from the 2364f219e013SMasahiro Yamada # environment or by being one of the special symbols n, m, and y? If 236590c36d8aSUlf Magnusson # so, the value is stored in self.cached_val, which is never 2366f219e013SMasahiro Yamada # invalidated. The trailing underscore avoids a collision with 2367f219e013SMasahiro Yamada # is_special(). 2368f219e013SMasahiro Yamada self.is_special_ = False 2369f219e013SMasahiro Yamada # Does the symbol get its value from the environment? 2370f219e013SMasahiro Yamada self.is_from_env = False 23719d01b787SMasahiro Yamada # Does the symbol have the 'allnoconfig_y' option set? 23729d01b787SMasahiro Yamada self.allnoconfig_y = False 23739d01b787SMasahiro Yamada 2374f219e013SMasahiro Yamada def _invalidate(self): 2375f219e013SMasahiro Yamada if self.is_special_: 2376f219e013SMasahiro Yamada return 2377f219e013SMasahiro Yamada 237890c36d8aSUlf Magnusson if self.is_choice_sym: 2379f219e013SMasahiro Yamada self.parent._invalidate() 2380f219e013SMasahiro Yamada 238190c36d8aSUlf Magnusson self.cached_val = None 238290c36d8aSUlf Magnusson self.cached_visibility = None 2383f219e013SMasahiro Yamada 2384f219e013SMasahiro Yamada def _invalidate_dependent(self): 2385f219e013SMasahiro Yamada for sym in self._get_dependent(): 2386f219e013SMasahiro Yamada sym._invalidate() 2387f219e013SMasahiro Yamada 2388f219e013SMasahiro Yamada def _set_user_value_no_invalidate(self, v, suppress_load_warnings): 2389f219e013SMasahiro Yamada """Like set_user_value(), but does not invalidate any symbols. 2390f219e013SMasahiro Yamada 239190c36d8aSUlf Magnusson suppress_load_warnings: some warnings are annoying when loading a 239290c36d8aSUlf Magnusson .config that can be helpful when manually invoking set_user_value(). 239390c36d8aSUlf Magnusson This flag is set to True to suppress such warnings. 2394f219e013SMasahiro Yamada 2395f219e013SMasahiro Yamada Perhaps this could be made optional for load_config() instead.""" 2396f219e013SMasahiro Yamada 2397f219e013SMasahiro Yamada if self.is_special_: 2398f219e013SMasahiro Yamada if self.is_from_env: 2399f219e013SMasahiro Yamada self.config._warn('attempt to assign the value "{0}" to the ' 2400f219e013SMasahiro Yamada 'symbol {1}, which gets its value from the ' 2401f219e013SMasahiro Yamada 'environment. Assignment ignored.' 2402f219e013SMasahiro Yamada .format(v, self.name)) 2403f219e013SMasahiro Yamada else: 2404f219e013SMasahiro Yamada self.config._warn('attempt to assign the value "{0}" to the ' 2405f219e013SMasahiro Yamada 'special symbol {1}. Assignment ignored.' 2406f219e013SMasahiro Yamada .format(v, self.name)) 2407f219e013SMasahiro Yamada return 2408f219e013SMasahiro Yamada 2409f219e013SMasahiro Yamada if not self.is_defined_: 2410f219e013SMasahiro Yamada filename, linenr = self.ref_locations[0] 241190c36d8aSUlf Magnusson if self.config.print_undef_assign: 241290c36d8aSUlf Magnusson _stderr_msg('note: attempt to assign the value "{0}" to {1}, ' 2413f219e013SMasahiro Yamada "which is referenced at {2}:{3} but never " 2414f219e013SMasahiro Yamada "defined. Assignment ignored." 2415f219e013SMasahiro Yamada .format(v, self.name, filename, linenr)) 2416f219e013SMasahiro Yamada return 2417f219e013SMasahiro Yamada 2418f219e013SMasahiro Yamada # Check if the value is valid for our type 241990c36d8aSUlf Magnusson if not ((self.type == BOOL and (v == "y" or v == "n") ) or 242090c36d8aSUlf Magnusson (self.type == TRISTATE and (v == "y" or v == "m" or 242190c36d8aSUlf Magnusson v == "n") ) or 2422f219e013SMasahiro Yamada (self.type == STRING ) or 2423f219e013SMasahiro Yamada (self.type == INT and _is_base_n(v, 10) ) or 2424f219e013SMasahiro Yamada (self.type == HEX and _is_base_n(v, 16) )): 242590c36d8aSUlf Magnusson self.config._warn('the value "{0}" is invalid for {1}, which has ' 242690c36d8aSUlf Magnusson "type {2}. Assignment ignored." 242790c36d8aSUlf Magnusson .format(v, self.name, TYPENAME[self.type])) 2428f219e013SMasahiro Yamada return 2429f219e013SMasahiro Yamada 243090c36d8aSUlf Magnusson if not self.prompts and not suppress_load_warnings: 2431f219e013SMasahiro Yamada self.config._warn('assigning "{0}" to the symbol {1} which ' 2432f219e013SMasahiro Yamada 'lacks prompts and thus has visibility "n". ' 2433f219e013SMasahiro Yamada 'The assignment will have no effect.' 2434f219e013SMasahiro Yamada .format(v, self.name)) 2435f219e013SMasahiro Yamada 2436f219e013SMasahiro Yamada self.user_val = v 2437f219e013SMasahiro Yamada 243890c36d8aSUlf Magnusson if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE): 2439f219e013SMasahiro Yamada choice = self.parent 2440f219e013SMasahiro Yamada if v == "y": 2441f219e013SMasahiro Yamada choice.user_val = self 2442f219e013SMasahiro Yamada choice.user_mode = "y" 2443f219e013SMasahiro Yamada elif v == "m": 2444f219e013SMasahiro Yamada choice.user_val = None 2445f219e013SMasahiro Yamada choice.user_mode = "m" 2446f219e013SMasahiro Yamada 2447f219e013SMasahiro Yamada def _unset_user_value_no_recursive_invalidate(self): 2448f219e013SMasahiro Yamada self._invalidate() 2449f219e013SMasahiro Yamada self.user_val = None 2450f219e013SMasahiro Yamada 245190c36d8aSUlf Magnusson if self.is_choice_sym: 2452f219e013SMasahiro Yamada self.parent._unset_user_value() 2453f219e013SMasahiro Yamada 245490c36d8aSUlf Magnusson def _make_conf(self, append_fn): 2455f219e013SMasahiro Yamada if self.already_written: 245690c36d8aSUlf Magnusson return 2457f219e013SMasahiro Yamada 2458f219e013SMasahiro Yamada self.already_written = True 2459f219e013SMasahiro Yamada 2460f219e013SMasahiro Yamada # Note: write_to_conf is determined in get_value() 2461f219e013SMasahiro Yamada val = self.get_value() 2462f219e013SMasahiro Yamada if not self.write_to_conf: 246390c36d8aSUlf Magnusson return 2464f219e013SMasahiro Yamada 2465f219e013SMasahiro Yamada if self.type == BOOL or self.type == TRISTATE: 246690c36d8aSUlf Magnusson if val == "y" or val == "m": 246790c36d8aSUlf Magnusson append_fn("CONFIG_{0}={1}".format(self.name, val)) 246890c36d8aSUlf Magnusson else: 246990c36d8aSUlf Magnusson append_fn("# CONFIG_{0} is not set".format(self.name)) 247090c36d8aSUlf Magnusson 247190c36d8aSUlf Magnusson elif self.type == INT or self.type == HEX: 247290c36d8aSUlf Magnusson append_fn("CONFIG_{0}={1}".format(self.name, val)) 2473f219e013SMasahiro Yamada 2474f219e013SMasahiro Yamada elif self.type == STRING: 2475f219e013SMasahiro Yamada # Escape \ and " 247690c36d8aSUlf Magnusson append_fn('CONFIG_{0}="{1}"' 2477f219e013SMasahiro Yamada .format(self.name, 247890c36d8aSUlf Magnusson val.replace("\\", "\\\\").replace('"', '\\"'))) 2479f219e013SMasahiro Yamada 2480f219e013SMasahiro Yamada else: 248190c36d8aSUlf Magnusson _internal_error("Internal error while creating .config: unknown " 248290c36d8aSUlf Magnusson 'type "{0}".'.format(self.type)) 2483f219e013SMasahiro Yamada 2484f219e013SMasahiro Yamada def _get_dependent(self): 2485f219e013SMasahiro Yamada """Returns the set of symbols that should be invalidated if the value 2486f219e013SMasahiro Yamada of the symbol changes, because they might be affected by the change. 2487f219e013SMasahiro Yamada Note that this is an internal API -- it's probably of limited 2488f219e013SMasahiro Yamada usefulness to clients.""" 2489f219e013SMasahiro Yamada if self.cached_deps is not None: 2490f219e013SMasahiro Yamada return self.cached_deps 2491f219e013SMasahiro Yamada 249290c36d8aSUlf Magnusson res = set(self.dep) 249390c36d8aSUlf Magnusson for s in self.dep: 249490c36d8aSUlf Magnusson res |= s._get_dependent() 2495f219e013SMasahiro Yamada 249690c36d8aSUlf Magnusson if self.is_choice_sym: 249790c36d8aSUlf Magnusson # Choice symbols also depend (recursively) on their siblings. The 249890c36d8aSUlf Magnusson # siblings are not included in 'dep' to avoid dependency loops. 249990c36d8aSUlf Magnusson for sibling in self.parent.actual_symbols: 250090c36d8aSUlf Magnusson if sibling is not self: 250190c36d8aSUlf Magnusson res.add(sibling) 250290c36d8aSUlf Magnusson res |= sibling.dep 250390c36d8aSUlf Magnusson for s in sibling.dep: 250490c36d8aSUlf Magnusson res |= s._get_dependent() 2505f219e013SMasahiro Yamada 2506f219e013SMasahiro Yamada self.cached_deps = res 2507f219e013SMasahiro Yamada return res 2508f219e013SMasahiro Yamada 2509f219e013SMasahiro Yamada def _has_auto_menu_dep_on(self, on): 2510f219e013SMasahiro Yamada """See Choice._determine_actual_symbols().""" 2511f219e013SMasahiro Yamada if not isinstance(self.parent, Choice): 251290c36d8aSUlf Magnusson _internal_error("Attempt to determine auto menu dependency for " 251390c36d8aSUlf Magnusson "symbol ouside of choice.") 2514f219e013SMasahiro Yamada 251590c36d8aSUlf Magnusson if not self.prompts: 2516f219e013SMasahiro Yamada # If we have no prompt, use the menu dependencies instead (what was 2517f219e013SMasahiro Yamada # specified with 'depends on') 2518f219e013SMasahiro Yamada return self.menu_dep is not None and \ 2519f219e013SMasahiro Yamada self.config._expr_depends_on(self.menu_dep, on) 2520f219e013SMasahiro Yamada 252190c36d8aSUlf Magnusson for _, cond_expr in self.prompts: 2522f219e013SMasahiro Yamada if self.config._expr_depends_on(cond_expr, on): 2523f219e013SMasahiro Yamada return True 2524f219e013SMasahiro Yamada 2525f219e013SMasahiro Yamada return False 2526f219e013SMasahiro Yamada 2527f219e013SMasahiro Yamadaclass Menu(Item): 2528f219e013SMasahiro Yamada 2529f219e013SMasahiro Yamada """Represents a menu statement.""" 2530f219e013SMasahiro Yamada 2531f219e013SMasahiro Yamada # 2532f219e013SMasahiro Yamada # Public interface 2533f219e013SMasahiro Yamada # 2534f219e013SMasahiro Yamada 2535f219e013SMasahiro Yamada def get_config(self): 2536f219e013SMasahiro Yamada """Return the Config instance this menu is from.""" 2537f219e013SMasahiro Yamada return self.config 2538f219e013SMasahiro Yamada 2539f219e013SMasahiro Yamada def get_title(self): 2540f219e013SMasahiro Yamada """Returns the title text of the menu.""" 2541f219e013SMasahiro Yamada return self.title 2542f219e013SMasahiro Yamada 2543f219e013SMasahiro Yamada def get_parent(self): 2544f219e013SMasahiro Yamada """Returns the menu or choice statement that contains the menu, or 2545f219e013SMasahiro Yamada None if the menu is at the top level. Note that if statements are 2546f219e013SMasahiro Yamada treated as syntactic sugar and do not have an explicit class 2547f219e013SMasahiro Yamada representation.""" 2548f219e013SMasahiro Yamada return self.parent 2549f219e013SMasahiro Yamada 2550f219e013SMasahiro Yamada def get_location(self): 2551f219e013SMasahiro Yamada """Returns the location of the menu as a (filename, linenr) tuple, 2552f219e013SMasahiro Yamada where filename is a string and linenr an int.""" 2553f219e013SMasahiro Yamada return (self.filename, self.linenr) 2554f219e013SMasahiro Yamada 255590c36d8aSUlf Magnusson def get_items(self, recursive=False): 255690c36d8aSUlf Magnusson """Returns a list containing the items (symbols, menus, choice 255790c36d8aSUlf Magnusson statements and comments) in in the menu, in the same order that the 255890c36d8aSUlf Magnusson items appear within the menu. 255990c36d8aSUlf Magnusson 256090c36d8aSUlf Magnusson recursive (default: False): True if items contained in items within the 256190c36d8aSUlf Magnusson menu should be included recursively (preorder).""" 256290c36d8aSUlf Magnusson 256390c36d8aSUlf Magnusson if not recursive: 256490c36d8aSUlf Magnusson return self.block 256590c36d8aSUlf Magnusson 256690c36d8aSUlf Magnusson res = [] 256790c36d8aSUlf Magnusson for item in self.block: 256890c36d8aSUlf Magnusson res.append(item) 256990c36d8aSUlf Magnusson if isinstance(item, Menu): 257090c36d8aSUlf Magnusson res.extend(item.get_items(True)) 257190c36d8aSUlf Magnusson elif isinstance(item, Choice): 257290c36d8aSUlf Magnusson res.extend(item.get_items()) 257390c36d8aSUlf Magnusson return res 257490c36d8aSUlf Magnusson 257590c36d8aSUlf Magnusson def get_symbols(self, recursive=False): 257690c36d8aSUlf Magnusson """Returns a list containing the symbols in the menu, in the same order 257790c36d8aSUlf Magnusson that they appear within the menu. 257890c36d8aSUlf Magnusson 257990c36d8aSUlf Magnusson recursive (default: False): True if symbols contained in items within 258090c36d8aSUlf Magnusson the menu should be included recursively.""" 258190c36d8aSUlf Magnusson 258290c36d8aSUlf Magnusson return [item for item in self.get_items(recursive) if 258390c36d8aSUlf Magnusson isinstance(item, Symbol)] 258490c36d8aSUlf Magnusson 258590c36d8aSUlf Magnusson def get_visibility(self): 258690c36d8aSUlf Magnusson """Returns the visibility of the menu. This also affects the visibility 258790c36d8aSUlf Magnusson of subitems. See also Symbol.get_visibility().""" 258890c36d8aSUlf Magnusson return self.config._eval_expr(self.dep_expr) 258990c36d8aSUlf Magnusson 259090c36d8aSUlf Magnusson def get_visible_if_visibility(self): 259190c36d8aSUlf Magnusson """Returns the visibility the menu gets from its 'visible if' 259290c36d8aSUlf Magnusson condition. "y" if the menu has no 'visible if' condition.""" 259390c36d8aSUlf Magnusson return self.config._eval_expr(self.visible_if_expr) 259490c36d8aSUlf Magnusson 259590c36d8aSUlf Magnusson def get_referenced_symbols(self, refs_from_enclosing=False): 259690c36d8aSUlf Magnusson """See Symbol.get_referenced_symbols().""" 259790c36d8aSUlf Magnusson return self.all_referenced_syms if refs_from_enclosing else \ 259890c36d8aSUlf Magnusson self.referenced_syms 259990c36d8aSUlf Magnusson 2600f219e013SMasahiro Yamada def __str__(self): 2601f219e013SMasahiro Yamada """Returns a string containing various information about the menu.""" 2602f219e013SMasahiro Yamada depends_on_str = self.config._expr_val_str(self.orig_deps, 2603f219e013SMasahiro Yamada "(no dependencies)") 2604f219e013SMasahiro Yamada visible_if_str = self.config._expr_val_str(self.visible_if_expr, 2605f219e013SMasahiro Yamada "(no dependencies)") 2606f219e013SMasahiro Yamada 260790c36d8aSUlf Magnusson additional_deps_str = " " + \ 260890c36d8aSUlf Magnusson self.config._expr_val_str(self.deps_from_containing, 2609f219e013SMasahiro Yamada "(no additional dependencies)") 2610f219e013SMasahiro Yamada 261190c36d8aSUlf Magnusson return _lines("Menu", 2612f219e013SMasahiro Yamada "Title : " + self.title, 2613f219e013SMasahiro Yamada "'depends on' dependencies : " + depends_on_str, 2614f219e013SMasahiro Yamada "'visible if' dependencies : " + visible_if_str, 261590c36d8aSUlf Magnusson "Additional dependencies from enclosing menus and " 261690c36d8aSUlf Magnusson "ifs:", 2617f219e013SMasahiro Yamada additional_deps_str, 2618f219e013SMasahiro Yamada "Location: {0}:{1}".format(self.filename, self.linenr)) 2619f219e013SMasahiro Yamada 2620f219e013SMasahiro Yamada # 2621f219e013SMasahiro Yamada # Private methods 2622f219e013SMasahiro Yamada # 2623f219e013SMasahiro Yamada 2624f219e013SMasahiro Yamada def __init__(self): 2625f219e013SMasahiro Yamada """Menu constructor -- not intended to be called directly by 262690c36d8aSUlf Magnusson Kconfiglib clients.""" 2627f219e013SMasahiro Yamada 2628f219e013SMasahiro Yamada self.title = None 2629f219e013SMasahiro Yamada self.dep_expr = None 263090c36d8aSUlf Magnusson self.visible_if_expr = None 263190c36d8aSUlf Magnusson self.block = None 263290c36d8aSUlf Magnusson self.config = None 263390c36d8aSUlf Magnusson self.parent = None 2634f219e013SMasahiro Yamada 2635f219e013SMasahiro Yamada # Dependency expression without dependencies from enclosing menus and 263690c36d8aSUlf Magnusson # ifs propagated 2637f219e013SMasahiro Yamada self.orig_deps = None 2638f219e013SMasahiro Yamada 263990c36d8aSUlf Magnusson # Dependencies inherited from containing menus and ifs 2640f219e013SMasahiro Yamada self.deps_from_containing = None 2641f219e013SMasahiro Yamada # The set of symbols referenced by this menu (see 2642f219e013SMasahiro Yamada # get_referenced_symbols()) 2643f219e013SMasahiro Yamada self.referenced_syms = set() 2644f219e013SMasahiro Yamada # Like 'referenced_syms', but includes symbols from 264590c36d8aSUlf Magnusson # dependencies inherited from enclosing menus and ifs 2646f219e013SMasahiro Yamada self.all_referenced_syms = None 2647f219e013SMasahiro Yamada 2648f219e013SMasahiro Yamada self.filename = None 2649f219e013SMasahiro Yamada self.linenr = None 2650f219e013SMasahiro Yamada 265190c36d8aSUlf Magnusson def _make_conf(self, append_fn): 2652f219e013SMasahiro Yamada if self.config._eval_expr(self.dep_expr) != "n" and \ 2653f219e013SMasahiro Yamada self.config._eval_expr(self.visible_if_expr) != "n": 265490c36d8aSUlf Magnusson append_fn("\n#\n# {0}\n#".format(self.title)) 265590c36d8aSUlf Magnusson _make_block_conf(self.block, append_fn) 2656f219e013SMasahiro Yamada 265790c36d8aSUlf Magnussonclass Choice(Item): 2658f219e013SMasahiro Yamada 2659f219e013SMasahiro Yamada """Represents a choice statement. A choice can be in one of three modes: 2660f219e013SMasahiro Yamada 2661f219e013SMasahiro Yamada "n" - The choice is not visible and no symbols can be selected. 2662f219e013SMasahiro Yamada 2663f219e013SMasahiro Yamada "m" - Any number of symbols can be set to "m". The rest will be "n". This 2664f219e013SMasahiro Yamada is safe since potentially conflicting options don't actually get 2665f219e013SMasahiro Yamada compiled into the kernel simultaneously with "m". 2666f219e013SMasahiro Yamada 2667f219e013SMasahiro Yamada "y" - One symbol will be "y" while the rest are "n". 2668f219e013SMasahiro Yamada 2669f219e013SMasahiro Yamada Only tristate choices can be in "m" mode, and the visibility of the choice 2670f219e013SMasahiro Yamada is an upper bound on the mode, so that e.g. a choice that depends on a 2671f219e013SMasahiro Yamada symbol with value "m" will be in "m" mode. 2672f219e013SMasahiro Yamada 2673f219e013SMasahiro Yamada The mode changes automatically when a value is assigned to a symbol within 2674f219e013SMasahiro Yamada the choice. 2675f219e013SMasahiro Yamada 2676f219e013SMasahiro Yamada See Symbol.get_visibility() too.""" 2677f219e013SMasahiro Yamada 2678f219e013SMasahiro Yamada # 2679f219e013SMasahiro Yamada # Public interface 2680f219e013SMasahiro Yamada # 2681f219e013SMasahiro Yamada 2682f219e013SMasahiro Yamada def get_config(self): 2683f219e013SMasahiro Yamada """Returns the Config instance this choice is from.""" 2684f219e013SMasahiro Yamada return self.config 2685f219e013SMasahiro Yamada 2686f219e013SMasahiro Yamada def get_name(self): 2687f219e013SMasahiro Yamada """For named choices, returns the name. Returns None for unnamed 2688f219e013SMasahiro Yamada choices. No named choices appear anywhere in the kernel Kconfig files 2689f219e013SMasahiro Yamada as of Linux 3.7.0-rc8.""" 2690f219e013SMasahiro Yamada return self.name 2691f219e013SMasahiro Yamada 269290c36d8aSUlf Magnusson def get_type(self): 269390c36d8aSUlf Magnusson """Returns the type of the choice. See Symbol.get_type().""" 269490c36d8aSUlf Magnusson return self.type 269590c36d8aSUlf Magnusson 2696f219e013SMasahiro Yamada def get_prompts(self): 2697f219e013SMasahiro Yamada """Returns a list of prompts defined for the choice, in the order they 2698f219e013SMasahiro Yamada appear in the configuration files. Returns the empty list for choices 2699f219e013SMasahiro Yamada with no prompt. 2700f219e013SMasahiro Yamada 2701f219e013SMasahiro Yamada This list will have a single entry for the vast majority of choices 2702f219e013SMasahiro Yamada having prompts, but having multiple prompts for a single choice is 2703f219e013SMasahiro Yamada possible through having multiple 'choice' entries for it (though I'm 2704f219e013SMasahiro Yamada not sure if that ever happens in practice).""" 2705f219e013SMasahiro Yamada return [prompt for prompt, _ in self.orig_prompts] 2706f219e013SMasahiro Yamada 2707f219e013SMasahiro Yamada def get_help(self): 2708f219e013SMasahiro Yamada """Returns the help text of the choice, or None if the choice has no 2709f219e013SMasahiro Yamada help text.""" 2710f219e013SMasahiro Yamada return self.help 2711f219e013SMasahiro Yamada 271290c36d8aSUlf Magnusson def get_parent(self): 271390c36d8aSUlf Magnusson """Returns the menu or choice statement that contains the choice, or 271490c36d8aSUlf Magnusson None if the choice is at the top level. Note that if statements are 271590c36d8aSUlf Magnusson treated as syntactic sugar and do not have an explicit class 271690c36d8aSUlf Magnusson representation.""" 271790c36d8aSUlf Magnusson return self.parent 271890c36d8aSUlf Magnusson 271990c36d8aSUlf Magnusson def get_def_locations(self): 272090c36d8aSUlf Magnusson """Returns a list of (filename, linenr) tuples, where filename (string) 272190c36d8aSUlf Magnusson and linenr (int) represent a location where the choice is defined. For 272290c36d8aSUlf Magnusson the vast majority of choices (all of them as of Linux 3.7.0-rc8) this 272390c36d8aSUlf Magnusson list will only contain one element, but its possible for named choices 272490c36d8aSUlf Magnusson to be defined in multiple locations.""" 272590c36d8aSUlf Magnusson return self.def_locations 272690c36d8aSUlf Magnusson 272790c36d8aSUlf Magnusson def get_selection(self): 272890c36d8aSUlf Magnusson """Returns the symbol selected (either by the user or through 272990c36d8aSUlf Magnusson defaults), or None if either no symbol is selected or the mode is not 273090c36d8aSUlf Magnusson "y".""" 273190c36d8aSUlf Magnusson if self.cached_selection is not None: 273290c36d8aSUlf Magnusson if self.cached_selection == NO_SELECTION: 273390c36d8aSUlf Magnusson return None 273490c36d8aSUlf Magnusson return self.cached_selection 273590c36d8aSUlf Magnusson 273690c36d8aSUlf Magnusson if self.get_mode() != "y": 273790c36d8aSUlf Magnusson return self._cache_ret(None) 273890c36d8aSUlf Magnusson 273990c36d8aSUlf Magnusson # User choice available? 274090c36d8aSUlf Magnusson if self.user_val is not None and _get_visibility(self.user_val) == "y": 274190c36d8aSUlf Magnusson return self._cache_ret(self.user_val) 274290c36d8aSUlf Magnusson 274390c36d8aSUlf Magnusson if self.optional: 274490c36d8aSUlf Magnusson return self._cache_ret(None) 274590c36d8aSUlf Magnusson 274690c36d8aSUlf Magnusson return self._cache_ret(self.get_selection_from_defaults()) 274790c36d8aSUlf Magnusson 274890c36d8aSUlf Magnusson def get_selection_from_defaults(self): 274990c36d8aSUlf Magnusson """Like Choice.get_selection(), but acts as if no symbol has been 275090c36d8aSUlf Magnusson selected by the user and no 'optional' flag is in effect.""" 275190c36d8aSUlf Magnusson 275290c36d8aSUlf Magnusson if not self.actual_symbols: 275390c36d8aSUlf Magnusson return None 275490c36d8aSUlf Magnusson 275590c36d8aSUlf Magnusson for symbol, cond_expr in self.def_exprs: 275690c36d8aSUlf Magnusson if self.config._eval_expr(cond_expr) != "n": 275790c36d8aSUlf Magnusson chosen_symbol = symbol 275890c36d8aSUlf Magnusson break 275990c36d8aSUlf Magnusson else: 276090c36d8aSUlf Magnusson chosen_symbol = self.actual_symbols[0] 276190c36d8aSUlf Magnusson 276290c36d8aSUlf Magnusson # Is the chosen symbol visible? 276390c36d8aSUlf Magnusson if _get_visibility(chosen_symbol) != "n": 276490c36d8aSUlf Magnusson return chosen_symbol 276590c36d8aSUlf Magnusson # Otherwise, pick the first visible symbol 276690c36d8aSUlf Magnusson for sym in self.actual_symbols: 276790c36d8aSUlf Magnusson if _get_visibility(sym) != "n": 276890c36d8aSUlf Magnusson return sym 276990c36d8aSUlf Magnusson return None 277090c36d8aSUlf Magnusson 277190c36d8aSUlf Magnusson def get_user_selection(self): 277290c36d8aSUlf Magnusson """If the choice is in "y" mode and has a user-selected symbol, returns 277390c36d8aSUlf Magnusson that symbol. Otherwise, returns None.""" 277490c36d8aSUlf Magnusson return self.user_val 2775f219e013SMasahiro Yamada 2776f219e013SMasahiro Yamada def get_items(self): 2777f219e013SMasahiro Yamada """Gets all items contained in the choice in the same order as within 2778f219e013SMasahiro Yamada the configuration ("items" instead of "symbols" since choices and 2779f219e013SMasahiro Yamada comments might appear within choices. This only happens in one place as 2780f219e013SMasahiro Yamada of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig).""" 278190c36d8aSUlf Magnusson return self.block 2782f219e013SMasahiro Yamada 2783f219e013SMasahiro Yamada def get_symbols(self): 2784f219e013SMasahiro Yamada """Returns a list containing the choice's symbols. 2785f219e013SMasahiro Yamada 2786f219e013SMasahiro Yamada A quirk (perhaps a bug) of Kconfig is that you can put items within a 2787f219e013SMasahiro Yamada choice that will not be considered members of the choice insofar as 2788f219e013SMasahiro Yamada selection is concerned. This happens for example if one symbol within a 2789f219e013SMasahiro Yamada choice 'depends on' the symbol preceding it, or if you put non-symbol 2790f219e013SMasahiro Yamada items within choices. 2791f219e013SMasahiro Yamada 2792f219e013SMasahiro Yamada As of Linux 3.7.0-rc8, this seems to be used intentionally in one 2793f219e013SMasahiro Yamada place: drivers/usb/gadget/Kconfig. 2794f219e013SMasahiro Yamada 2795f219e013SMasahiro Yamada This function returns the "proper" symbols of the choice in the order 2796f219e013SMasahiro Yamada they appear in the choice, excluding such items. If you want all items 2797f219e013SMasahiro Yamada in the choice, use get_items().""" 2798f219e013SMasahiro Yamada return self.actual_symbols 2799f219e013SMasahiro Yamada 2800f219e013SMasahiro Yamada def get_referenced_symbols(self, refs_from_enclosing=False): 2801f219e013SMasahiro Yamada """See Symbol.get_referenced_symbols().""" 280290c36d8aSUlf Magnusson return self.all_referenced_syms if refs_from_enclosing else \ 280390c36d8aSUlf Magnusson self.referenced_syms 2804f219e013SMasahiro Yamada 2805f219e013SMasahiro Yamada def get_visibility(self): 2806f219e013SMasahiro Yamada """Returns the visibility of the choice statement: one of "n", "m" or 2807f219e013SMasahiro Yamada "y". This acts as an upper limit on the mode of the choice (though bool 2808f219e013SMasahiro Yamada choices can only have the mode "y"). See the class documentation for an 2809f219e013SMasahiro Yamada explanation of modes.""" 281090c36d8aSUlf Magnusson return _get_visibility(self) 2811f219e013SMasahiro Yamada 2812f219e013SMasahiro Yamada def get_mode(self): 2813f219e013SMasahiro Yamada """Returns the mode of the choice. See the class documentation for 2814f219e013SMasahiro Yamada an explanation of modes.""" 2815f219e013SMasahiro Yamada minimum_mode = "n" if self.optional else "m" 2816f219e013SMasahiro Yamada mode = self.user_mode if self.user_mode is not None else minimum_mode 281790c36d8aSUlf Magnusson mode = self.config._eval_min(mode, _get_visibility(self)) 2818f219e013SMasahiro Yamada 2819f219e013SMasahiro Yamada # Promote "m" to "y" for boolean choices 2820f219e013SMasahiro Yamada if mode == "m" and self.type == BOOL: 2821f219e013SMasahiro Yamada return "y" 2822f219e013SMasahiro Yamada 2823f219e013SMasahiro Yamada return mode 2824f219e013SMasahiro Yamada 2825f219e013SMasahiro Yamada def is_optional(self): 282690c36d8aSUlf Magnusson """Returns True if the choice has the 'optional' flag set (and so will 282790c36d8aSUlf Magnusson default to "n" mode).""" 2828f219e013SMasahiro Yamada return self.optional 2829f219e013SMasahiro Yamada 2830f219e013SMasahiro Yamada def __str__(self): 2831f219e013SMasahiro Yamada """Returns a string containing various information about the choice 2832f219e013SMasahiro Yamada statement.""" 2833f219e013SMasahiro Yamada return self.config._get_sym_or_choice_str(self) 2834f219e013SMasahiro Yamada 2835f219e013SMasahiro Yamada # 2836f219e013SMasahiro Yamada # Private methods 2837f219e013SMasahiro Yamada # 2838f219e013SMasahiro Yamada 2839f219e013SMasahiro Yamada def __init__(self): 2840f219e013SMasahiro Yamada """Choice constructor -- not intended to be called directly by 284190c36d8aSUlf Magnusson Kconfiglib clients.""" 2842f219e013SMasahiro Yamada 2843f219e013SMasahiro Yamada self.name = None # Yes, choices can be named 2844f219e013SMasahiro Yamada self.type = UNKNOWN 284590c36d8aSUlf Magnusson self.prompts = [] 284690c36d8aSUlf Magnusson self.def_exprs = [] # 'default' properties 284790c36d8aSUlf Magnusson self.help = None # Help text 284890c36d8aSUlf Magnusson self.block = None # List of contained items 284990c36d8aSUlf Magnusson self.config = None 285090c36d8aSUlf Magnusson self.parent = None 2851f219e013SMasahiro Yamada 285290c36d8aSUlf Magnusson self.user_val = None 285390c36d8aSUlf Magnusson self.user_mode = None 2854f219e013SMasahiro Yamada 2855f219e013SMasahiro Yamada # We need to filter out symbols that appear within the choice block but 2856f219e013SMasahiro Yamada # are not considered choice items (see 285790c36d8aSUlf Magnusson # Choice._determine_actual_symbols()) This list holds the "actual" 285890c36d8aSUlf Magnusson # choice items. 2859f219e013SMasahiro Yamada self.actual_symbols = [] 2860f219e013SMasahiro Yamada 286190c36d8aSUlf Magnusson # The prompts and default values without any dependencies from 286290c36d8aSUlf Magnusson # enclosing menus and ifs propagated 286390c36d8aSUlf Magnusson self.orig_prompts = [] 286490c36d8aSUlf Magnusson self.orig_def_exprs = [] 286590c36d8aSUlf Magnusson 286690c36d8aSUlf Magnusson # Dependencies inherited from containing menus and ifs 286790c36d8aSUlf Magnusson self.deps_from_containing = None 2868f219e013SMasahiro Yamada # The set of symbols referenced by this choice (see 2869f219e013SMasahiro Yamada # get_referenced_symbols()) 2870f219e013SMasahiro Yamada self.referenced_syms = set() 2871f219e013SMasahiro Yamada # Like 'referenced_syms', but includes symbols from 287290c36d8aSUlf Magnusson # dependencies inherited from enclosing menus and ifs 2873f219e013SMasahiro Yamada self.all_referenced_syms = set() 2874f219e013SMasahiro Yamada 2875f219e013SMasahiro Yamada # See Choice.get_def_locations() 2876f219e013SMasahiro Yamada self.def_locations = [] 2877f219e013SMasahiro Yamada 287890c36d8aSUlf Magnusson # Cached values 2879f219e013SMasahiro Yamada self.cached_selection = None 288090c36d8aSUlf Magnusson self.cached_visibility = None 288190c36d8aSUlf Magnusson 288290c36d8aSUlf Magnusson self.optional = False 2883f219e013SMasahiro Yamada 2884f219e013SMasahiro Yamada def _determine_actual_symbols(self): 2885f219e013SMasahiro Yamada """If a symbol's visibility depends on the preceding symbol within a 288690c36d8aSUlf Magnusson choice, it is no longer viewed as a choice item. (This is quite 288790c36d8aSUlf Magnusson possibly a bug, but some things consciously use it... ugh. It stems 288890c36d8aSUlf Magnusson from automatic submenu creation.) In addition, it's possible to have 288990c36d8aSUlf Magnusson choices and comments within choices, and those shouldn't be considered 289090c36d8aSUlf Magnusson choice items either. Only drivers/usb/gadget/Kconfig seems to depend on 289190c36d8aSUlf Magnusson any of this. This method computes the "actual" items in the choice and 289290c36d8aSUlf Magnusson sets the is_choice_sym flag on them (retrieved via is_choice_symbol()). 2893f219e013SMasahiro Yamada 2894f219e013SMasahiro Yamada Don't let this scare you: an earlier version simply checked for a 2895f219e013SMasahiro Yamada sequence of symbols where all symbols after the first appeared in the 2896f219e013SMasahiro Yamada 'depends on' expression of the first, and that worked fine. The added 2897f219e013SMasahiro Yamada complexity is to be future-proof in the event that 2898f219e013SMasahiro Yamada drivers/usb/gadget/Kconfig turns even more sinister. It might very well 2899f219e013SMasahiro Yamada be overkilling things (especially if that file is refactored ;).""" 2900f219e013SMasahiro Yamada 2901f219e013SMasahiro Yamada # Items might depend on each other in a tree structure, so we need a 2902f219e013SMasahiro Yamada # stack to keep track of the current tentative parent 2903f219e013SMasahiro Yamada stack = [] 2904f219e013SMasahiro Yamada 290590c36d8aSUlf Magnusson for item in self.block: 2906f219e013SMasahiro Yamada if not isinstance(item, Symbol): 2907f219e013SMasahiro Yamada stack = [] 2908f219e013SMasahiro Yamada continue 2909f219e013SMasahiro Yamada 291090c36d8aSUlf Magnusson while stack: 2911f219e013SMasahiro Yamada if item._has_auto_menu_dep_on(stack[-1]): 2912f219e013SMasahiro Yamada # The item should not be viewed as a choice item, so don't 291390c36d8aSUlf Magnusson # set item.is_choice_sym 2914f219e013SMasahiro Yamada stack.append(item) 2915f219e013SMasahiro Yamada break 2916f219e013SMasahiro Yamada else: 2917f219e013SMasahiro Yamada stack.pop() 2918f219e013SMasahiro Yamada else: 291990c36d8aSUlf Magnusson item.is_choice_sym = True 2920f219e013SMasahiro Yamada self.actual_symbols.append(item) 2921f219e013SMasahiro Yamada stack.append(item) 2922f219e013SMasahiro Yamada 2923f219e013SMasahiro Yamada def _cache_ret(self, selection): 2924f219e013SMasahiro Yamada # As None is used to indicate the lack of a cached value we can't use 2925f219e013SMasahiro Yamada # that to cache the fact that the choice has no selection. Instead, we 2926f219e013SMasahiro Yamada # use the symbolic constant NO_SELECTION. 2927f219e013SMasahiro Yamada if selection is None: 2928f219e013SMasahiro Yamada self.cached_selection = NO_SELECTION 2929f219e013SMasahiro Yamada else: 2930f219e013SMasahiro Yamada self.cached_selection = selection 2931f219e013SMasahiro Yamada 2932f219e013SMasahiro Yamada return selection 2933f219e013SMasahiro Yamada 2934f219e013SMasahiro Yamada def _invalidate(self): 2935f219e013SMasahiro Yamada self.cached_selection = None 293690c36d8aSUlf Magnusson self.cached_visibility = None 2937f219e013SMasahiro Yamada 2938f219e013SMasahiro Yamada def _unset_user_value(self): 2939f219e013SMasahiro Yamada self._invalidate() 2940f219e013SMasahiro Yamada self.user_val = None 2941f219e013SMasahiro Yamada self.user_mode = None 2942f219e013SMasahiro Yamada 294390c36d8aSUlf Magnusson def _make_conf(self, append_fn): 294490c36d8aSUlf Magnusson _make_block_conf(self.block, append_fn) 2945f219e013SMasahiro Yamada 2946f219e013SMasahiro Yamadaclass Comment(Item): 2947f219e013SMasahiro Yamada 2948f219e013SMasahiro Yamada """Represents a comment statement.""" 2949f219e013SMasahiro Yamada 2950f219e013SMasahiro Yamada # 2951f219e013SMasahiro Yamada # Public interface 2952f219e013SMasahiro Yamada # 2953f219e013SMasahiro Yamada 2954f219e013SMasahiro Yamada def get_config(self): 2955f219e013SMasahiro Yamada """Returns the Config instance this comment is from.""" 2956f219e013SMasahiro Yamada return self.config 2957f219e013SMasahiro Yamada 2958f219e013SMasahiro Yamada def get_text(self): 2959f219e013SMasahiro Yamada """Returns the text of the comment.""" 2960f219e013SMasahiro Yamada return self.text 2961f219e013SMasahiro Yamada 2962f219e013SMasahiro Yamada def get_parent(self): 2963f219e013SMasahiro Yamada """Returns the menu or choice statement that contains the comment, or 2964f219e013SMasahiro Yamada None if the comment is at the top level. Note that if statements are 2965f219e013SMasahiro Yamada treated as syntactic sugar and do not have an explicit class 2966f219e013SMasahiro Yamada representation.""" 2967f219e013SMasahiro Yamada return self.parent 2968f219e013SMasahiro Yamada 2969f219e013SMasahiro Yamada def get_location(self): 2970f219e013SMasahiro Yamada """Returns the location of the comment as a (filename, linenr) tuple, 2971f219e013SMasahiro Yamada where filename is a string and linenr an int.""" 2972f219e013SMasahiro Yamada return (self.filename, self.linenr) 2973f219e013SMasahiro Yamada 297490c36d8aSUlf Magnusson def get_visibility(self): 297590c36d8aSUlf Magnusson """Returns the visibility of the comment. See also 297690c36d8aSUlf Magnusson Symbol.get_visibility().""" 297790c36d8aSUlf Magnusson return self.config._eval_expr(self.dep_expr) 2978f219e013SMasahiro Yamada 297990c36d8aSUlf Magnusson def get_referenced_symbols(self, refs_from_enclosing=False): 298090c36d8aSUlf Magnusson """See Symbol.get_referenced_symbols().""" 298190c36d8aSUlf Magnusson return self.all_referenced_syms if refs_from_enclosing else \ 298290c36d8aSUlf Magnusson self.referenced_syms 298390c36d8aSUlf Magnusson 298490c36d8aSUlf Magnusson def __str__(self): 298590c36d8aSUlf Magnusson """Returns a string containing various information about the 298690c36d8aSUlf Magnusson comment.""" 298790c36d8aSUlf Magnusson dep_str = self.config._expr_val_str(self.orig_deps, 298890c36d8aSUlf Magnusson "(no dependencies)") 298990c36d8aSUlf Magnusson 299090c36d8aSUlf Magnusson additional_deps_str = " " + \ 299190c36d8aSUlf Magnusson self.config._expr_val_str(self.deps_from_containing, 2992f219e013SMasahiro Yamada "(no additional dependencies)") 2993f219e013SMasahiro Yamada 299490c36d8aSUlf Magnusson return _lines("Comment", 2995f219e013SMasahiro Yamada "Text: " + str(self.text), 2996f219e013SMasahiro Yamada "Dependencies: " + dep_str, 299790c36d8aSUlf Magnusson "Additional dependencies from enclosing menus and " 299890c36d8aSUlf Magnusson "ifs:", 2999f219e013SMasahiro Yamada additional_deps_str, 3000f219e013SMasahiro Yamada "Location: {0}:{1}".format(self.filename, self.linenr)) 3001f219e013SMasahiro Yamada 3002f219e013SMasahiro Yamada # 3003f219e013SMasahiro Yamada # Private methods 3004f219e013SMasahiro Yamada # 3005f219e013SMasahiro Yamada 3006f219e013SMasahiro Yamada def __init__(self): 3007f219e013SMasahiro Yamada """Comment constructor -- not intended to be called directly by 300890c36d8aSUlf Magnusson Kconfiglib clients.""" 3009f219e013SMasahiro Yamada 3010f219e013SMasahiro Yamada self.text = None 3011f219e013SMasahiro Yamada self.dep_expr = None 301290c36d8aSUlf Magnusson self.config = None 301390c36d8aSUlf Magnusson self.parent = None 3014f219e013SMasahiro Yamada 3015f219e013SMasahiro Yamada # Dependency expression without dependencies from enclosing menus and 301690c36d8aSUlf Magnusson # ifs propagated 3017f219e013SMasahiro Yamada self.orig_deps = None 3018f219e013SMasahiro Yamada 301990c36d8aSUlf Magnusson # Dependencies inherited from containing menus and ifs 3020f219e013SMasahiro Yamada self.deps_from_containing = None 3021f219e013SMasahiro Yamada # The set of symbols referenced by this comment (see 3022f219e013SMasahiro Yamada # get_referenced_symbols()) 3023f219e013SMasahiro Yamada self.referenced_syms = set() 3024f219e013SMasahiro Yamada # Like 'referenced_syms', but includes symbols from 302590c36d8aSUlf Magnusson # dependencies inherited from enclosing menus and ifs 3026f219e013SMasahiro Yamada self.all_referenced_syms = None 3027f219e013SMasahiro Yamada 3028f219e013SMasahiro Yamada self.filename = None 3029f219e013SMasahiro Yamada self.linenr = None 3030f219e013SMasahiro Yamada 303190c36d8aSUlf Magnusson def _make_conf(self, append_fn): 3032f219e013SMasahiro Yamada if self.config._eval_expr(self.dep_expr) != "n": 303390c36d8aSUlf Magnusson append_fn("\n#\n# {0}\n#".format(self.text)) 3034f219e013SMasahiro Yamada 303590c36d8aSUlf Magnussonclass Kconfig_Syntax_Error(Exception): 303690c36d8aSUlf Magnusson """Exception raised for syntax errors.""" 303790c36d8aSUlf Magnusson pass 3038f219e013SMasahiro Yamada 303990c36d8aSUlf Magnussonclass Internal_Error(Exception): 304090c36d8aSUlf Magnusson """Exception raised for internal errors.""" 304190c36d8aSUlf Magnusson pass 304290c36d8aSUlf Magnusson 304390c36d8aSUlf Magnusson# 304490c36d8aSUlf Magnusson# Public functions 304590c36d8aSUlf Magnusson# 304690c36d8aSUlf Magnusson 304790c36d8aSUlf Magnussondef tri_less(v1, v2): 304890c36d8aSUlf Magnusson """Returns True if the tristate v1 is less than the tristate v2, where "n", 304990c36d8aSUlf Magnusson "m" and "y" are ordered from lowest to highest.""" 305090c36d8aSUlf Magnusson return TRI_TO_INT[v1] < TRI_TO_INT[v2] 305190c36d8aSUlf Magnusson 305290c36d8aSUlf Magnussondef tri_less_eq(v1, v2): 305390c36d8aSUlf Magnusson """Returns True if the tristate v1 is less than or equal to the tristate 305490c36d8aSUlf Magnusson v2, where "n", "m" and "y" are ordered from lowest to highest.""" 305590c36d8aSUlf Magnusson return TRI_TO_INT[v1] <= TRI_TO_INT[v2] 305690c36d8aSUlf Magnusson 305790c36d8aSUlf Magnussondef tri_greater(v1, v2): 305890c36d8aSUlf Magnusson """Returns True if the tristate v1 is greater than the tristate v2, where 305990c36d8aSUlf Magnusson "n", "m" and "y" are ordered from lowest to highest.""" 306090c36d8aSUlf Magnusson return TRI_TO_INT[v1] > TRI_TO_INT[v2] 306190c36d8aSUlf Magnusson 306290c36d8aSUlf Magnussondef tri_greater_eq(v1, v2): 306390c36d8aSUlf Magnusson """Returns True if the tristate v1 is greater than or equal to the tristate 306490c36d8aSUlf Magnusson v2, where "n", "m" and "y" are ordered from lowest to highest.""" 306590c36d8aSUlf Magnusson return TRI_TO_INT[v1] >= TRI_TO_INT[v2] 306690c36d8aSUlf Magnusson 306790c36d8aSUlf Magnusson# 306890c36d8aSUlf Magnusson# Internal classes 306990c36d8aSUlf Magnusson# 307090c36d8aSUlf Magnusson 307190c36d8aSUlf Magnussonclass _Feed(object): 307290c36d8aSUlf Magnusson 307390c36d8aSUlf Magnusson """Class for working with sequences in a stream-like fashion; handy for 307490c36d8aSUlf Magnusson tokens.""" 307590c36d8aSUlf Magnusson 307690c36d8aSUlf Magnusson # This would be more helpful on the item classes, but would remove some 307790c36d8aSUlf Magnusson # flexibility 307890c36d8aSUlf Magnusson __slots__ = ['items', 'length', 'i'] 3079f219e013SMasahiro Yamada 3080f219e013SMasahiro Yamada def __init__(self, items): 3081f219e013SMasahiro Yamada self.items = items 3082f219e013SMasahiro Yamada self.length = len(self.items) 3083f219e013SMasahiro Yamada self.i = 0 3084f219e013SMasahiro Yamada 3085f219e013SMasahiro Yamada def get_next(self): 3086f219e013SMasahiro Yamada if self.i >= self.length: 3087f219e013SMasahiro Yamada return None 3088f219e013SMasahiro Yamada item = self.items[self.i] 3089f219e013SMasahiro Yamada self.i += 1 3090f219e013SMasahiro Yamada return item 3091f219e013SMasahiro Yamada 3092f219e013SMasahiro Yamada def peek_next(self): 3093f219e013SMasahiro Yamada return None if self.i >= self.length else self.items[self.i] 3094f219e013SMasahiro Yamada 3095f219e013SMasahiro Yamada def check(self, token): 3096f219e013SMasahiro Yamada """Check if the next token is 'token'. If so, remove it from the token 3097f219e013SMasahiro Yamada feed and return True. Otherwise, leave it in and return False.""" 309890c36d8aSUlf Magnusson if self.i < self.length and self.items[self.i] == token: 3099f219e013SMasahiro Yamada self.i += 1 3100f219e013SMasahiro Yamada return True 3101f219e013SMasahiro Yamada return False 3102f219e013SMasahiro Yamada 310390c36d8aSUlf Magnusson def unget_all(self): 310490c36d8aSUlf Magnusson self.i = 0 3105f219e013SMasahiro Yamada 310690c36d8aSUlf Magnussonclass _FileFeed(object): 3107f219e013SMasahiro Yamada 310890c36d8aSUlf Magnusson """Feeds lines from a file. Keeps track of the filename and current line 310990c36d8aSUlf Magnusson number. Joins any line ending in \\ with the following line. We need to be 311090c36d8aSUlf Magnusson careful to get the line number right in the presence of continuation 311190c36d8aSUlf Magnusson lines.""" 3112f219e013SMasahiro Yamada 311390c36d8aSUlf Magnusson __slots__ = ['filename', 'lines', 'length', 'linenr'] 3114f219e013SMasahiro Yamada 311590c36d8aSUlf Magnusson def __init__(self, filename): 3116f219e013SMasahiro Yamada self.filename = _clean_up_path(filename) 311790c36d8aSUlf Magnusson with open(filename, "r") as f: 311890c36d8aSUlf Magnusson # No interleaving of I/O and processing yet. Don't know if it would 311990c36d8aSUlf Magnusson # help. 312090c36d8aSUlf Magnusson self.lines = f.readlines() 312190c36d8aSUlf Magnusson self.length = len(self.lines) 312290c36d8aSUlf Magnusson self.linenr = 0 3123f219e013SMasahiro Yamada 312490c36d8aSUlf Magnusson def get_next(self): 312590c36d8aSUlf Magnusson if self.linenr >= self.length: 312690c36d8aSUlf Magnusson return None 312790c36d8aSUlf Magnusson line = self.lines[self.linenr] 312890c36d8aSUlf Magnusson self.linenr += 1 312990c36d8aSUlf Magnusson while line.endswith("\\\n"): 313090c36d8aSUlf Magnusson line = line[:-2] + self.lines[self.linenr] 313190c36d8aSUlf Magnusson self.linenr += 1 313290c36d8aSUlf Magnusson return line 3133f219e013SMasahiro Yamada 313490c36d8aSUlf Magnusson def peek_next(self): 313590c36d8aSUlf Magnusson linenr = self.linenr 313690c36d8aSUlf Magnusson if linenr >= self.length: 313790c36d8aSUlf Magnusson return None 313890c36d8aSUlf Magnusson line = self.lines[linenr] 313990c36d8aSUlf Magnusson while line.endswith("\\\n"): 314090c36d8aSUlf Magnusson linenr += 1 314190c36d8aSUlf Magnusson line = line[:-2] + self.lines[linenr] 314290c36d8aSUlf Magnusson return line 314390c36d8aSUlf Magnusson 314490c36d8aSUlf Magnusson def unget(self): 314590c36d8aSUlf Magnusson self.linenr -= 1 314690c36d8aSUlf Magnusson while self.lines[self.linenr].endswith("\\\n"): 314790c36d8aSUlf Magnusson self.linenr -= 1 314890c36d8aSUlf Magnusson 314990c36d8aSUlf Magnusson def next_nonblank(self): 315090c36d8aSUlf Magnusson """Removes lines up to and including the next non-blank (not all-space) 315190c36d8aSUlf Magnusson line and returns it. Returns None if there are no more non-blank 315290c36d8aSUlf Magnusson lines.""" 315390c36d8aSUlf Magnusson while 1: 315490c36d8aSUlf Magnusson line = self.get_next() 315590c36d8aSUlf Magnusson if line is None or not line.isspace(): 315690c36d8aSUlf Magnusson return line 3157f219e013SMasahiro Yamada 3158f219e013SMasahiro Yamada# 315990c36d8aSUlf Magnusson# Internal functions 3160f219e013SMasahiro Yamada# 3161f219e013SMasahiro Yamada 316290c36d8aSUlf Magnussondef _get_visibility(sc): 316390c36d8aSUlf Magnusson """Symbols and Choices have a "visibility" that acts as an upper bound on 316490c36d8aSUlf Magnusson the values a user can set for them, corresponding to the visibility in e.g. 316590c36d8aSUlf Magnusson 'make menuconfig'. This function calculates the visibility for the Symbol 316690c36d8aSUlf Magnusson or Choice 'sc' -- the logic is nearly identical.""" 316790c36d8aSUlf Magnusson if sc.cached_visibility is None: 316890c36d8aSUlf Magnusson vis = "n" 316990c36d8aSUlf Magnusson for _, cond_expr in sc.prompts: 317090c36d8aSUlf Magnusson vis = sc.config._eval_max(vis, cond_expr) 3171f219e013SMasahiro Yamada 317290c36d8aSUlf Magnusson if isinstance(sc, Symbol) and sc.is_choice_sym: 317390c36d8aSUlf Magnusson vis = sc.config._eval_min(vis, _get_visibility(sc.parent)) 3174f219e013SMasahiro Yamada 317590c36d8aSUlf Magnusson # Promote "m" to "y" if we're dealing with a non-tristate 317690c36d8aSUlf Magnusson if vis == "m" and sc.type != TRISTATE: 317790c36d8aSUlf Magnusson vis = "y" 3178f219e013SMasahiro Yamada 317990c36d8aSUlf Magnusson sc.cached_visibility = vis 3180f219e013SMasahiro Yamada 318190c36d8aSUlf Magnusson return sc.cached_visibility 3182f219e013SMasahiro Yamada 318390c36d8aSUlf Magnussondef _make_and(e1, e2): 318490c36d8aSUlf Magnusson """Constructs an AND (&&) expression. Performs trivial simplification. 318590c36d8aSUlf Magnusson Nones equate to 'y'. 318690c36d8aSUlf Magnusson 318790c36d8aSUlf Magnusson Note: returns None if e1 == e2 == None.""" 318890c36d8aSUlf Magnusson if e1 is None or e1 == "y": 318990c36d8aSUlf Magnusson return e2 319090c36d8aSUlf Magnusson if e2 is None or e2 == "y": 319190c36d8aSUlf Magnusson return e1 319290c36d8aSUlf Magnusson 319390c36d8aSUlf Magnusson # Prefer to merge argument lists if possible to reduce the number of nodes 319490c36d8aSUlf Magnusson 319590c36d8aSUlf Magnusson if isinstance(e1, tuple) and e1[0] == AND: 319690c36d8aSUlf Magnusson if isinstance(e2, tuple) and e2[0] == AND: 319790c36d8aSUlf Magnusson return (AND, e1[1] + e2[1]) 319890c36d8aSUlf Magnusson return (AND, e1[1] + [e2]) 319990c36d8aSUlf Magnusson 320090c36d8aSUlf Magnusson if isinstance(e2, tuple) and e2[0] == AND: 320190c36d8aSUlf Magnusson return (AND, e2[1] + [e1]) 320290c36d8aSUlf Magnusson 320390c36d8aSUlf Magnusson return (AND, [e1, e2]) 320490c36d8aSUlf Magnusson 320590c36d8aSUlf Magnussondef _make_or(e1, e2): 320690c36d8aSUlf Magnusson """Constructs an OR (||) expression. Performs trivial simplification and 320790c36d8aSUlf Magnusson avoids Nones. Nones equate to 'y', which is usually what we want, but needs 320890c36d8aSUlf Magnusson to be kept in mind.""" 320990c36d8aSUlf Magnusson 321090c36d8aSUlf Magnusson # Perform trivial simplification and avoid None's (which 321190c36d8aSUlf Magnusson # correspond to y's) 321290c36d8aSUlf Magnusson if e1 is None or e2 is None or e1 == "y" or e2 == "y": 321390c36d8aSUlf Magnusson return "y" 321490c36d8aSUlf Magnusson if e1 == "n": 321590c36d8aSUlf Magnusson return e2 321690c36d8aSUlf Magnusson 321790c36d8aSUlf Magnusson # Prefer to merge argument lists if possible to reduce the number of nodes 321890c36d8aSUlf Magnusson 321990c36d8aSUlf Magnusson if isinstance(e1, tuple) and e1[0] == OR: 322090c36d8aSUlf Magnusson if isinstance(e2, tuple) and e2[0] == OR: 322190c36d8aSUlf Magnusson return (OR, e1[1] + e2[1]) 322290c36d8aSUlf Magnusson return (OR, e1[1] + [e2]) 322390c36d8aSUlf Magnusson 322490c36d8aSUlf Magnusson if isinstance(e2, tuple) and e2[0] == OR: 322590c36d8aSUlf Magnusson return (OR, e2[1] + [e1]) 322690c36d8aSUlf Magnusson 322790c36d8aSUlf Magnusson return (OR, [e1, e2]) 322890c36d8aSUlf Magnusson 322990c36d8aSUlf Magnussondef _get_expr_syms_rec(expr, res): 323090c36d8aSUlf Magnusson """_get_expr_syms() helper. Recurses through expressions.""" 323190c36d8aSUlf Magnusson if isinstance(expr, Symbol): 323290c36d8aSUlf Magnusson res.add(expr) 323390c36d8aSUlf Magnusson elif isinstance(expr, str): 323490c36d8aSUlf Magnusson return 323590c36d8aSUlf Magnusson elif expr[0] == AND or expr[0] == OR: 323690c36d8aSUlf Magnusson for term in expr[1]: 323790c36d8aSUlf Magnusson _get_expr_syms_rec(term, res) 323890c36d8aSUlf Magnusson elif expr[0] == NOT: 323990c36d8aSUlf Magnusson _get_expr_syms_rec(expr[1], res) 324090c36d8aSUlf Magnusson elif expr[0] == EQUAL or expr[0] == UNEQUAL: 324190c36d8aSUlf Magnusson if isinstance(expr[1], Symbol): 324290c36d8aSUlf Magnusson res.add(expr[1]) 324390c36d8aSUlf Magnusson if isinstance(expr[2], Symbol): 324490c36d8aSUlf Magnusson res.add(expr[2]) 324590c36d8aSUlf Magnusson else: 324690c36d8aSUlf Magnusson _internal_error("Internal error while fetching symbols from an " 324790c36d8aSUlf Magnusson "expression with token stream {0}.".format(expr)) 324890c36d8aSUlf Magnusson 324990c36d8aSUlf Magnussondef _get_expr_syms(expr): 325090c36d8aSUlf Magnusson """Returns the set() of symbols appearing in expr.""" 325190c36d8aSUlf Magnusson res = set() 325290c36d8aSUlf Magnusson if expr is not None: 325390c36d8aSUlf Magnusson _get_expr_syms_rec(expr, res) 325490c36d8aSUlf Magnusson return res 325590c36d8aSUlf Magnusson 325690c36d8aSUlf Magnussondef _str_val(obj): 325790c36d8aSUlf Magnusson """Returns the value of obj as a string. If obj is not a string (constant 325890c36d8aSUlf Magnusson symbol), it must be a Symbol.""" 325990c36d8aSUlf Magnusson return obj if isinstance(obj, str) else obj.get_value() 326090c36d8aSUlf Magnusson 326190c36d8aSUlf Magnussondef _make_block_conf(block, append_fn): 326290c36d8aSUlf Magnusson """Returns a list of .config strings for a block (list) of items.""" 326390c36d8aSUlf Magnusson 326490c36d8aSUlf Magnusson # Collect the substrings in a list and later use join() instead of += to 326590c36d8aSUlf Magnusson # build the final .config contents. With older Python versions, this yields 326690c36d8aSUlf Magnusson # linear instead of quadratic complexity. 326790c36d8aSUlf Magnusson for item in block: 326890c36d8aSUlf Magnusson item._make_conf(append_fn) 326990c36d8aSUlf Magnusson 327090c36d8aSUlf Magnussondef _sym_str_string(sym_or_str): 327190c36d8aSUlf Magnusson if isinstance(sym_or_str, str): 327290c36d8aSUlf Magnusson return '"' + sym_or_str + '"' 327390c36d8aSUlf Magnusson return sym_or_str.name 327490c36d8aSUlf Magnusson 327590c36d8aSUlf Magnussondef _intersperse(lst, op): 327690c36d8aSUlf Magnusson """_expr_to_str() helper. Gets the string representation of each expression 327790c36d8aSUlf Magnusson in lst and produces a list where op has been inserted between the 327890c36d8aSUlf Magnusson elements.""" 327990c36d8aSUlf Magnusson if not lst: 3280f219e013SMasahiro Yamada return "" 328190c36d8aSUlf Magnusson 328290c36d8aSUlf Magnusson res = [] 328390c36d8aSUlf Magnusson 328490c36d8aSUlf Magnusson def handle_sub_expr(expr): 328590c36d8aSUlf Magnusson no_parens = isinstance(expr, (str, Symbol)) or \ 328690c36d8aSUlf Magnusson expr[0] in (EQUAL, UNEQUAL) or \ 328790c36d8aSUlf Magnusson PRECEDENCE[op] <= PRECEDENCE[expr[0]] 328890c36d8aSUlf Magnusson if not no_parens: 328990c36d8aSUlf Magnusson res.append("(") 329090c36d8aSUlf Magnusson res.extend(_expr_to_str_rec(expr)) 329190c36d8aSUlf Magnusson if not no_parens: 329290c36d8aSUlf Magnusson res.append(")") 329390c36d8aSUlf Magnusson 329490c36d8aSUlf Magnusson op_str = OP_TO_STR[op] 329590c36d8aSUlf Magnusson 329690c36d8aSUlf Magnusson handle_sub_expr(lst[0]) 329790c36d8aSUlf Magnusson for expr in lst[1:]: 329890c36d8aSUlf Magnusson res.append(op_str) 329990c36d8aSUlf Magnusson handle_sub_expr(expr) 330090c36d8aSUlf Magnusson 330190c36d8aSUlf Magnusson return res 330290c36d8aSUlf Magnusson 330390c36d8aSUlf Magnussondef _expr_to_str_rec(expr): 330490c36d8aSUlf Magnusson if expr is None: 330590c36d8aSUlf Magnusson return [""] 330690c36d8aSUlf Magnusson 330790c36d8aSUlf Magnusson if isinstance(expr, (Symbol, str)): 330890c36d8aSUlf Magnusson return [_sym_str_string(expr)] 330990c36d8aSUlf Magnusson 331090c36d8aSUlf Magnusson if expr[0] in (AND, OR): 331190c36d8aSUlf Magnusson return _intersperse(expr[1], expr[0]) 331290c36d8aSUlf Magnusson 331390c36d8aSUlf Magnusson if expr[0] == NOT: 331490c36d8aSUlf Magnusson need_parens = not isinstance(expr[1], (str, Symbol)) 331590c36d8aSUlf Magnusson 331690c36d8aSUlf Magnusson res = ["!"] 331790c36d8aSUlf Magnusson if need_parens: 331890c36d8aSUlf Magnusson res.append("(") 331990c36d8aSUlf Magnusson res.extend(_expr_to_str_rec(expr[1])) 332090c36d8aSUlf Magnusson if need_parens: 332190c36d8aSUlf Magnusson res.append(")") 332290c36d8aSUlf Magnusson return res 332390c36d8aSUlf Magnusson 332490c36d8aSUlf Magnusson if expr[0] in (EQUAL, UNEQUAL): 332590c36d8aSUlf Magnusson return [_sym_str_string(expr[1]), 332690c36d8aSUlf Magnusson OP_TO_STR[expr[0]], 332790c36d8aSUlf Magnusson _sym_str_string(expr[2])] 332890c36d8aSUlf Magnusson 332990c36d8aSUlf Magnussondef _expr_to_str(expr): 333090c36d8aSUlf Magnusson return "".join(_expr_to_str_rec(expr)) 3331f219e013SMasahiro Yamada 3332f219e013SMasahiro Yamadadef _indentation(line): 333390c36d8aSUlf Magnusson """Returns the length of the line's leading whitespace, treating tab stops 333490c36d8aSUlf Magnusson as being spaced 8 characters apart.""" 333590c36d8aSUlf Magnusson line = line.expandtabs() 333690c36d8aSUlf Magnusson return len(line) - len(line.lstrip()) 3337f219e013SMasahiro Yamada 3338f219e013SMasahiro Yamadadef _deindent(line, indent): 3339f219e013SMasahiro Yamada """Deindent 'line' by 'indent' spaces.""" 3340f219e013SMasahiro Yamada line = line.expandtabs() 3341f219e013SMasahiro Yamada if len(line) <= indent: 3342f219e013SMasahiro Yamada return line 3343f219e013SMasahiro Yamada return line[indent:] 3344f219e013SMasahiro Yamada 3345f219e013SMasahiro Yamadadef _is_base_n(s, n): 3346f219e013SMasahiro Yamada try: 3347f219e013SMasahiro Yamada int(s, n) 3348f219e013SMasahiro Yamada return True 3349f219e013SMasahiro Yamada except ValueError: 3350f219e013SMasahiro Yamada return False 3351f219e013SMasahiro Yamada 335290c36d8aSUlf Magnussondef _lines(*args): 335390c36d8aSUlf Magnusson """Returns a string consisting of all arguments, with newlines inserted 3354f219e013SMasahiro Yamada between them.""" 3355f219e013SMasahiro Yamada return "\n".join(args) 3356f219e013SMasahiro Yamada 3357f219e013SMasahiro Yamadadef _comment(s): 3358f219e013SMasahiro Yamada """Returns a new string with "#" inserted before each line in 's'.""" 3359f219e013SMasahiro Yamada if not s: 3360f219e013SMasahiro Yamada return "#" 3361f219e013SMasahiro Yamada res = "".join(["#" + line for line in s.splitlines(True)]) 3362f219e013SMasahiro Yamada if s.endswith("\n"): 3363f219e013SMasahiro Yamada return res + "#" 3364f219e013SMasahiro Yamada return res 3365f219e013SMasahiro Yamada 3366f219e013SMasahiro Yamadadef _clean_up_path(path): 336790c36d8aSUlf Magnusson """Strips an initial "./" and any trailing slashes from 'path'.""" 3368f219e013SMasahiro Yamada if path.startswith("./"): 3369f219e013SMasahiro Yamada path = path[2:] 337090c36d8aSUlf Magnusson return path.rstrip("/") 3371f219e013SMasahiro Yamada 337290c36d8aSUlf Magnussondef _stderr_msg(msg, filename, linenr): 3373f219e013SMasahiro Yamada if filename is not None: 337490c36d8aSUlf Magnusson sys.stderr.write("{0}:{1}: ".format(_clean_up_path(filename), linenr)) 337590c36d8aSUlf Magnusson sys.stderr.write(msg + "\n") 3376f219e013SMasahiro Yamada 337790c36d8aSUlf Magnussondef _tokenization_error(s, filename, linenr): 337890c36d8aSUlf Magnusson loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) 337990c36d8aSUlf Magnusson raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'" 338090c36d8aSUlf Magnusson .format(loc, s.strip())) 3381f219e013SMasahiro Yamada 3382f219e013SMasahiro Yamadadef _parse_error(s, msg, filename, linenr): 338390c36d8aSUlf Magnusson loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) 338490c36d8aSUlf Magnusson raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}" 338590c36d8aSUlf Magnusson .format(loc, s.strip(), 338690c36d8aSUlf Magnusson "." if msg is None else ": " + msg)) 3387f219e013SMasahiro Yamada 3388f219e013SMasahiro Yamadadef _internal_error(msg): 338990c36d8aSUlf Magnusson raise Internal_Error(msg + 339090c36d8aSUlf Magnusson "\nSorry! You may want to send an email to ulfalizer a.t Google's " 339190c36d8aSUlf Magnusson "email service to tell me about this. Include the message above and the " 339290c36d8aSUlf Magnusson "stack trace and describe what you were doing.") 3393f219e013SMasahiro Yamada 339490c36d8aSUlf Magnusson# 339590c36d8aSUlf Magnusson# Internal global constants 339690c36d8aSUlf Magnusson# 3397f219e013SMasahiro Yamada 339890c36d8aSUlf Magnusson# Tokens 339990c36d8aSUlf Magnusson(T_AND, T_OR, T_NOT, 340090c36d8aSUlf Magnusson T_OPEN_PAREN, T_CLOSE_PAREN, 340190c36d8aSUlf Magnusson T_EQUAL, T_UNEQUAL, 340290c36d8aSUlf Magnusson T_MAINMENU, T_MENU, T_ENDMENU, 340390c36d8aSUlf Magnusson T_SOURCE, T_CHOICE, T_ENDCHOICE, 340490c36d8aSUlf Magnusson T_COMMENT, T_CONFIG, T_MENUCONFIG, 340590c36d8aSUlf Magnusson T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON, 340690c36d8aSUlf Magnusson T_OPTIONAL, T_PROMPT, T_DEFAULT, 340790c36d8aSUlf Magnusson T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING, 340890c36d8aSUlf Magnusson T_DEF_BOOL, T_DEF_TRISTATE, 3409*d036107aSTom Rini T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV, 3410*d036107aSTom Rini T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40) 3411f219e013SMasahiro Yamada 341290c36d8aSUlf Magnusson# The leading underscore before the function assignments below prevent pydoc 341390c36d8aSUlf Magnusson# from listing them. The constants could be hidden too, but they're fairly 341490c36d8aSUlf Magnusson# obviously internal anyway, so don't bother spamming the code. 3415f219e013SMasahiro Yamada 341690c36d8aSUlf Magnusson# Keyword to token map. Note that the get() method is assigned directly as a 341790c36d8aSUlf Magnusson# small optimization. 341890c36d8aSUlf Magnusson_get_keyword = \ 341990c36d8aSUlf Magnusson {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU, 342090c36d8aSUlf Magnusson "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE, 342190c36d8aSUlf Magnusson "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT, 342290c36d8aSUlf Magnusson "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF, 342390c36d8aSUlf Magnusson "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL, 342490c36d8aSUlf Magnusson "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL, 342590c36d8aSUlf Magnusson "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL, 342690c36d8aSUlf Magnusson "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT, 3427*d036107aSTom Rini "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION, 3428*d036107aSTom Rini "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV, 3429*d036107aSTom Rini "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES, 343090c36d8aSUlf Magnusson "visible": T_VISIBLE}.get 343190c36d8aSUlf Magnusson 343290c36d8aSUlf Magnusson# Strings to use for True and False 343390c36d8aSUlf MagnussonBOOL_STR = {False: "false", True: "true"} 343490c36d8aSUlf Magnusson 343590c36d8aSUlf Magnusson# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE 343690c36d8aSUlf Magnusson# is included to avoid symbols being registered for named choices. 343790c36d8aSUlf MagnussonSTRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE, 343890c36d8aSUlf Magnusson T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU)) 343990c36d8aSUlf Magnusson 344090c36d8aSUlf Magnusson# Matches the initial token on a line; see _tokenize(). Also eats trailing 344190c36d8aSUlf Magnusson# whitespace as an optimization. 344290c36d8aSUlf Magnusson_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match 344390c36d8aSUlf Magnusson 344490c36d8aSUlf Magnusson# Matches an identifier/keyword optionally preceded by whitespace. Also eats 344590c36d8aSUlf Magnusson# trailing whitespace as an optimization. 344690c36d8aSUlf Magnusson_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match 344790c36d8aSUlf Magnusson 344890c36d8aSUlf Magnusson# Regular expressions for parsing .config files 344990c36d8aSUlf Magnusson_set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match 345090c36d8aSUlf Magnusson_unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match 345190c36d8aSUlf Magnusson 345290c36d8aSUlf Magnusson# Regular expression for finding $-references to symbols in strings 345390c36d8aSUlf Magnusson_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search 345490c36d8aSUlf Magnusson 345590c36d8aSUlf Magnusson# Integers representing symbol types 345690c36d8aSUlf MagnussonUNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6) 345790c36d8aSUlf Magnusson 345890c36d8aSUlf Magnusson# Strings to use for types 345990c36d8aSUlf MagnussonTYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate", 346090c36d8aSUlf Magnusson STRING: "string", HEX: "hex", INT: "int"} 346190c36d8aSUlf Magnusson 346290c36d8aSUlf Magnusson# Token to type mapping 346390c36d8aSUlf MagnussonTOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING, 346490c36d8aSUlf Magnusson T_INT: INT, T_HEX: HEX} 346590c36d8aSUlf Magnusson 346690c36d8aSUlf Magnusson# Default values for symbols of different types (the value the symbol gets if 346790c36d8aSUlf Magnusson# it is not assigned a user value and none of its 'default' clauses kick in) 346890c36d8aSUlf MagnussonDEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""} 346990c36d8aSUlf Magnusson 347090c36d8aSUlf Magnusson# Indicates that no item is selected in a choice statement 347190c36d8aSUlf MagnussonNO_SELECTION = 0 347290c36d8aSUlf Magnusson 347390c36d8aSUlf Magnusson# Integers representing expression types 347490c36d8aSUlf MagnussonAND, OR, NOT, EQUAL, UNEQUAL = range(5) 347590c36d8aSUlf Magnusson 347690c36d8aSUlf Magnusson# Map from tristate values to integers 347790c36d8aSUlf MagnussonTRI_TO_INT = {"n": 0, "m": 1, "y": 2} 347890c36d8aSUlf Magnusson 347990c36d8aSUlf Magnusson# Printing-related stuff 348090c36d8aSUlf Magnusson 348190c36d8aSUlf MagnussonOP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "} 348290c36d8aSUlf MagnussonPRECEDENCE = {OR: 0, AND: 1, NOT: 2} 3483