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