kconfiglib: update to the latest version
PATCH 1/1 kconfiglib: update to the latest version
Corresponds to 2f319b8 in https://github.com/ulfalizer/Kconfiglib.
Fixes:
- Unset user values when loading a zero-byte .config. (5e54e2c)
- Ignore indented .config assignments. (f8a7510)
- Do not require $srctree to be set for non-kernel projects. (d56e9c1)
Also adds Python 3 support and has a lot of internal cleanup and optimization. Makes tools/genboardscfg.py run slightly faster.
--- tools/buildman/kconfiglib.py | 2898 +++++++++++++++++++----------------------- 1 file changed, 1329 insertions(+), 1569 deletions(-)
tools/buildman/kconfiglib.py +1329 -1569
@@ -36,57 +36,61 @@ Kconfig-based configuration systems. Features include the following: For the Linux kernel, scripts are run using - $ make scriptconfig SCRIPT=<path to script> [SCRIPT_ARG=<arg>] + $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>] -Running scripts via the 'scriptconfig' target ensures that required environment -variables (SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. -Alternative architectures can be specified like for other 'make *config' -targets: +Using the 'scriptconfig' target ensures that required environment variables +(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. - $ make scriptconfig ARCH=mips SCRIPT=<path to script> [SCRIPT_ARG=<arg>] - -The script will receive the name of the Kconfig file to load in sys.argv[1]. -(As of Linux 3.7.0-rc8 this is always "Kconfig" from the kernel top-level -directory.) If an argument is provided with SCRIPT_ARG, it will appear in -sys.argv[2]. +Scripts receive the name of the Kconfig file to load in sys.argv[1]. As of +Linux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory. +If an argument is provided with SCRIPT_ARG, it appears as sys.argv[2]. To get an interactive Python prompt with Kconfiglib preloaded and a Config -object 'c' created, use +object 'c' created, run - $ make iscriptconfig [ARCH=<architecture>] + $ make iscriptconfig [ARCH=<arch>] -Kconfiglib requires Python 2. For (i)scriptconfig the command to run the Python -interpreter can be passed in the environment variable PYTHONCMD (defaults to -'python'; PyPy works too and is a bit faster). +Kconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python +interpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy +works well too, and might give a nice speedup for long-running jobs. -Look in the examples/ subdirectory for examples, which can be run with e.g. +The examples/ directory contains short example scripts, which can be run with +e.g. $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py or - $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG="kernel" + $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel -Look in testsuite.py for the test suite. +testsuite.py contains the test suite. See the top of the script for how to run +it. Credits: Written by Ulf "Ulfalizer" Magnusson -Send bug reports, suggestions and other feedback to kconfiglib at gmail.com . -Don't wrestle with internal APIs. Tell me what you need and I might add it in a -safe way as a client API instead.""" - -# If you have Psyco installed (32-bit installations, Python <= 2.6 only), -# setting this to True (right here, not@runtime) might give a nice speedup. -# (22% faster for parsing arch/x86/Kconfig and 58% faster for evaluating all -# symbols in it without a .config on my Core Duo.) -use_psyco = False +Send bug reports, suggestions and other feedback to ulfalizer a.t Google's +email service. Don't wrestle with internal APIs. Tell me what you need and I +might add it in a safe way as a client API instead.""" import os import re -import string import sys -class Config(): +# File layout: +# +# Public classes +# Public functions +# Internal classes +# Internal functions +# Internal global constants + +# Line length: 79 columns + +# +# Public classes +# + +class Config(object): """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the set of symbols and other items appearing in the configuration together with
@@ -97,31 +101,30 @@ class Config(): # Public interface # - def __init__(self, - filename = "Kconfig", - base_dir = "$srctree", - print_warnings = True, - print_undef_assign = False): + def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True, + print_undef_assign=False): """Creates a new Config object, representing a Kconfig configuration. Raises Kconfig_Syntax_Error on syntax errors. filename (default: "Kconfig") -- The base Kconfig file of the - configuration. For the Linux kernel, this should usually be be + configuration. For the Linux kernel, you'll probably want "Kconfig" from the top-level directory, as environment variables will make sure the right Kconfig is included from - there (usually arch/<architecture>/Kconfig). If you are using - kconfiglib via 'make scriptconfig' the filename of the - correct Kconfig will be in sys.argv[1]. - - base_dir (default: "$srctree") -- The base directory relative to which - 'source' statements within Kconfig files will work. For the - Linux kernel this should be the top-level directory of the - kernel tree. $-references to environment variables will be - expanded. - - The environment variable 'srctree' is set by the Linux makefiles - to the top-level kernel directory. A default of "." would not - work if an alternative build directory is used. + there (arch/<architecture>/Kconfig). If you are using + kconfiglib via 'make scriptconfig', the filename of the base + base Kconfig file will be in sys.argv[1]. + + base_dir (default: None) -- The base directory relative to which + 'source' statements within Kconfig files will work. For the + Linux kernel this should be the top-level directory of the + kernel tree. $-references to existing environment variables + will be expanded. + + If None (the default), the environment variable 'srctree' will + be used if set, and the current directory otherwise. 'srctree' + is set by the Linux makefiles to the top-level kernel + directory. A default of "." would not work with an alternative + build directory. print_warnings (default: True) -- Set to True if warnings related to this configuration should be printed to stderr. This can
@@ -138,6 +141,12 @@ class Config(): # The set of all symbols, indexed by name (a string) self.syms = {} + # Python 2/3 compatibility hack. This is the only one needed. + if sys.version_info[0] >= 3: + self.syms_iter = self.syms.values + else: + self.syms_iter = self.syms.itervalues + # The set of all defined symbols in the configuration in the order they # appear in the Kconfig files. This excludes the special symbols n, m, # and y as well as symbols that are referenced but never defined.
@@ -147,14 +156,14 @@ class Config(): # by name (a string) self.named_choices = {} - def register_special_symbol(type, name, value): + def register_special_symbol(type_, name, val): sym = Symbol() sym.is_special_ = True sym.is_defined_ = True sym.config = self sym.name = name - sym.type = type - sym.cached_value = value + sym.type = type_ + sym.cached_val = val self.syms[name] = sym return sym
@@ -172,7 +181,7 @@ class Config(): self.defconfig_sym = None # See Symbol.get_(src)arch() - self.arch = os.environ.get("ARCH") + self.arch = os.environ.get("ARCH") self.srcarch = os.environ.get("SRCARCH") # See Config.__init__(). We need this for get_defconfig_filename().
@@ -181,7 +190,10 @@ class Config(): self.srctree = "." self.filename = filename - self.base_dir = _strip_trailing_slash(os.path.expandvars(base_dir)) + if base_dir is None: + self.base_dir = self.srctree + else: + self.base_dir = os.path.expandvars(base_dir) # The 'mainmenu' text self.mainmenu_text = None
@@ -210,11 +222,11 @@ class Config(): self.end_line_tokens = None # See the comment in _parse_expr(). - self.parse_expr_cur_sym_or_choice = None - self.parse_expr_line = None - self.parse_expr_filename = None - self.parse_expr_linenr = None - self.parse_expr_transform_m = None + self._cur_item = None + self._line = None + self._filename = None + self._linenr = None + self._transform_m = None # Parse the Kconfig files self.top_block = self._parse_file(filename, None, None, None)
@@ -222,148 +234,144 @@ class Config(): # Build Symbol.dep for all symbols self._build_dep() - def load_config(self, filename, replace = True): + def load_config(self, filename, replace=True): """Loads symbol values from a file in the familiar .config format. - Equivalent to calling Symbol.set_user_value() to set each of the - values. - - filename -- The .config file to load. $-references to environment - variables will be expanded. For scripts to work even - when an alternative build directory is used with the - Linux kernel, you need to refer to the top-level kernel - directory with "$srctree". + Equivalent to calling Symbol.set_user_value() to set each of the + values. - replace (default: True) -- True if the configuration should replace - the old configuration; False if it should add to it.""" + "# CONFIG_FOO is not set" within a .config file is treated specially + and sets the user value of FOO to 'n'. The C implementation works the + same way. - def warn_override(filename, linenr, name, old_user_val, new_user_val): - self._warn("overriding the value of {0}. " - 'Old value: "{1}", new value: "{2}".' - .format(name, old_user_val, new_user_val), - filename, - linenr) + filename -- The .config file to load. $-references to environment + variables will be expanded. For scripts to work even when + an alternative build directory is used with the Linux + kernel, you need to refer to the top-level kernel directory + with "$srctree". - filename = os.path.expandvars(filename) + replace (default: True) -- True if the configuration should replace + the old configuration; False if it should add to it.""" # Put this first so that a missing file doesn't screw up our state - line_feeder = _FileFeed(_get_lines(filename), filename) + filename = os.path.expandvars(filename) + line_feeder = _FileFeed(filename) self.config_filename = filename - # Invalidate everything. This is usually faster than finding the - # minimal set of symbols that needs to be invalidated, as nearly all - # symbols will tend to be affected anyway. - if replace: - self.unset_user_values() - else: - self._invalidate_all() - + # # Read header - - self.config_header = None + # def is_header_line(line): - return line.startswith("#") and \ - not unset_re.match(line) - - first_line = line_feeder.get_next() - - if first_line is None: - return - - if not is_header_line(first_line): - line_feeder.go_back() - else: - self.config_header = first_line[1:] + return line is not None and line.startswith("#") and \ + not _unset_re_match(line) - # Read remaining header lines - while 1: - line = line_feeder.get_next() - - if line is None: - break - - if not is_header_line(line): - line_feeder.go_back() - break - - self.config_header += line[1:] + self.config_header = None + line = line_feeder.peek_next() + if is_header_line(line): + self.config_header = "" + while is_header_line(line_feeder.peek_next()): + self.config_header += line_feeder.get_next()[1:] # Remove trailing newline if self.config_header.endswith("\n"): self.config_header = self.config_header[:-1] - # Read assignments + # + # Read assignments. Hotspot for some workloads. + # - filename = line_feeder.get_filename() + def warn_override(filename, linenr, name, old_user_val, new_user_val): + self._warn('overriding the value of {0}. ' + 'Old value: "{1}", new value: "{2}".' + .format(name, old_user_val, new_user_val), + filename, linenr) + + # Invalidate everything to keep things simple. It might be possible to + # improve performance for the case where multiple configurations are + # loaded by only invalidating a symbol (and its dependent symbols) if + # the new user value differs from the old. One complication would be + # that symbols not mentioned in the .config must lose their user value + # when replace = True, which is the usual case. + if replace: + self.unset_user_values() + else: + self._invalidate_all() while 1: line = line_feeder.get_next() if line is None: return - linenr = line_feeder.get_linenr() + line = line.rstrip() - line = line.strip() + set_match = _set_re_match(line) + if set_match: + name, val = set_match.groups() + + if val.startswith(('"', "'")): + if len(val) < 2 or val[-1] != val[0]: + _parse_error(line, "malformed string literal", + line_feeder.get_filename(), + line_feeder.get_linenr()) + # Strip quotes and remove escapings. The unescaping + # procedure should be safe since " can only appear as \" + # inside the string. + val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\") - set_re_match = set_re.match(line) - if set_re_match: - name, val = set_re_match.groups() - # The unescaping producedure below should be safe since " can - # only appear as \" inside the string - val = _strip_quotes(val, line, filename, linenr)\ - .replace('\\"', '"').replace("\\\\", "\\") if name in self.syms: sym = self.syms[name] - - old_user_val = sym.user_val - if old_user_val is not None: - warn_override(filename, linenr, name, old_user_val, val) + if sym.user_val is not None: + warn_override(line_feeder.get_filename(), + line_feeder.get_linenr(), + name, sym.user_val, val) if sym.is_choice_symbol_: user_mode = sym.parent.user_mode if user_mode is not None and user_mode != val: - self._warn("assignment to {0} changes mode of containing " - 'choice from "{1}" to "{2}".' + self._warn("assignment to {0} changes mode of " + 'containing choice from "{1}" to "{2}".' .format(name, val, user_mode), - filename, - linenr) + line_feeder.get_filename(), + line_feeder.get_linenr()) sym._set_user_value_no_invalidate(val, True) - else: - self._undef_assign('attempt to assign the value "{0}" to the ' - "undefined symbol {1}." - .format(val, name), - filename, - linenr) - + if self.print_undef_assign: + _stderr_msg('note: attempt to assign the value "{0}" ' + "to the undefined symbol {1}." + .format(val, name), + line_feeder.get_filename(), + line_feeder.get_linenr()) else: - unset_re_match = unset_re.match(line) - if unset_re_match: - name = unset_re_match.group(1) + unset_match = _unset_re_match(line) + if unset_match: + name = unset_match.group(1) if name in self.syms: sym = self.syms[name] - - old_user_val = sym.user_val - if old_user_val is not None: - warn_override(filename, linenr, name, old_user_val, "n") + if sym.user_val is not None: + warn_override(line_feeder.get_filename(), + line_feeder.get_linenr(), + name, sym.user_val, "n") sym._set_user_value_no_invalidate("n", True) - def write_config(self, filename, header = None): + def write_config(self, filename, header=None): """Writes out symbol values in the familiar .config format. - filename -- The filename under which to save the configuration. + Kconfiglib makes sure the format matches what the C implementation + would generate, down to whitespace. This eases testing. + + filename -- The filename under which to save the configuration. - header (default: None) -- A textual header that will appear at the - beginning of the file, with each line commented out - automatically. None means no header.""" + header (default: None) -- A textual header that will appear at the + beginning of the file, with each line commented out + automatically. None means no header.""" # already_written is set when _make_conf() is called on a symbol, so # that symbols defined in multiple locations only get one entry in the # .config. We need to reset it prior to writing out a new .config. - for sym in self.syms.itervalues(): + for sym in self.syms_iter(): sym.already_written = False with open(filename, "w") as f:
@@ -373,11 +381,13 @@ class Config(): f.write("\n") # Write configuration. - # (You'd think passing a list around to all the nodes and appending - # to it to avoid copying would be faster, but it's actually a lot - # slower with PyPy, and about as fast with Python. Passing the file - # around is slower too.) - f.write("\n".join(self.top_block._make_conf())) + + # Passing a list around to all the nodes and appending to it to + # avoid copying was surprisingly a lot slower with PyPy, and about + # as fast with Python. Passing the file around was slower too. Been + # a while since I last measured though. + + f.write("\n".join(_make_block_conf(self.top_block))) f.write("\n") def get_kconfig_filename(self):
@@ -395,8 +405,8 @@ class Config(): def get_srcarch(self): """Returns the value the environment variable SRCARCH had@the time the Config instance was created, or None if SRCARCH was not set. For - the kernel, this corresponds to the arch/ subdirectory containing - architecture-specific source code.""" + the kernel, this corresponds to the particular arch/ subdirectory + containing architecture-specific code.""" return self.srcarch def get_srctree(self):
@@ -407,8 +417,8 @@ class Config(): return self.srctree def get_config_filename(self): - """Returns the name of the most recently loaded configuration file, or - None if no configuration has been loaded.""" + """Returns the filename of the most recently loaded configuration file, + or None if no configuration has been loaded.""" return self.config_filename def get_mainmenu_text(self):
@@ -429,24 +439,28 @@ class Config(): If the environment variable 'srctree' was set when the Config was created, get_defconfig_filename() will first look relative to that directory before looking in the current directory; see - Config.__init__().""" - + Config.__init__(). + + WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses + the --defconfig=<defconfig> option when calling the C implementation of + e.g. 'make defconfig'. This option overrides the 'option + defconfig_list' symbol, meaning the result from + get_defconfig_filename() might not match what 'make defconfig' would + use. That probably ought to be worked around somehow, so that this + function always gives the "expected" result.""" if self.defconfig_sym is None: return None - - for (filename, cond_expr) in self.defconfig_sym.def_exprs: + for filename, cond_expr in self.defconfig_sym.def_exprs: if self._eval_expr(cond_expr) == "y": filename = self._expand_sym_refs(filename) - # We first look in $srctree. os.path.join() won't work here as # an absolute path in filename would override $srctree. - srctree_filename = os.path.normpath(self.srctree + "/" + filename) + srctree_filename = os.path.normpath(self.srctree + "/" + + filename) if os.path.exists(srctree_filename): return srctree_filename - if os.path.exists(filename): return filename - return None def get_symbol(self, name):
@@ -457,13 +471,13 @@ class Config(): return self.syms.get(name) def get_top_level_items(self): - """Returns a list containing the items (symbols, menus, choice - statements and comments) at the top level of the configuration -- that - is, all items that do not appear within a menu or choice. The items - appear in the same order as within the configuration.""" - return self.top_block.get_items() + """Returns a list containing the items (symbols, menus, choices, and + comments) at the top level of the configuration -- that is, all items + that do not appear within a menu or choice. The items appear in the + same order as within the configuration.""" + return self.top_block - def get_symbols(self, all_symbols = True): + def get_symbols(self, all_symbols=True): """Returns a list of symbols from the configuration. An alternative for iterating over all defined symbols (in the order of definition) is
@@ -475,12 +489,13 @@ class Config(): for sym in config.get_symbols(False): ... - all_symbols (default: True) -- If True, all symbols - including special - and undefined symbols - will be included in the result, in - an undefined order. If False, only symbols actually defined - and not merely referred to in the configuration will be - included in the result, and will appear in the order that - they are defined within the Kconfig configuration files.""" + all_symbols (default: True) -- If True, all symbols -- including + special and undefined symbols -- will be included in the + result, in an undefined order. If False, only symbols + actually defined and not merely referred to in the + configuration will be included in the result, and will + appear in the order that they are defined within the + Kconfig configuration files.""" return self.syms.values() if all_symbols else self.kconfig_syms def get_choices(self):
@@ -506,23 +521,26 @@ class Config(): For example, if FOO and BAR are tristate symbols at least one of which has the value "y", then config.eval("y && (FOO || BAR)") => "y" - This functions always yields a tristate value. To get the value of + This function always yields a tristate value. To get the value of non-bool, non-tristate symbols, use Symbol.get_value(). The result of this function is consistent with how evaluation works for conditional expressions in the configuration as well as in the C implementation. "m" and m are rewritten as '"m" && MODULES' and 'm && MODULES', respectively, and a result of "m" will get promoted to "y" if - we're running without modules.""" + we're running without modules. + + Syntax checking is somewhat lax, partly to be compatible with lax + parsing in the C implementation.""" return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed - None, # Current symbol or choice + None, # Current symbol/choice s)) # line def get_config_header(self): """Returns the (uncommented) textual header of the .config file most recently loaded with load_config(). Returns None if no .config file has been loaded or if the most recently loaded .config file has no header. - The header comprises all lines up to but not including the first line + The header consists of all lines up to but not including the first line that either 1. Does not start with "#"
@@ -540,8 +558,7 @@ class Config(): things like attempting to assign illegal values to symbols with Symbol.set_user_value()) should be printed to stderr. - print_warnings -- True if warnings should be - printed, otherwise False.""" + print_warnings -- True if warnings should be printed.""" self.print_warnings = print_warnings def set_print_undef_assign(self, print_undef_assign):
@@ -572,42 +589,42 @@ class Config(): def unset_user_values(self): """Resets the values of all symbols, as if Config.load_config() or Symbol.set_user_value() had never been called.""" - for sym in self.syms.itervalues(): + for sym in self.syms_iter(): sym._unset_user_value_no_recursive_invalidate() def __str__(self): """Returns a string containing various information about the Config.""" - return _sep_lines("Configuration", - "File : " + self.filename, - "Base directory : " + self.base_dir, - "Value of $ARCH at creation time : " + - ("(not set)" if self.arch is None else self.arch), - "Value of $SRCARCH at creation time : " + - ("(not set)" if self.srcarch is None else self.srcarch), - "Source tree (derived from $srctree;", - "defaults to '.' if $srctree isn't set) : " + self.srctree, - "Most recently loaded .config : " + - ("(no .config loaded)" if self.config_filename is None else + return _lines("Configuration", + "File : " + + self.filename, + "Base directory : " + + self.base_dir, + "Value of $ARCH at creation time : " + + ("(not set)" if self.arch is None else self.arch), + "Value of $SRCARCH at creation time : " + + ("(not set)" if self.srcarch is None else + self.srcarch), + "Source tree (derived from $srctree;", + "defaults to '.' if $srctree isn't set) : " + + self.srctree, + "Most recently loaded .config : " + + ("(no .config loaded)" + if self.config_filename is None else self.config_filename), - "Print warnings : " + - bool_str[self.print_warnings], - "Print assignments to undefined symbols : " + - bool_str[self.print_undef_assign]) - + "Print warnings : " + + bool_str[self.print_warnings], + "Print assignments to undefined symbols : " + + bool_str[self.print_undef_assign]) # # Private methods # def _invalidate_all(self): - for sym in self.syms.itervalues(): + for sym in self.syms_iter(): sym._invalidate() - def _tokenize(self, - s, - for_eval = False, - filename = None, - linenr = None): + def _tokenize(self, s, for_eval=False, filename=None, linenr=None): """Returns a _Feed instance containing tokens derived from the string 's'. Registers any new symbols encountered (via _sym_lookup()).
@@ -617,7 +634,11 @@ class Config(): for_eval -- True when parsing an expression for a call to Config.eval(), in which case we should not treat the first token specially nor register new symbols.""" - s = s.lstrip() + + # lstrip() would work here too, but removing the '\n' at the end leads + # to earlier termination in the 'while' loop below, saving lots of + # calls + s = s.strip() if s == "" or s[0] == "#": return _Feed([])
@@ -628,21 +649,21 @@ class Config(): else: # The initial word on a line is parsed specially. Let # command_chars = [A-Za-z0-9_]. Then - # - leading non-command_chars characters on the line are ignored, and - # - the first token consists the following one or more command_chars - # characters. + # - leading non-command_chars characters are ignored, and + # - the first token consists the following one or more + # command_chars characters. # This is why things like "----help--" are accepted. - initial_token_match = initial_token_re.match(s) + initial_token_match = _initial_token_re_match(s) if initial_token_match is None: return _Feed([]) # The current index in the string being tokenized i = initial_token_match.end() - keyword = keywords.get(initial_token_match.group(1)) + keyword = _get_keyword(initial_token_match.group(1)) if keyword is None: # We expect a keyword as the first token - _tokenization_error(s, len(s), filename, linenr) + _tokenization_error(s, filename, linenr) if keyword == T_HELP: # Avoid junk after "help", e.g. "---", being registered as a # symbol
@@ -659,7 +680,7 @@ class Config(): while i < strlen: # Test for an identifier/keyword preceded by whitespace first; this # is the most common case. - id_keyword_match = id_keyword_re.match(s, i) + id_keyword_match = _id_keyword_re_match(s, i) if id_keyword_match: # We have an identifier or keyword. The above also stripped any # whitespace for us.
@@ -668,7 +689,7 @@ class Config(): i = id_keyword_match.end() # Keyword? - keyword = keywords.get(name) + keyword = _get_keyword(name) if keyword is not None: append(keyword) # What would ordinarily be considered a name is treated as a
@@ -700,97 +721,67 @@ class Config(): s = s[i:].lstrip() if s == "": break - strlen = len(s) - i = 0 c = s[0] + i = 1 # String literal (constant symbol) if c == '"' or c == "'": - i += 1 - if "\\" in s: # Slow path: This could probably be sped up, but it's a # very unusual case anyway. quote = c - value = "" + val = "" while 1: - if i >= strlen: - _tokenization_error(s, strlen, filename, - linenr) + if i >= len(s): + _tokenization_error(s, filename, linenr) c = s[i] if c == quote: break if c == "\\": - if i + 1 >= strlen: - _tokenization_error(s, strlen, filename, - linenr) - value += s[i + 1] + if i + 1 >= len(s): + _tokenization_error(s, filename, linenr) + val += s[i + 1] i += 2 else: - value += c + val += c i += 1 i += 1 - append(value) + append(val) else: - # Fast path: If the string contains no backslashes (almost - # always) we can simply look for the matching quote. + # Fast path: If the string contains no backslashes + # (almost always) we can simply look for the matching + # quote. end = s.find(c, i) if end == -1: - _tokenization_error(s, strlen, filename, linenr) + _tokenization_error(s, filename, linenr) append(s[i:end]) i = end + 1 elif c == "&": - if i + 1 >= strlen: - # Invalid characters are ignored - continue - if s[i + 1] != "&": - # Invalid characters are ignored - i += 1 - continue + # Invalid characters are ignored + if i >= len(s) or s[i] != "&": continue append(T_AND) - i += 2 + i += 1 elif c == "|": - if i + 1 >= strlen: - # Invalid characters are ignored - continue - if s[i + 1] != "|": - # Invalid characters are ignored - i += 1 - continue + # Invalid characters are ignored + if i >= len(s) or s[i] != "|": continue append(T_OR) - i += 2 + i += 1 elif c == "!": - if i + 1 >= strlen: - _tokenization_error(s, strlen, filename, linenr) - if s[i + 1] == "=": + if i < len(s) and s[i] == "=": append(T_UNEQUAL) - i += 2 + i += 1 else: append(T_NOT) - i += 1 - - elif c == "=": - append(T_EQUAL) - i += 1 - elif c == "(": - append(T_OPEN_PAREN) - i += 1 - - elif c == ")": - append(T_CLOSE_PAREN) - i += 1 - - elif c == "#": - break + elif c == "=": append(T_EQUAL) + elif c == "(": append(T_OPEN_PAREN) + elif c == ")": append(T_CLOSE_PAREN) + elif c == "#": break # Comment - else: - # Invalid characters are ignored - i += 1 - continue + else: continue # Invalid characters are ignored previous = tokens[-1]
@@ -810,22 +801,17 @@ class Config(): # <expr> '&&' <expr> # <expr> '||' <expr> - def _parse_expr(self, - feed, - cur_sym_or_choice, - line, - filename = None, - linenr = None, - transform_m = True): + def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None, + transform_m=True): """Parse an expression from the tokens in 'feed' using a simple top-down approach. The result has the form (<operator>, <list containing parsed operands>). feed -- _Feed instance containing the tokens for the expression. - cur_sym_or_choice -- The symbol or choice currently being parsed, or - None if we're not parsing a symbol or choice. - Used for recording references to symbols. + cur_item -- The item (Symbol, Choice, Menu, or Comment) currently being + parsed, or None if we're not parsing an item. Used for + recording references to symbols. line -- The line containing the expression being parsed.
@@ -833,19 +819,18 @@ class Config(): linenr (default: None) -- The line number containing the expression. - transform_m (default: False) -- Determines if 'm' should be rewritten to - 'm && MODULES' -- see - parse_val_and_cond().""" + transform_m (default: False) -- Determines if 'm' should be rewritten + to 'm && MODULES' -- see parse_val_and_cond().""" # Use instance variables to avoid having to pass these as arguments # through the top-down parser in _parse_expr_2(), which is tedious and # obfuscates the code. A profiler run shows no noticeable performance # difference. - self.parse_expr_cur_sym_or_choice = cur_sym_or_choice - self.parse_expr_line = line - self.parse_expr_filename = filename - self.parse_expr_linenr = linenr - self.parse_expr_transform_m = transform_m + self._cur_item = cur_item + self._transform_m = transform_m + self._line = line + self._filename = filename + self._linenr = linenr return self._parse_expr_2(feed)
@@ -854,7 +839,6 @@ class Config(): # Keep parsing additional terms while the lookahead is '||' while feed.check(T_OR): or_terms.append(self._parse_or_term(feed)) - return or_terms[0] if len(or_terms) == 1 else (OR, or_terms) def _parse_or_term(self, feed):
@@ -862,129 +846,100 @@ class Config(): # Keep parsing additional terms while the lookahead is '&&' while feed.check(T_AND): and_terms.append(self._parse_factor(feed)) - return and_terms[0] if len(and_terms) == 1 else (AND, and_terms) def _parse_factor(self, feed): - if feed.check(T_OPEN_PAREN): - expr_parse = self._parse_expr_2(feed) + token = feed.get_next() + + if isinstance(token, (Symbol, str)): + if self._cur_item is not None and isinstance(token, Symbol): + self._cur_item.referenced_syms.add(token) + + next_token = feed.peek_next() + # For conditional expressions ('depends on <expr>', + # '... if <expr>', # etc.), "m" and m are rewritten to + # "m" && MODULES. + if next_token != T_EQUAL and next_token != T_UNEQUAL: + if self._transform_m and (token is self.m or token == "m"): + return (AND, ["m", self._sym_lookup("MODULES")]) + return token + + relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL + token_2 = feed.get_next() + if self._cur_item is not None and isinstance(token_2, Symbol): + self._cur_item.referenced_syms.add(token_2) + return (relation, token, token_2) + + if token == T_NOT: + return (NOT, self._parse_factor(feed)) + if token == T_OPEN_PAREN: + expr_parse = self._parse_expr_2(feed) if not feed.check(T_CLOSE_PAREN): - _parse_error(self.parse_expr_line, - "missing end parenthesis.", - self.parse_expr_filename, - self.parse_expr_linenr) - + _parse_error(self._line, "missing end parenthesis", + self._filename, self._linenr) return expr_parse - if feed.check(T_NOT): - return (NOT, self._parse_factor(feed)) - - sym_or_string = feed.get_next() + _parse_error(self._line, "malformed expression", self._filename, + self._linenr) - if not isinstance(sym_or_string, (Symbol, str)): - _parse_error(self.parse_expr_line, - "malformed expression.", - self.parse_expr_filename, - self.parse_expr_linenr) - - if self.parse_expr_cur_sym_or_choice is not None and \ - isinstance(sym_or_string, Symbol): - self.parse_expr_cur_sym_or_choice.referenced_syms.add(sym_or_string) - - next_token = feed.peek_next() - - # For conditional expressions ('depends on <expr>', '... if <expr>', - # etc.), "m" and m are rewritten to "m" && MODULES. - if next_token != T_EQUAL and next_token != T_UNEQUAL: - if self.parse_expr_transform_m and (sym_or_string is self.m or - sym_or_string == "m"): - return (AND, ["m", self._sym_lookup("MODULES")]) - return sym_or_string - - relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL - sym_or_string_2 = feed.get_next() - - if self.parse_expr_cur_sym_or_choice is not None and \ - isinstance(sym_or_string_2, Symbol): - self.parse_expr_cur_sym_or_choice.referenced_syms.add(sym_or_string_2) - - if sym_or_string is self.m: - sym_or_string = "m" - - if sym_or_string_2 is self.m: - sym_or_string_2 = "m" - - return (relation, sym_or_string, sym_or_string_2) - - def _parse_file(self, filename, parent, deps, visible_if_deps, res = None): - """Parse the Kconfig file 'filename'. The result is a _Block with all - items from the file. See _parse_block() for the meaning of the - parameters.""" - line_feeder = _FileFeed(_get_lines(filename), filename) - return self._parse_block(line_feeder, None, parent, deps, visible_if_deps, res) + def _parse_file(self, filename, parent, deps, visible_if_deps, res=None): + """Parses the Kconfig file 'filename'. Returns a list with the Items in + the file. See _parse_block() for the meaning of the parameters.""" + return self._parse_block(_FileFeed(filename), None, parent, deps, + visible_if_deps, res) def _parse_block(self, line_feeder, end_marker, parent, deps, - visible_if_deps = None, res = None): + visible_if_deps=None, res=None): """Parses a block, which is the contents of either a file or an if, - menu, or choice statement. The result is a _Block with the items from - the block. + menu, or choice statement. Returns a list with the Items in the block. end_marker -- The token that ends the block, e.g. T_ENDIF ("endif") for - if's. None for files. + ifs. None for files. parent -- The enclosing menu, choice or if, or None if we're at the top level. - deps -- Dependencies from enclosing menus, choices and if's. + deps -- Dependencies from enclosing menus, choices and ifs. visible_if_deps (default: None) -- 'visible if' dependencies from enclosing menus. - res (default: None) -- The _Block to add items to. If None, a new - _Block is created to hold the items.""" - - block = _Block() if res is None else res + res (default: None) -- The list to add items to. If None, a new list is + created to hold the items.""" - filename = line_feeder.get_filename() + block = [] if res is None else res while 1: - # Do we already have a tokenized line that we determined wasn't # part of whatever we were parsing earlier? See comment in # Config.__init__(). if self.end_line is not None: - assert self.end_line_tokens is not None + line = self.end_line tokens = self.end_line_tokens tokens.go_to_start() - line = self.end_line - linenr = line_feeder.get_linenr() - self.end_line = None self.end_line_tokens = None - else: line = line_feeder.get_next() if line is None: if end_marker is not None: - raise Kconfig_Syntax_Error, ( + raise Kconfig_Syntax_Error( "Unexpected end of file {0}." .format(line_feeder.get_filename())) return block - linenr = line_feeder.get_linenr() - - tokens = self._tokenize(line, False, filename, linenr) - - if tokens.is_empty(): - continue + tokens = self._tokenize(line, False, + line_feeder.get_filename(), + line_feeder.get_linenr()) t0 = tokens.get_next() + if t0 is None: + continue - # Have we reached the end of the block? - if t0 == end_marker: - return block + # Cases are ordered roughly by frequency, which speeds things up a + # bit if t0 == T_CONFIG or t0 == T_MENUCONFIG: # The tokenizer will automatically allocate a new Symbol object
@@ -993,86 +948,117 @@ class Config(): sym = tokens.get_next() # Symbols defined in multiple places get the parent of their - # first definition. However, for symbols whose parents are choice - # statements, the choice statement takes precedence. + # first definition. However, for symbols whose parents are + # choice statements, the choice statement takes precedence. if not sym.is_defined_ or isinstance(parent, Choice): sym.parent = parent sym.is_defined_ = True self.kconfig_syms.append(sym) - block.add_item(sym) + block.append(sym) self._parse_properties(line_feeder, sym, deps, visible_if_deps) + elif t0 == T_SOURCE: + kconfig_file = tokens.get_next() + exp_kconfig_file = self._expand_sym_refs(kconfig_file) + f = os.path.join(self.base_dir, exp_kconfig_file) + if not os.path.exists(f): + raise IOError('{0}:{1}: sourced file "{2}" (expands to ' + '"{3}") not found. Perhaps base_dir ' + '(argument to Config.__init__(), currently ' + '"{4}") is set to the wrong value.' + .format(line_feeder.get_filename(), + line_feeder.get_linenr(), + kconfig_file, exp_kconfig_file, + self.base_dir)) + + # Add items to the same block + self._parse_file(f, parent, deps, visible_if_deps, block) + + elif t0 == end_marker: + # We have reached the end of the block + return block + + elif t0 == T_IF: + # If statements are treated as syntactic sugar for adding + # dependencies to enclosed items and do not have an explicit + # object representation. + + dep_expr = self._parse_expr(tokens, None, line, + line_feeder.get_filename(), + line_feeder.get_linenr()) + # Add items to the same block + self._parse_block(line_feeder, T_ENDIF, parent, + _make_and(dep_expr, deps), + visible_if_deps, block) + + elif t0 == T_COMMENT: + comment = Comment() + + comment.config = self + comment.parent = parent + comment.filename = line_feeder.get_filename() + comment.linenr = line_feeder.get_linenr() + comment.text = tokens.get_next() + + self.comments.append(comment) + block.append(comment) + + self._parse_properties(line_feeder, comment, deps, + visible_if_deps) + elif t0 == T_MENU: menu = Menu() - self.menus.append(menu) + menu.config = self menu.parent = parent + menu.filename = line_feeder.get_filename() + menu.linenr = line_feeder.get_linenr() menu.title = tokens.get_next() - menu.filename = filename - menu.linenr = linenr + self.menus.append(menu) + block.append(menu) # Parse properties and contents - self._parse_properties(line_feeder, menu, deps, visible_if_deps) - menu.block = self._parse_block(line_feeder, - T_ENDMENU, - menu, + self._parse_properties(line_feeder, menu, deps, + visible_if_deps) + menu.block = self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr, _make_and(visible_if_deps, menu.visible_if_expr)) - block.add_item(menu) - - elif t0 == T_IF: - # If statements are treated as syntactic sugar for adding - # dependencies to enclosed items and do not have an explicit - # object representation. - - dep_expr = self._parse_expr(tokens, None, line, filename, linenr) - self._parse_block(line_feeder, - T_ENDIF, - parent, - _make_and(dep_expr, deps), - visible_if_deps, - block) # Add items to the same block - elif t0 == T_CHOICE: - # We support named choices - already_defined = False - name = None - if len(tokens) > 1 and isinstance(tokens[1], str): - name = tokens[1] - already_defined = name in self.named_choices - - if already_defined: - choice = self.named_choices[name] - else: + name = tokens.get_next() + if name is None: choice = Choice() self.choices.append(choice) - if name is not None: + else: + # Named choice + choice = self.named_choices.get(name) + if choice is None: + choice = Choice() choice.name = name self.named_choices[name] = choice + self.choices.append(choice) choice.config = self choice.parent = parent - choice.def_locations.append((filename, linenr)) + choice.def_locations.append((line_feeder.get_filename(), + line_feeder.get_linenr())) # Parse properties and contents - self._parse_properties(line_feeder, choice, deps, visible_if_deps) - choice.block = self._parse_block(line_feeder, - T_ENDCHOICE, - choice, - None, - visible_if_deps) + self._parse_properties(line_feeder, choice, deps, + visible_if_deps) + choice.block = self._parse_block(line_feeder, T_ENDCHOICE, + choice, deps, visible_if_deps) choice._determine_actual_symbols() - # If no type is set for the choice, its type is that of the first - # choice item + # If no type is set for the choice, its type is that of the + # first choice item if choice.type == UNKNOWN: for item in choice.get_symbols(): if item.type != UNKNOWN:
@@ -1084,71 +1070,35 @@ class Config(): if item.type == UNKNOWN: item.type = choice.type - # For named choices defined in multiple locations, only record - # at the first definition - if not already_defined: - block.add_item(choice) - - elif t0 == T_COMMENT: - comment = Comment() - comment.config = self - comment.parent = parent - - comment.filename = filename - comment.linenr = linenr - - comment.text = tokens.get_next() - self._parse_properties(line_feeder, comment, deps, visible_if_deps) - - block.add_item(comment) - self.comments.append(comment) - - elif t0 == T_SOURCE: - kconfig_file = tokens.get_next() - exp_kconfig_file = self._expand_sym_refs(kconfig_file) - f = os.path.join(self.base_dir, exp_kconfig_file) - - if not os.path.exists(f): - raise IOError, ('{0}:{1}: sourced file "{2}" (expands to\n' - '"{3}") not found. Perhaps base_dir\n' - '(argument to Config.__init__(), currently\n' - '"{4}") is set to the wrong value.' - .format(filename, - linenr, - kconfig_file, - exp_kconfig_file, - self.base_dir)) - - # Add items to the same block - self._parse_file(f, parent, deps, visible_if_deps, block) + block.append(choice) elif t0 == T_MAINMENU: text = tokens.get_next() - if self.mainmenu_text is not None: self._warn("overriding 'mainmenu' text. " 'Old value: "{0}", new value: "{1}".' - .format(self.mainmenu_text, text), - filename, - linenr) - + .format(self.mainmenu_text, text), + line_feeder.get_filename(), + line_feeder.get_linenr()) self.mainmenu_text = text else: - _parse_error(line, "unrecognized construct.", filename, linenr) + _parse_error(line, "unrecognized construct", + line_feeder.get_filename(), + line_feeder.get_linenr()) def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps): - """Parsing of properties for symbols, menus, choices, and comments.""" + """Parsing of properties for symbols, menus, choices, and comments. + Takes care of propagating dependencies from enclosing menus and ifs.""" def parse_val_and_cond(tokens, line, filename, linenr): """Parses '<expr1> if <expr2>' constructs, where the 'if' part is optional. Returns a tuple containing the parsed expressions, with None as the second element if the 'if' part is missing.""" val = self._parse_expr(tokens, stmt, line, filename, linenr, False) - if tokens.check(T_IF): - return (val, self._parse_expr(tokens, stmt, line, filename, linenr)) - + return (val, self._parse_expr(tokens, stmt, line, filename, + linenr)) return (val, None) # In case the symbol is defined in multiple locations, we need to
@@ -1172,41 +1122,53 @@ class Config(): tokens = self._tokenize(line, False, filename, linenr) - if tokens.is_empty(): + t0 = tokens.get_next() + if t0 is None: continue - t0 = tokens.get_next() + # Cases are ordered roughly by frequency, which speeds things up a + # bit - if t0 == T_HELP: - # Find first non-empty line and get its indentation + if t0 == T_DEPENDS: + if not tokens.check(T_ON): + _parse_error(line, 'expected "on" after "depends"', + filename, linenr) - line_feeder.remove_while(str.isspace) - line = line_feeder.get_next() + parsed_deps = self._parse_expr(tokens, stmt, line, filename, + linenr) + + if isinstance(stmt, (Menu, Comment)): + stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps) + else: + depends_on_expr = _make_and(depends_on_expr, parsed_deps) + elif t0 == T_HELP: + # Find first non-blank (not all-space) line and get its + # indentation + + line_feeder.remove_blank() + line = line_feeder.get_next() if line is None: stmt.help = "" break indent = _indentation(line) - - # If the first non-empty lines has zero indent, there is no - # help text if indent == 0: + # If the first non-empty lines has zero indent, there is no + # help text stmt.help = "" line_feeder.go_back() break - help_lines = [_deindent(line, indent)] - # The help text goes on till the first non-empty line with less # indent + help_lines = [_deindent(line, indent)] while 1: line = line_feeder.get_next() - if (line is None) or \ + if line is None or \ (not line.isspace() and _indentation(line) < indent): stmt.help = "".join(help_lines) break - help_lines.append(_deindent(line, indent)) if line is None:
@@ -1214,39 +1176,6 @@ class Config(): line_feeder.go_back() - elif t0 == T_PROMPT: - # 'prompt' properties override each other within a single - # definition of a symbol, but additional prompts can be added - # by defining the symbol multiple times; hence 'new_prompt' - # instead of 'prompt'. - new_prompt = parse_val_and_cond(tokens, line, filename, linenr) - - elif t0 == T_DEFAULT: - new_def_exprs.append(parse_val_and_cond(tokens, line, filename, linenr)) - - elif t0 == T_DEPENDS: - if not tokens.check(T_ON): - _parse_error(line, 'expected "on" after "depends".', filename, linenr) - - parsed_deps = self._parse_expr(tokens, stmt, line, filename, linenr) - - if isinstance(stmt, (Menu, Comment)): - stmt.dep_expr = _make_and(stmt.dep_expr, parsed_deps) - else: - depends_on_expr = _make_and(depends_on_expr, parsed_deps) - - elif t0 == T_VISIBLE: - if not tokens.check(T_IF): - _parse_error(line, 'expected "if" after "visible".', filename, linenr) - if not isinstance(stmt, Menu): - _parse_error(line, - "'visible if' is only valid for menus.", - filename, - linenr) - - parsed_deps = self._parse_expr(tokens, stmt, line, filename, linenr) - stmt.visible_if_expr = _make_and(stmt.visible_if_expr, parsed_deps) - elif t0 == T_SELECT: target = tokens.get_next()
@@ -1255,15 +1184,33 @@ class Config(): if tokens.check(T_IF): new_selects.append((target, - self._parse_expr(tokens, stmt, line, filename, linenr))) + self._parse_expr(tokens, stmt, line, + filename, linenr))) else: new_selects.append((target, None)) elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING): stmt.type = token_to_type[t0] + if len(tokens) > 1: + new_prompt = parse_val_and_cond(tokens, line, filename, + linenr) + + elif t0 == T_DEFAULT: + new_def_exprs.append(parse_val_and_cond(tokens, line, filename, + linenr)) + elif t0 == T_DEF_BOOL: + stmt.type = BOOL if len(tokens) > 1: - new_prompt = parse_val_and_cond(tokens, line, filename, linenr) + new_def_exprs.append(parse_val_and_cond(tokens, line, + filename, linenr)) + + elif t0 == T_PROMPT: + # 'prompt' properties override each other within a single + # definition of a symbol, but additional prompts can be added + # by defining the symbol multiple times; hence 'new_prompt' + # instead of 'prompt'. + new_prompt = parse_val_and_cond(tokens, line, filename, linenr) elif t0 == T_RANGE: lower = tokens.get_next()
@@ -1273,29 +1220,16 @@ class Config(): if tokens.check(T_IF): stmt.ranges.append((lower, upper, - self._parse_expr(tokens, stmt, line, filename, linenr))) + self._parse_expr(tokens, stmt, line, + filename, linenr))) else: stmt.ranges.append((lower, upper, None)) - elif t0 == T_DEF_BOOL: - stmt.type = BOOL - - if len(tokens) > 1: - new_def_exprs.append(parse_val_and_cond(tokens, line, filename, linenr)) - elif t0 == T_DEF_TRISTATE: stmt.type = TRISTATE - if len(tokens) > 1: - new_def_exprs.append(parse_val_and_cond(tokens, line, filename, linenr)) - - elif t0 == T_OPTIONAL: - if not isinstance(stmt, Choice): - _parse_error(line, - '"optional" is only valid for choices.', - filename, - linenr) - stmt.optional = True + new_def_exprs.append(parse_val_and_cond(tokens, line, + filename, linenr)) elif t0 == T_OPTION: if tokens.check(T_ENV) and tokens.check(T_EQUAL):
@@ -1305,117 +1239,145 @@ class Config(): stmt.is_from_env = True if env_var not in os.environ: - self._warn(""" -The symbol {0} references the non-existent environment variable {1} and will -get the empty string as its value. - -If you're using kconfiglib via 'make (i)scriptconfig' it should have set up the -environment correctly for you. If you still got this message, that might be an -error, and you should e-mail kconfiglib at gmail.com. -.""" .format(stmt.name, env_var), - filename, - linenr) - - stmt.cached_value = "" + self._warn("The symbol {0} references the " + "non-existent environment variable {1} and " + "will get the empty string as its value. " + "If you're using kconfiglib via " + "'make (i)scriptconfig', it should have " + "set up the environment correctly for you. " + "If you still got this message, that " + "might be an error, and you should email " + "ulfalizer a.t Google's email service.""" + .format(stmt.name, env_var), + filename, linenr) + + stmt.cached_val = "" else: - stmt.cached_value = os.environ[env_var] + stmt.cached_val = os.environ[env_var] elif tokens.check(T_DEFCONFIG_LIST): self.defconfig_sym = stmt elif tokens.check(T_MODULES): - self._warn("the 'modules' option is not supported. " - "Let me know if this is a problem for you; " - "it shouldn't be that hard to implement.", - filename, - linenr) + # To reduce warning spam, only warn if 'option modules' is + # set on some symbol that isn't MODULES, which should be + # safe. I haven't run into any projects that make use + # modules besides the kernel yet, and there it's likely to + # keep being called "MODULES". + if stmt.name != "MODULES": + self._warn("the 'modules' option is not supported. " + "Let me know if this is a problem for you; " + "it shouldn't be that hard to implement. " + "(Note that modules are still supported -- " + "Kconfiglib just assumes the symbol name " + "MODULES, like older versions of the C " + "implementation did when 'option modules' " + "wasn't used.)", + filename, linenr) + + elif tokens.check(T_ALLNOCONFIG_Y): + if not isinstance(stmt, Symbol): + _parse_error(line, + "the 'allnoconfig_y' option is only " + "valid for symbols", + filename, + linenr) + stmt.allnoconfig_y = True else: - _parse_error(line, "unrecognized option.", filename, linenr) + _parse_error(line, "unrecognized option", filename, linenr) - else: - # See comment in Config.__init__() - self.end_line = line - self.end_line_tokens = tokens - break + elif t0 == T_VISIBLE: + if not tokens.check(T_IF): + _parse_error(line, 'expected "if" after "visible"', + filename, linenr) + if not isinstance(stmt, Menu): + _parse_error(line, + "'visible if' is only valid for menus", + filename, linenr) - # Propagate dependencies from enclosing menus and if's. + parsed_deps = self._parse_expr(tokens, stmt, line, filename, + linenr) + stmt.visible_if_expr = _make_and(stmt.visible_if_expr, + parsed_deps) - # For menus and comments.. - if isinstance(stmt, (Menu, Comment)): - stmt.orig_deps = stmt.dep_expr - stmt.deps_from_containing = deps - stmt.dep_expr = _make_and(stmt.dep_expr, deps) + elif t0 == T_OPTIONAL: + if not isinstance(stmt, Choice): + _parse_error(line, + '"optional" is only valid for choices', + filename, + linenr) + stmt.optional = True + + else: + # See comment in Config.__init__() + self.end_line = line + self.end_line_tokens = tokens + break + + # Done parsing properties. Now propagate 'depends on' and enclosing + # menu/if dependencies to expressions. - stmt.all_referenced_syms = \ - stmt.referenced_syms | _get_expr_syms(deps) + # The set of symbols referenced directly by the statement plus all + # symbols referenced by enclosing menus and ifs + stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps) - # For symbols and choices.. + # Save original dependencies from enclosing menus and ifs + stmt.deps_from_containing = deps + + if isinstance(stmt, (Menu, Comment)): + stmt.dep_expr = _make_and(stmt.orig_deps, deps) else: + # Symbol or Choice # See comment for 'menu_dep' stmt.menu_dep = depends_on_expr - # Propagate dependencies specified with 'depends on' to any new - # default expressions, prompts, and selections. ("New" since a - # symbol might be defined in multiple places and the dependencies - # should only apply to the local definition.) - - new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr)) - for (val_expr, cond_expr) in new_def_exprs] - - new_selects = [(target, _make_and(cond_expr, depends_on_expr)) - for (target, cond_expr) in new_selects] + # Propagate dependencies to prompts if new_prompt is not None: + # Propagate 'visible if' dependencies from enclosing menus prompt, cond_expr = new_prompt - - # 'visible if' dependencies from enclosing menus get propagated - # to prompts - if visible_if_deps is not None: - cond_expr = _make_and(cond_expr, visible_if_deps) - + cond_expr = _make_and(cond_expr, visible_if_deps) + # Propagate 'depends on' dependencies new_prompt = (prompt, _make_and(cond_expr, depends_on_expr)) + # Save original + stmt.orig_prompts.append(new_prompt) + # Finalize with dependencies from enclosing menus and ifs + stmt.prompts.append((new_prompt[0], + _make_and(new_prompt[1], deps))) - # We save the original expressions -- before any menu and if - # conditions have been propagated -- so these can be retrieved - # later. + # Propagate dependencies to defaults + # Propagate 'depends on' dependencies + new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr)) + for val_expr, cond_expr in new_def_exprs] + # Save original stmt.orig_def_exprs.extend(new_def_exprs) - if new_prompt is not None: - stmt.orig_prompts.append(new_prompt) + # Finalize with dependencies from enclosing menus and ifs + stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps)) + for val_expr, cond_expr in new_def_exprs]) + + # Propagate dependencies to selects # Only symbols can select if isinstance(stmt, Symbol): + # Propagate 'depends on' dependencies + new_selects = [(target, _make_and(cond_expr, depends_on_expr)) + for target, cond_expr in new_selects] + # Save original stmt.orig_selects.extend(new_selects) - - # Save dependencies from enclosing menus and if's - stmt.deps_from_containing = deps - - # The set of symbols referenced directly by the symbol/choice plus - # all symbols referenced by enclosing menus and if's. - stmt.all_referenced_syms = \ - stmt.referenced_syms | _get_expr_syms(deps) - - # Propagate dependencies from enclosing menus and if's - - stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps)) - for (val_expr, cond_expr) in new_def_exprs]) - - for (target, cond) in new_selects: - target.rev_dep = _make_or(target.rev_dep, - _make_and(stmt, - _make_and(cond, deps))) - - if new_prompt is not None: - prompt, cond_expr = new_prompt - stmt.prompts.append((prompt, _make_and(cond_expr, deps))) + # Finalize with dependencies from enclosing menus and ifs + for target, cond in new_selects: + target.rev_dep = _make_or(target.rev_dep, + _make_and(stmt, + _make_and(cond, deps))) # # Symbol table manipulation # - def _sym_lookup(self, name, add_sym_if_not_exists = True): + def _sym_lookup(self, name, add_sym_if_not_exists=True): """Fetches the symbol 'name' from the symbol table, optionally adding it if it does not exist (this is usually what we want).""" if name in self.syms:
@@ -1468,75 +1430,59 @@ error, and you should e-mail kconfiglib at gmail.com. first_expr = expr[0] - if first_expr == OR: - res = "n" - - for subexpr in expr[1]: - ev = self._eval_expr_2(subexpr) - - # Return immediately upon discovering a "y" term - if ev == "y": - return "y" - - if ev == "m": - res = "m" - - # 'res' is either "n" or "m" here; we already handled the - # short-circuiting "y" case in the loop. - return res - if first_expr == AND: res = "y" - for subexpr in expr[1]: ev = self._eval_expr_2(subexpr) - # Return immediately upon discovering an "n" term if ev == "n": return "n" - if ev == "m": res = "m" - # 'res' is either "m" or "y" here; we already handled the # short-circuiting "n" case in the loop. return res + if first_expr == OR: + res = "n" + for subexpr in expr[1]: + ev = self._eval_expr_2(subexpr) + # Return immediately upon discovering a "y" term + if ev == "y": + return "y" + if ev == "m": + res = "m" + # 'res' is either "n" or "m" here; we already handled the + # short-circuiting "y" case in the loop. + return res + if first_expr == NOT: ev = self._eval_expr_2(expr[1]) - if ev == "y": return "n" - return "y" if (ev == "n") else "m" if first_expr == EQUAL: - return "y" if (self._get_str_value(expr[1]) == - self._get_str_value(expr[2])) else "n" + return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n" if first_expr == UNEQUAL: - return "y" if (self._get_str_value(expr[1]) != - self._get_str_value(expr[2])) else "n" + return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n" _internal_error("Internal error while evaluating expression: " "unknown operation {0}.".format(first_expr)) - def _get_str_value(self, obj): - if isinstance(obj, str): - return obj - # obj is a Symbol - return obj.get_value() - def _eval_min(self, e1, e2): + """Returns the minimum value of the two expressions. Equates None with + 'y'.""" e1_eval = self._eval_expr(e1) e2_eval = self._eval_expr(e2) - return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval def _eval_max(self, e1, e2): + """Returns the maximum value of the two expressions. Equates None with + 'y'.""" e1_eval = self._eval_expr(e1) e2_eval = self._eval_expr(e2) - return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval #
@@ -1558,8 +1504,6 @@ error, and you should e-mail kconfiglib at gmail.com. for caching/invalidation purposes. The calculated sets might be larger than necessary as we don't do any complicated analysis of the expressions.""" - for sym in self.syms.itervalues(): - sym.dep = set() # Adds 'sym' as a directly dependent symbol to all symbols that appear # in the expression 'e'
@@ -1574,31 +1518,30 @@ error, and you should e-mail kconfiglib at gmail.com. # (these won't be included in 'dep' as that makes the dependency # graph unwieldy, but Symbol._get_dependent() will include them) # - Any symbols in a choice statement that depends on the symbol - for sym in self.syms.itervalues(): - for (_, e) in sym.prompts: + for sym in self.syms_iter(): + for _, e in sym.prompts: add_expr_deps(e, sym) - for (v, e) in sym.def_exprs: + for v, e in sym.def_exprs: add_expr_deps(v, sym) add_expr_deps(e, sym) add_expr_deps(sym.rev_dep, sym) - for (l, u, e) in sym.ranges: + for l, u, e in sym.ranges: add_expr_deps(l, sym) add_expr_deps(u, sym) add_expr_deps(e, sym) if sym.is_choice_symbol_: choice = sym.parent - - for (_, e) in choice.prompts: + for _, e in choice.prompts: add_expr_deps(e, sym) - - for (_, e) in choice.def_exprs: + for _, e in choice.def_exprs: add_expr_deps(e, sym) - def _expr_val_str(self, expr, no_value_str = "(none)", get_val_instead_of_eval = False): + def _expr_val_str(self, expr, no_value_str="(none)", + get_val_instead_of_eval=False): # Since values are valid expressions, _expr_to_str() will get a nice # string representation for those as well.
@@ -1619,17 +1562,17 @@ error, and you should e-mail kconfiglib at gmail.com. empty string for undefined symbols.""" while 1: - sym_ref_re_match = sym_ref_re.search(s) - if sym_ref_re_match is None: + sym_ref_match = _sym_ref_re_search(s) + if sym_ref_match is None: return s - sym_name = sym_ref_re_match.group(0)[1:] + sym_name = sym_ref_match.group(0)[1:] sym = self.syms.get(sym_name) expansion = "" if sym is None else sym.get_value() - s = s[:sym_ref_re_match.start()] + \ + s = s[:sym_ref_match.start()] + \ expansion + \ - s[sym_ref_re_match.end():] + s[sym_ref_match.end():] def _get_sym_or_choice_str(self, sc): """Symbols and choices have many properties in common, so we factor out
@@ -1643,120 +1586,108 @@ error, and you should e-mail kconfiglib at gmail.com. # Common symbol/choice properties # - user_value_str = "(no user value)" if sc.user_val is None else s(sc.user_val) - - visibility_str = s(sc.get_visibility()) + user_val_str = "(no user value)" if sc.user_val is None else \ + s(sc.user_val) # Build prompts string - if sc.prompts == []: + if not sc.prompts: prompts_str = " (no prompts)" else: prompts_str_rows = [] - - for (prompt, cond_expr) in sc.orig_prompts: + for prompt, cond_expr in sc.orig_prompts: if cond_expr is None: prompts_str_rows.append(' "{0}"'.format(prompt)) else: - prompts_str_rows.append(' "{0}" if '.format(prompt) + - self._expr_val_str(cond_expr)) - + prompts_str_rows.append( + ' "{0}" if {1}'.format(prompt, + self._expr_val_str(cond_expr))) prompts_str = "\n".join(prompts_str_rows) # Build locations string - if sc.def_locations == []: + if not sc.def_locations: locations_str = "(no locations)" else: locations_str = " ".join(["{0}:{1}".format(filename, linenr) for (filename, linenr) in sc.def_locations]) - # Build additional-dependencies-from-menus-and-if's string - additional_deps_str = " " + self._expr_val_str(sc.deps_from_containing, - "(no additional dependencies)") + # Build additional-dependencies-from-menus-and-ifs string + additional_deps_str = " " + \ + self._expr_val_str(sc.deps_from_containing, + "(no additional dependencies)") # # Symbol-specific stuff # if isinstance(sc, Symbol): - - # Build value string - value_str = s(sc.get_value()) - # Build ranges string if isinstance(sc, Symbol): - if sc.ranges == []: + if not sc.ranges: ranges_str = " (no ranges)" else: ranges_str_rows = [] - - for (l, u, cond_expr) in sc.ranges: + for l, u, cond_expr in sc.ranges: if cond_expr is None: - ranges_str_rows.append(" [{0}, {1}]".format(s(l), s(u))) + ranges_str_rows.append(" [{0}, {1}]".format(s(l), + s(u))) else: ranges_str_rows.append(" [{0}, {1}] if {2}" - .format(s(l), s(u), self._expr_val_str(cond_expr))) - + .format(s(l), s(u), + self._expr_val_str(cond_expr))) ranges_str = "\n".join(ranges_str_rows) # Build default values string - if sc.def_exprs == []: + if not sc.def_exprs: defaults_str = " (no default values)" else: defaults_str_rows = [] - - for (val_expr, cond_expr) in sc.orig_def_exprs: - row_str = " " + self._expr_val_str(val_expr, "(none)", sc.type == STRING) + for val_expr, cond_expr in sc.orig_def_exprs: + row_str = " " + self._expr_val_str(val_expr, "(none)", + sc.type == STRING) defaults_str_rows.append(row_str) - defaults_str_rows.append(" Condition: " + self._expr_val_str(cond_expr)) - + defaults_str_rows.append(" Condition: " + + self._expr_val_str(cond_expr)) defaults_str = "\n".join(defaults_str_rows) # Build selects string - if sc.orig_selects == []: + if not sc.orig_selects: selects_str = " (no selects)" else: selects_str_rows = [] - - for (target, cond_expr) in sc.orig_selects: + for target, cond_expr in sc.orig_selects: if cond_expr is None: selects_str_rows.append(" {0}".format(target.name)) else: - selects_str_rows.append(" {0} if ".format(target.name) + - self._expr_val_str(cond_expr)) - + selects_str_rows.append( + " {0} if {1}".format(target.name, + self._expr_val_str(cond_expr))) selects_str = "\n".join(selects_str_rows) - # Build reverse dependencies string - if sc.rev_dep == "n": - rev_dep_str = " (no reverse dependencies)" - else: - rev_dep_str = " " + self._expr_val_str(sc.rev_dep) - - res = _sep_lines("Symbol " + (sc.name if sc.name is not None else "(no name)"), - "Type : " + typename[sc.type], - "Value : " + value_str, - "User value : " + user_value_str, - "Visibility : " + visibility_str, - "Is choice item : " + bool_str[sc.is_choice_symbol_], - "Is defined : " + bool_str[sc.is_defined_], - "Is from env. : " + bool_str[sc.is_from_env], - "Is special : " + bool_str[sc.is_special_] + "\n") - - if sc.ranges != []: - res += _sep_lines("Ranges:", - ranges_str + "\n") - - res += _sep_lines("Prompts:", - prompts_str, - "Default values:", - defaults_str, - "Selects:", - selects_str, - "Reverse dependencies:", - rev_dep_str, - "Additional dependencies from enclosing menus and if's:", - additional_deps_str, - "Locations: " + locations_str) + res = _lines("Symbol " + + ("(no name)" if sc.name is None else sc.name), + "Type : " + typename[sc.type], + "Value : " + s(sc.get_value()), + "User value : " + user_val_str, + "Visibility : " + s(sc.get_visibility()), + "Is choice item : " + bool_str[sc.is_choice_symbol_], + "Is defined : " + bool_str[sc.is_defined_], + "Is from env. : " + bool_str[sc.is_from_env], + "Is special : " + bool_str[sc.is_special_] + "\n") + if sc.ranges: + res += _lines("Ranges:", ranges_str + "\n") + res += _lines("Prompts:", + prompts_str, + "Default values:", + defaults_str, + "Selects:", + selects_str, + "Reverse (select-related) dependencies:", + " (no reverse dependencies)" if sc.rev_dep == "n" + else " " + self._expr_val_str(sc.rev_dep), + "Additional dependencies from enclosing menus " + "and ifs:", + additional_deps_str, + "Locations: " + locations_str) return res
@@ -1764,616 +1695,220 @@ error, and you should e-mail kconfiglib at gmail.com. # Choice-specific stuff # - # Build name string (for named choices) - if sc.name is None: - name_str = "(no name)" - else: - name_str = sc.name - # Build selected symbol string sel = sc.get_selection() - if sel is None: - sel_str = "(no selection)" - else: - sel_str = sel.name - - # Build mode string - mode_str = s(sc.get_mode()) + sel_str = "(no selection)" if sel is None else sel.name # Build default values string - if sc.def_exprs == []: + if not sc.def_exprs: defaults_str = " (no default values)" else: defaults_str_rows = [] - - for (sym, cond_expr) in sc.orig_def_exprs: + for sym, cond_expr in sc.orig_def_exprs: if cond_expr is None: defaults_str_rows.append(" {0}".format(sym.name)) else: defaults_str_rows.append(" {0} if ".format(sym.name) + self._expr_val_str(cond_expr)) - defaults_str = "\n".join(defaults_str_rows) # Build contained symbols string names = [sym.name for sym in sc.get_symbols()] + syms_string = " ".join(names) if names else "(empty)" + + return _lines("Choice", + "Name (for named choices): " + + ("(no name)" if sc.name is None else sc.name), + "Type : " + typename[sc.type], + "Selected symbol : " + sel_str, + "User value : " + user_val_str, + "Mode : " + s(sc.get_mode()), + "Visibility : " + s(sc.get_visibility()), + "Optional : " + bool_str[sc.optional], + "Prompts:", + prompts_str, + "Defaults:", + defaults_str, + "Choice symbols:", + " " + syms_string, + "Additional dependencies from enclosing menus and " + "ifs:", + additional_deps_str, + "Locations: " + locations_str) - if names == []: - syms_string = "(empty)" - else: - syms_string = " ".join(names) - - return _sep_lines("Choice", - "Name (for named choices): " + name_str, - "Type : " + typename[sc.type], - "Selected symbol : " + sel_str, - "User value : " + user_value_str, - "Mode : " + mode_str, - "Visibility : " + visibility_str, - "Optional : " + bool_str[sc.optional], - "Prompts:", - prompts_str, - "Defaults:", - defaults_str, - "Choice symbols:", - " " + syms_string, - "Additional dependencies from enclosing menus and if's:", - additional_deps_str, - "Locations: " + locations_str) + def _eq_to_sym(self, eq): + """_expr_depends_on() helper. For (in)equalities of the form sym = y/m + or sym != n, returns sym. For other (in)equalities, returns None.""" + relation, left, right = eq + + def transform_y_m_n(item): + if item is self.y: return "y" + if item is self.m: return "m" + if item is self.n: return "n" + return item + + left = transform_y_m_n(left) + right = transform_y_m_n(right) + + # Make sure the symbol (if any) appears to the left + if not isinstance(left, Symbol): + left, right = right, left + if not isinstance(left, Symbol): + return None + if (relation == EQUAL and (right == "y" or right == "m")) or \ + (relation == UNEQUAL and right == "n"): + return left + return None def _expr_depends_on(self, expr, sym): """Reimplementation of expr_depends_symbol() from mconf.c. Used to - determine if a submenu should be implicitly created, which influences what - items inside choice statements are considered choice items.""" + determine if a submenu should be implicitly created, which influences + what items inside choice statements are considered choice items.""" if expr is None: return False def rec(expr): if isinstance(expr, str): return False - if isinstance(expr, Symbol): return expr is sym e0 = expr[0] - if e0 == EQUAL or e0 == UNEQUAL: return self._eq_to_sym(expr) is sym - if e0 == AND: for and_expr in expr[1]: if rec(and_expr): return True - return False return rec(expr) - def _eq_to_sym(self, eq): - """_expr_depends_on() helper. For (in)equalities of the form sym = y/m - or sym != n, returns sym. For other (in)equalities, returns None.""" - relation, left, right = eq + def _warn(self, msg, filename=None, linenr=None): + """For printing warnings to stderr.""" + if self.print_warnings: + _stderr_msg("warning: " + msg, filename, linenr) - left = self._transform_n_m_y(left) - right = self._transform_n_m_y(right) +class Item(object): - # Make sure the symbol (if any) appears to the left - if not isinstance(left, Symbol): - left, right = right, left + """Base class for symbols and other Kconfig constructs. Subclasses are + Symbol, Choice, Menu, and Comment.""" - if not isinstance(left, Symbol): - return None + def is_symbol(self): + """Returns True if the item is a symbol. Short for + isinstance(item, kconfiglib.Symbol).""" + return isinstance(self, Symbol) - if (relation == EQUAL and (right == "m" or right == "y")) or \ - (relation == UNEQUAL and right == "n"): - return left + def is_choice(self): + """Returns True if the item is a choice. Short for + isinstance(item, kconfiglib.Choice).""" + return isinstance(self, Choice) - return None + def is_menu(self): + """Returns True if the item is a menu. Short for + isinstance(item, kconfiglib.Menu).""" + return isinstance(self, Menu) - def _transform_n_m_y(self, item): - """_eq_to_sym() helper. Translates the symbols n, m, and y to their - string equivalents.""" - if item is self.n: - return "n" - if item is self.m: - return "m" - if item is self.y: - return "y" - return item + def is_comment(self): + """Returns True if the item is a comment. Short for + isinstance(item, kconfiglib.Comment).""" + return isinstance(self, Comment) - def _warn(self, msg, filename = None, linenr = None): - """For printing warnings to stderr.""" - if self.print_warnings: - self._warn_or_undef_assign(msg, WARNING, filename, linenr) - - def _undef_assign(self, msg, filename = None, linenr = None): - """For printing informational messages related to assignments - to undefined variables to stderr.""" - if self.print_undef_assign: - self._warn_or_undef_assign(msg, UNDEF_ASSIGN, filename, linenr) - - def _warn_or_undef_assign(self, msg, msg_type, filename, linenr): - if filename is not None: - sys.stderr.write("{0}:".format(_clean_up_path(filename))) - if linenr is not None: - sys.stderr.write("{0}:".format(linenr)) - - if msg_type == WARNING: - sys.stderr.write("warning: ") - elif msg_type == UNDEF_ASSIGN: - sys.stderr.write("info: ") - else: - _internal_error('Internal error while printing warning: unknown warning type "{0}".' - .format(msg_type)) +class Symbol(Item): - sys.stderr.write(msg + "\n") + """Represents a configuration symbol - e.g. FOO for -def _get_expr_syms(expr): - """Returns the set() of symbols appearing in expr.""" - res = set() - if expr is None: - return res + config FOO + ...""" - def rec(expr): - if isinstance(expr, Symbol): - res.add(expr) - return + # + # Public interface + # - if isinstance(expr, str): - return + def get_value(self): + """Calculate and return the value of the symbol. See also + Symbol.set_user_value().""" - e0 = expr[0] + if self.cached_val is not None: + return self.cached_val - if e0 == OR or e0 == AND: - for term in expr[1]: - rec(term) + # As a quirk of Kconfig, undefined symbols get their name as their + # value. This is why things like "FOO = bar" work for seeing if FOO has + # the value "bar". + if self.type == UNKNOWN: + self.cached_val = self.name + return self.name - elif e0 == NOT: - rec(expr[1]) + new_val = default_value[self.type] + vis = _get_visibility(self) - elif e0 == EQUAL or e0 == UNEQUAL: - _, v1, v2 = expr + # This is easiest to calculate together with the value + self.write_to_conf = False - if isinstance(v1, Symbol): - res.add(v1) + if self.type == BOOL or self.type == TRISTATE: + # The visibility and mode (modules-only or single-selection) of + # choice items will be taken into account in _get_visibility() + if self.is_choice_symbol_: + if vis != "n": + choice = self.parent + mode = choice.get_mode() - if isinstance(v2, Symbol): - res.add(v2) + self.write_to_conf = (mode != "n") - else: - _internal_error("Internal error while fetching symbols from an " - "expression with token stream {0}.".format(expr)) + if mode == "y": + if choice.get_selection() is self: + new_val = "y" + else: + new_val = "n" + elif mode == "m": + if self.user_val == "m" or self.user_val == "y": + new_val = "m" - rec(expr) - return res + else: + # If the symbol is visible and has a user value, use that. + # Otherwise, look at defaults. + use_defaults = True + if vis != "n": + self.write_to_conf = True + if self.user_val is not None: + new_val = self.config._eval_min(self.user_val, vis) + use_defaults = False -# -# Construction of expressions -# + if use_defaults: + for val_expr, cond_expr in self.def_exprs: + cond_eval = self.config._eval_expr(cond_expr) + if cond_eval != "n": + self.write_to_conf = True + new_val = self.config._eval_min(val_expr, + cond_eval) + break -# These functions as well as the _eval_min/max() functions above equate -# None with "y", which is usually what we want, but needs to be kept in -# mind. + # Reverse (select-related) dependencies take precedence + rev_dep_val = self.config._eval_expr(self.rev_dep) + if rev_dep_val != "n": + self.write_to_conf = True + new_val = self.config._eval_max(new_val, rev_dep_val) -def _make_or(e1, e2): - # Perform trivial simplification and avoid None's (which - # correspond to y's) - if e1 is None or e2 is None or \ - e1 == "y" or e2 == "y": - return "y" + # Promote "m" to "y" for booleans + if new_val == "m" and self.type == BOOL: + new_val = "y" - if e1 == "n": - return e2 + elif self.type == STRING: + use_defaults = True - if e2 == "n": - return e1 + if vis != "n": + self.write_to_conf = True + if self.user_val is not None: + new_val = self.user_val + use_defaults = False - # Prefer to merge/update argument list if possible instead of creating - # a new OR node - - if isinstance(e1, tuple) and e1[0] == OR: - if isinstance(e2, tuple) and e2[0] == OR: - return (OR, e1[1] + e2[1]) - return (OR, e1[1] + [e2]) - - if isinstance(e2, tuple) and e2[0] == OR: - return (OR, e2[1] + [e1]) - - return (OR, [e1, e2]) - -# Note: returns None if e1 == e2 == None - -def _make_and(e1, e2): - if e1 == "n" or e2 == "n": - return "n" - - if e1 is None or e1 == "y": - return e2 - - if e2 is None or e2 == "y": - return e1 - - # Prefer to merge/update argument list if possible instead of creating - # a new AND node - - if isinstance(e1, tuple) and e1[0] == AND: - if isinstance(e2, tuple) and e2[0] == AND: - return (AND, e1[1] + e2[1]) - return (AND, e1[1] + [e2]) - - if isinstance(e2, tuple) and e2[0] == AND: - return (AND, e2[1] + [e1]) - - return (AND, [e1, e2]) - -# -# Constants and functions related to types, parsing, evaluation and printing, -# put globally to unclutter the Config class a bit. -# - -# Tokens -(T_OR, T_AND, T_NOT, - T_OPEN_PAREN, T_CLOSE_PAREN, - T_EQUAL, T_UNEQUAL, - T_MAINMENU, T_MENU, T_ENDMENU, - T_SOURCE, T_CHOICE, T_ENDCHOICE, - T_COMMENT, T_CONFIG, T_MENUCONFIG, - T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON, - T_OPTIONAL, T_PROMPT, T_DEFAULT, - T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING, - T_DEF_BOOL, T_DEF_TRISTATE, - T_SELECT, T_RANGE, T_OPTION, T_ENV, - T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(0, 38) - -# Keyword to token map -keywords = { - "mainmenu" : T_MAINMENU, - "menu" : T_MENU, - "endmenu" : T_ENDMENU, - "endif" : T_ENDIF, - "endchoice" : T_ENDCHOICE, - "source" : T_SOURCE, - "choice" : T_CHOICE, - "config" : T_CONFIG, - "comment" : T_COMMENT, - "menuconfig" : T_MENUCONFIG, - "help" : T_HELP, - "if" : T_IF, - "depends" : T_DEPENDS, - "on" : T_ON, - "optional" : T_OPTIONAL, - "prompt" : T_PROMPT, - "default" : T_DEFAULT, - "bool" : T_BOOL, - "boolean" : T_BOOL, - "tristate" : T_TRISTATE, - "int" : T_INT, - "hex" : T_HEX, - "def_bool" : T_DEF_BOOL, - "def_tristate" : T_DEF_TRISTATE, - "string" : T_STRING, - "select" : T_SELECT, - "range" : T_RANGE, - "option" : T_OPTION, - "env" : T_ENV, - "defconfig_list" : T_DEFCONFIG_LIST, - "modules" : T_MODULES, - "visible" : T_VISIBLE } - -# Strings to use for True and False -bool_str = { False : "false", True : "true" } - -# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE -# is included to avoid symbols being registered for named choices. -string_lex = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE, - T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU)) - -# Matches the initial token on a line; see _tokenize(). -initial_token_re = re.compile(r"[^\w]*(\w+)") - -# Matches an identifier/keyword optionally preceded by whitespace -id_keyword_re = re.compile(r"\s*([\w./-]+)") - -# Regular expressions for parsing .config files -set_re = re.compile(r"CONFIG_(\w+)=(.*)") -unset_re = re.compile(r"# CONFIG_(\w+) is not set") - -# Regular expression for finding $-references to symbols in strings -sym_ref_re = re.compile(r"\$[A-Za-z_]+") - -# Integers representing symbol types -UNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(0, 6) - -# Strings to use for types -typename = { - UNKNOWN : "unknown", - BOOL : "bool", - TRISTATE : "tristate", - STRING : "string", - HEX : "hex", - INT : "int" } - -# Token to type mapping -token_to_type = { T_BOOL : BOOL, - T_TRISTATE : TRISTATE, - T_STRING : STRING, - T_INT : INT, - T_HEX : HEX } - -# Default values for symbols of different types (the value the symbol gets if -# it is not assigned a user value and none of its 'default' clauses kick in) -default_value = { BOOL : "n", - TRISTATE : "n", - STRING : "", - INT : "", - HEX : "" } - -# Indicates that no item is selected in a choice statement -NO_SELECTION = 0 - -# Integers representing expression types -OR, AND, NOT, EQUAL, UNEQUAL = range(0, 5) - -# Map from tristate values to integers -tri_to_int = { "n" : 0, "m" : 1, "y" : 2 } - -# Printing-related stuff - -op_to_str = { AND : " && ", - OR : " || ", - EQUAL : " = ", - UNEQUAL : " != " } - -precedence = { OR : 0, AND : 1, NOT : 2 } - -# Types of informational messages -WARNING = 0 -UNDEF_ASSIGN = 1 - -def _intersperse(lst, op): - """_expr_to_str() helper. Gets the string representation of each expression in lst - and produces a list where op has been inserted between the elements.""" - if lst == []: - return "" - - res = [] - - def handle_sub_expr(expr): - no_parens = isinstance(expr, (str, Symbol)) or \ - expr[0] in (EQUAL, UNEQUAL) or \ - precedence[op] <= precedence[expr[0]] - if not no_parens: - res.append("(") - res.extend(_expr_to_str_rec(expr)) - if not no_parens: - res.append(")") - - op_str = op_to_str[op] - - handle_sub_expr(lst[0]) - for expr in lst[1:]: - res.append(op_str) - handle_sub_expr(expr) - - return res - -def _expr_to_str(expr): - s = "".join(_expr_to_str_rec(expr)) - return s - -def _sym_str_string(sym_or_str): - if isinstance(sym_or_str, str): - return '"{0}"'.format(sym_or_str) - return sym_or_str.name - -def _expr_to_str_rec(expr): - if expr is None: - return [""] - - if isinstance(expr, (Symbol, str)): - return [_sym_str_string(expr)] - - e0 = expr[0] - - if e0 == OR or e0 == AND: - return _intersperse(expr[1], expr[0]) - - if e0 == NOT: - need_parens = not isinstance(expr[1], (str, Symbol)) - - res = ["!"] - if need_parens: - res.append("(") - res.extend(_expr_to_str_rec(expr[1])) - if need_parens: - res.append(")") - return res - - if e0 == EQUAL or e0 == UNEQUAL: - return [_sym_str_string(expr[1]), - op_to_str[expr[0]], - _sym_str_string(expr[2])] - -class _Block: - - """Represents a list of items (symbols, menus, choice statements and - comments) appearing at the top-level of a file or witin a menu, choice or - if statement.""" - - def __init__(self): - self.items = [] - - def get_items(self): - return self.items - - def add_item(self, item): - self.items.append(item) - - def _make_conf(self): - # Collect the substrings in a list and later use join() instead of += - # to build the final .config contents. With older Python versions, this - # yields linear instead of quadratic complexity. - strings = [] - for item in self.items: - strings.extend(item._make_conf()) - - return strings - - def add_depend_expr(self, expr): - for item in self.items: - item.add_depend_expr(expr) - -class Item(): - - """Base class for symbols and other Kconfig constructs. Subclasses are - Symbol, Choice, Menu, and Comment.""" - - def is_symbol(self): - """Returns True if the item is a symbol, otherwise False. Short for - isinstance(item, kconfiglib.Symbol).""" - return isinstance(self, Symbol) - - def is_choice(self): - """Returns True if the item is a choice, otherwise False. Short for - isinstance(item, kconfiglib.Choice).""" - return isinstance(self, Choice) - - def is_menu(self): - """Returns True if the item is a menu, otherwise False. Short for - isinstance(item, kconfiglib.Menu).""" - return isinstance(self, Menu) - - def is_comment(self): - """Returns True if the item is a comment, otherwise False. Short for - isinstance(item, kconfiglib.Comment).""" - return isinstance(self, Comment) - -class _HasVisibility(): - - """Base class for elements that have a "visibility" that acts as an upper - limit on the values a user can set for them. Subclasses are Symbol and - Choice (which supply some of the attributes).""" - - def __init__(self): - self.cached_visibility = None - self.prompts = [] - - def _invalidate(self): - self.cached_visibility = None - - def _get_visibility(self): - if self.cached_visibility is None: - vis = "n" - for (prompt, cond_expr) in self.prompts: - vis = self.config._eval_max(vis, cond_expr) - - if isinstance(self, Symbol) and self.is_choice_symbol_: - vis = self.config._eval_min(vis, self.parent._get_visibility()) - - # Promote "m" to "y" if we're dealing with a non-tristate - if vis == "m" and self.type != TRISTATE: - vis = "y" - - self.cached_visibility = vis - - return self.cached_visibility - -class Symbol(Item, _HasVisibility): - - """Represents a configuration symbol - e.g. FOO for - - config FOO - ...""" - - # - # Public interface - # - - def get_value(self): - """Calculate and return the value of the symbol. See also - Symbol.set_user_value().""" - - if self.cached_value is not None: - return self.cached_value - - self.write_to_conf = False - - # As a quirk of Kconfig, undefined symbols get their name as their - # value. This is why things like "FOO = bar" work for seeing if FOO has - # the value "bar". - if self.type == UNKNOWN: - self.cached_value = self.name - return self.name - - new_val = default_value[self.type] - - vis = self._get_visibility() - - if self.type == BOOL or self.type == TRISTATE: - # The visibility and mode (modules-only or single-selection) of - # choice items will be taken into account in self._get_visibility() - - if self.is_choice_symbol_: - if vis != "n": - choice = self.parent - mode = choice.get_mode() - - self.write_to_conf = (mode != "n") - - if mode == "y": - new_val = "y" if (choice.get_selection() is self) else "n" - elif mode == "m": - if self.user_val == "m" or self.user_val == "y": - new_val = "m" - - else: - use_defaults = True - - if vis != "n": - # If the symbol is visible and has a user value, use that. - # Otherwise, look@defaults. - self.write_to_conf = True - - if self.user_val is not None: - new_val = self.config._eval_min(self.user_val, vis) - use_defaults = False - - if use_defaults: - for (val_expr, cond_expr) in self.def_exprs: - cond_eval = self.config._eval_expr(cond_expr) - - if cond_eval != "n": - self.write_to_conf = True - new_val = self.config._eval_min(val_expr, cond_eval) - break - - # Reverse dependencies take precedence - rev_dep_val = self.config._eval_expr(self.rev_dep) - - if rev_dep_val != "n": - self.write_to_conf = True - new_val = self.config._eval_max(new_val, rev_dep_val) - - # Promote "m" to "y" for booleans - if new_val == "m" and self.type == BOOL: - new_val = "y" - - elif self.type == STRING: - use_defaults = True - - if vis != "n": - self.write_to_conf = True - if self.user_val is not None: - new_val = self.user_val - use_defaults = False - - if use_defaults: - for (val_expr, cond_expr) in self.def_exprs: - if self.config._eval_expr(cond_expr) != "n": - self.write_to_conf = True - new_val = self.config._get_str_value(val_expr) - break + if use_defaults: + for val_expr, cond_expr in self.def_exprs: + if self.config._eval_expr(cond_expr) != "n": + self.write_to_conf = True + new_val = _str_val(val_expr) + break elif self.type == HEX or self.type == INT: has_active_range = False
@@ -2387,9 +1922,8 @@ class Symbol(Item, _HasVisibility): if self.config._eval_expr(cond_expr) != "n": has_active_range = True - low_str = self.config._get_str_value(l) - high_str = self.config._get_str_value(h) - + low_str = _str_val(l) + high_str = _str_val(h) low = int(low_str, base) if \ _is_base_n(low_str, base) else 0 high = int(high_str, base) if \
@@ -2413,7 +1947,7 @@ class Symbol(Item, _HasVisibility): new_val = self.user_val if use_defaults: - for (val_expr, cond_expr) in self.def_exprs: + for val_expr, cond_expr in self.def_exprs: if self.config._eval_expr(cond_expr) != "n": self.write_to_conf = True
@@ -2422,7 +1956,7 @@ class Symbol(Item, _HasVisibility): # to the range, and the output has "0x" as appropriate # for the type. - new_val = self.config._get_str_value(val_expr) + new_val = _str_val(val_expr) if _is_base_n(new_val, base): new_val_num = int(new_val, base)
@@ -2443,11 +1977,10 @@ class Symbol(Item, _HasVisibility): # If no user value or default kicks in but the hex/int has # an active range, then the low end of the range is used, # provided it's > 0, with "0x" prepended as appropriate. - if has_active_range and low > 0: new_val = (hex(low) if self.type == HEX else str(low)) - self.cached_value = new_val + self.cached_val = new_val return new_val def set_user_value(self, v):
@@ -2493,8 +2026,8 @@ class Symbol(Item, _HasVisibility): def get_user_value(self): """Returns the value assigned to the symbol in a .config or via - Symbol.set_user_value() (provided the value was valid for the type of the - symbol). Returns None in case of no user value.""" + Symbol.set_user_value() (provided the value was valid for the type of + the symbol). Returns None in case of no user value.""" return self.user_val def get_name(self):
@@ -2516,9 +2049,10 @@ class Symbol(Item, _HasVisibility): cannot be modified (see is_modifiable()), returns None. Otherwise, returns the highest value the symbol can be set to with - Symbol.set_user_value() (that will not be truncated): one of "m" or "y", - arranged from lowest to highest. This corresponds to the highest value - the symbol could be given in e.g. the 'make menuconfig' interface. + Symbol.set_user_value() (that will not be truncated): one of "m" or + "y", arranged from lowest to highest. This corresponds to the highest + value the symbol could be given in e.g. the 'make menuconfig' + interface. See also the tri_less*() and tri_greater*() functions, which could come in handy."""
@@ -2528,7 +2062,7 @@ class Symbol(Item, _HasVisibility): # A bool selected to "m" gets promoted to "y" if self.type == BOOL and rev_dep == "m": rev_dep = "y" - vis = self._get_visibility() + vis = _get_visibility(self) if (tri_to_int[vis] - tri_to_int[rev_dep]) > 0: return vis return None
@@ -2538,9 +2072,10 @@ class Symbol(Item, _HasVisibility): cannot be modified (see is_modifiable()), returns None. Otherwise, returns the lowest value the symbol can be set to with - Symbol.set_user_value() (that will not be truncated): one of "n" or "m", - arranged from lowest to highest. This corresponds to the lowest value - the symbol could be given in e.g. the 'make menuconfig' interface. + Symbol.set_user_value() (that will not be truncated): one of "n" or + "m", arranged from lowest to highest. This corresponds to the lowest + value the symbol could be given in e.g. the 'make menuconfig' + interface. See also the tri_less*() and tri_greater*() functions, which could come in handy."""
@@ -2550,7 +2085,7 @@ class Symbol(Item, _HasVisibility): # A bool selected to "m" gets promoted to "y" if self.type == BOOL and rev_dep == "m": rev_dep = "y" - if (tri_to_int[self._get_visibility()] - tri_to_int[rev_dep]) > 0: + if (tri_to_int[_get_visibility(self)] - tri_to_int[rev_dep]) > 0: return rev_dep return None
@@ -2574,7 +2109,7 @@ class Symbol(Item, _HasVisibility): if self.type == BOOL and rev_dep == "m": rev_dep = "y" res = ["n", "m", "y"][tri_to_int[rev_dep] : - tri_to_int[self._get_visibility()] + 1] + tri_to_int[_get_visibility(self)] + 1] return res if len(res) > 1 else [] def get_type(self):
@@ -2628,7 +2163,7 @@ class Symbol(Item, _HasVisibility): You should probably look at get_lower/upper_bound(), get_assignable_values() and is_modifiable() before using this.""" - return self._get_visibility() + return _get_visibility(self) def get_parent(self): """Returns the menu or choice statement that contains the symbol, or
@@ -2637,7 +2172,7 @@ class Symbol(Item, _HasVisibility): representation.""" return self.parent - def get_referenced_symbols(self, refs_from_enclosing = False): + def get_referenced_symbols(self, refs_from_enclosing=False): """Returns the set() of all symbols referenced by this symbol. For example, the symbol defined by
@@ -2651,9 +2186,10 @@ class Symbol(Item, _HasVisibility): references the symbols A through G. refs_from_enclosing (default: False) -- If True, the symbols - referenced by enclosing menus and if's will be + referenced by enclosing menus and ifs will be included in the result.""" - return self.all_referenced_syms if refs_from_enclosing else self.referenced_syms + return self.all_referenced_syms if refs_from_enclosing else \ + self.referenced_syms def get_selected_symbols(self): """Returns the set() of all symbols X for which this symbol has a
@@ -2713,7 +2249,7 @@ class Symbol(Item, _HasVisibility): def is_modifiable(self): """Returns True if the value of the symbol could be modified by calling - Symbol.set_user_value() and False otherwise. + Symbol.set_user_value(). For bools and tristates, this corresponds to the symbol being visible in the 'make menuconfig' interface and not already being pinned to a
@@ -2728,43 +2264,45 @@ class Symbol(Item, _HasVisibility): # A bool selected to "m" gets promoted to "y" if self.type == BOOL and rev_dep == "m": rev_dep = "y" - return (tri_to_int[self._get_visibility()] - + return (tri_to_int[_get_visibility(self)] - tri_to_int[rev_dep]) > 0 - return self._get_visibility() != "n" + return _get_visibility(self) != "n" def is_defined(self): """Returns False if the symbol is referred to in the Kconfig but never - actually defined, otherwise True.""" + actually defined.""" return self.is_defined_ def is_special(self): """Returns True if the symbol is one of the special symbols n, m, y, or - UNAME_RELEASE, or gets its value from the environment. Otherwise, - returns False.""" + UNAME_RELEASE, or gets its value from the environment.""" return self.is_special_ def is_from_environment(self): - """Returns True if the symbol gets its value from the environment. - Otherwise, returns False.""" + """Returns True if the symbol gets its value from the environment.""" return self.is_from_env def has_ranges(self): """Returns True if the symbol is of type INT or HEX and has ranges that - limits what values it can take on, otherwise False.""" - return self.ranges != [] + limit what values it can take on.""" + return bool(self.ranges) def is_choice_symbol(self): """Returns True if the symbol is in a choice statement and is an actual - choice symbol (see Choice.get_symbols()); otherwise, returns - False.""" + choice symbol (see Choice.get_symbols()).""" return self.is_choice_symbol_ def is_choice_selection(self): """Returns True if the symbol is contained in a choice statement and is - the selected item, otherwise False. Equivalent to 'sym.is_choice_symbol() - and sym.get_parent().get_selection() is sym'.""" + the selected item. Equivalent to + + sym.is_choice_symbol() and sym.get_parent().get_selection() is sym""" return self.is_choice_symbol_ and self.parent.get_selection() is self + def is_allnoconfig_y(self):
Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.1.4
Part of the diff is not shown here. View the raw message.
PATCH 1/1 kconfiglib: update to the latest version
Quoted 21 lines · Ulf Magnusson
Corresponds to 2f319b8 in https://github.com/ulfalizer/Kconfiglib.
Fixes:
- Unset user values when loading a zero-byte .config. (5e54e2c)
- Ignore indented .config assignments. (f8a7510)
- Do not require $srctree to be set for non-kernel projects. (d56e9c1)Also adds Python 3 support and has a lot of internal cleanup and optimization. Makes
tools/genboardscfg.pyrun slightly faster.Signed-off-by Ulf Magnusson <[email protected]>--- tools/buildman/kconfiglib.py | 2898 +++++++++++++++++++----------------------- 1 file changed, 1329 insertions(+), 1569 deletions(-)tools/buildman/kconfiglib.py +0
-0
tools/buildman/kconfiglib.py Kconfig-based configuration systems. Features include the following: · line 36
Quoted 2 lines
tools/buildman/kconfiglib.py +0
-0@@ -36,57 +36,61 @@ Kconfig-based configuration systems. Features include the following:
Adding __slots__ (https://docs.python.org/2/reference/datamodel.html#slots) to the Kconfiglib classes reduces the runtime of genboardscfg.py further from 1.6 to 1.4 seconds on my system, also saving a lot of memory. It'd remove some flexibility though (and breaks the kconfig-diff.py example), so it should probably
be optional if I add it. Maybe genboardscfg.py is fast enough already.
/Ulf
Quoted 4 lines · Ulf Magnusson
Corresponds to 2f319b8 in https://github.com/ulfalizer/Kconfiglib.
Fixes:
Quoted 8 lines
- Unset user values when loading a zero-byte .config. (5e54e2c)
- Ignore indented .config assignments. (f8a7510)
- Do not require $srctree to be set for non-kernel projects. (d56e9c1)Also adds Python 3 support and has a lot of internal cleanup and optimization. Makes
tools/genboardscfg.pyrun slightly faster.Signed-off-by Ulf Magnusson <[email protected]>
Thanks for posting this. It however doesn't apply cleanly on top of current master, can you please re-update? Thanks!
Signature · 8 lines
Tom -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: Digital signature URL: <http://lists.denx.de/pipermail/u-boot/attachments/20150813/75d550f8/attachment.sig>
Quoted 13 lines · Tom Rini
Quoted 11 lines · Ulf Magnusson
Corresponds to 2f319b8 in https://github.com/ulfalizer/Kconfiglib.
Fixes:
- Unset user values when loading a zero-byte .config. (5e54e2c)
- Ignore indented .config assignments. (f8a7510)
- Do not require $srctree to be set for non-kernel projects. (d56e9c1)Also adds Python 3 support and has a lot of internal cleanup and optimization. Makes
tools/genboardscfg.pyrun slightly faster.
Quoted 7 lines
Quoted 1 line
Signed-off-by Ulf Magnusson <[email protected]>Thanks for posting this. It however doesn't apply cleanly on top of current master, can you please re-update? Thanks!
--
Tom
Seems odd since kconfiglib.py hasn't changed in the U-Boot tree, but maybe I messed up somehow.
Maybe it'd be easier to just overwrite tools/buildman/kconfiglib.py with the most recent version
(https://github.com/ulfalizer/Kconfiglib/blob/ba71a0eac153d05996af529a647a1ab97163802a/kconfiglib.py)
and adding the following commit message. I've verified that it works. The patch amounts to "everything changed" and is huge (~4300 changes). :)
Please tell me if you'd rather have a patch still.
kconfiglib: update to the latest version
Corresponds to ba71a0eac153d0 (Fix _parse_block() 'parent' documentation re. ifs.) from upstream, just adding a short header at the beginning.
Has performance improvements, code cleanup, Python 3 support, and various small fixes, including the following:
- Unset user values when loading a zero-byte .config. (5e54e2c)
- Ignore indented .config assignments. (f8a7510)
- Do not require $srctree to be set for non-kernel projects. (d56e9c1)
- Report correct locations in the presence of continuation lines.
(0cebc87)
Cheers,
Ulf
Quoted 22 lines · Ulf Magnusson
Quoted 10 lines · Tom Rini
Quoted 8 lines · Ulf Magnusson
Corresponds to 2f319b8 in https://github.com/ulfalizer/Kconfiglib.
Fixes:
- Unset user values when loading a zero-byte .config. (5e54e2c)
- Ignore indented .config assignments. (f8a7510)
- Do not require $srctree to be set for non-kernel projects. (d56e9c1)Quoted 7 lines
Quoted 4 lines
Also adds Python 3 support and has a lot of internal cleanup and optimization. Makes
tools/genboardscfg.pyrun slightly faster.Signed-off-by Ulf Magnusson <[email protected]>Thanks for posting this. It however doesn't apply cleanly on top of current master, can you please re-update? Thanks!
Seems odd since
kconfiglib.pyhasn't changed in the U-Boot tree, but maybe I messed up somehow.
Quoted 7 lines
Maybe it'd be easier to just overwrite
tools/buildman/kconfiglib.pywith the most recent version
(https://github.com/ulfalizer/Kconfiglib/blob/ba71a0eac153d05996af529a647a1ab97163802a/kconfiglib.py)
and adding the following commit message. I've verified that it works. The patch amounts to "everything changed" and is huge (~4300 changes). :)Please tell me if you'd rather have a patch still.
Yeah, it'll be big but it helps with tracking. Make sure you don't wipe out the SPDX tag when you update :) Thanks again!
Signature · 8 lines
Tom -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: Digital signature URL: <http://lists.denx.de/pipermail/u-boot/attachments/20150813/cabe4d46/attachment.sig>