diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/tools/cr/cr/config.py | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/tools/cr/cr/config.py')
-rw-r--r-- | chromium/tools/cr/cr/config.py | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/chromium/tools/cr/cr/config.py b/chromium/tools/cr/cr/config.py new file mode 100644 index 00000000000..a19b837a446 --- /dev/null +++ b/chromium/tools/cr/cr/config.py @@ -0,0 +1,240 @@ +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Configuration variable management for the cr tool. + +This holds the classes that support the hierarchical variable management used +in the cr tool to provide all the command configuration controls. +""" + +import string + +import cr.visitor + +_PARSE_CONSTANT_VALUES = [None, True, False] +_PARSE_CONSTANTS = dict((str(value), value) for value in _PARSE_CONSTANT_VALUES) + +# GLOBALS is the singleton used to tie static global configuration objects +# together. +GLOBALS = [] + + +class _MissingToErrorFormatter(string.Formatter): + """A string formatter used in value resolve. + + The main extra it adds is a new conversion specifier 'e' that throws a + KeyError if it could not find the value. + This allows a string value to use {A_KEY!e} to indicate that it is a + formatting error if A_KEY is not present. + """ + + def convert_field(self, value, conversion): + if conversion == 'e': + result = str(value) + if not result: + raise KeyError('unknown') + return result + return super(_MissingToErrorFormatter, self).convert_field( + value, conversion) + + +class _Tracer(object): + """Traces variable lookups. + + This adds a hook to a config object, and uses it to track all variable + lookups that happen and add them to a trail. When done, it removes the hook + again. This is used to provide debugging information about what variables are + used in an operation. + """ + + def __init__(self, config): + self.config = config + self.trail = [] + + def __enter__(self): + self.config.fixup_hooks.append(self._Trace) + return self + + def __exit__(self, *_): + self.config.fixup_hooks.remove(self._Trace) + self.config.trail = self.trail + return False + + def _Trace(self, _, key, value): + self.trail.append((key, value)) + return value + + +class Config(cr.visitor.Node): + """The main variable holding class. + + This holds a set of unresolved key value pairs, and the set of child Config + objects that should be referenced when looking up a key. + Key search is one in a pre-order traversal, and new children are prepended. + This means parents override children, and the most recently added child + overrides the rest. + + Values can be simple python types, callable dynamic values, or strings. + If the value is a string, it is assumed to be a standard python format string + where the root config object is used to resolve the keys. This allows values + to refer to variables that are overriden in another part of the hierarchy. + """ + + @classmethod + def From(cls, *args, **kwargs): + """Builds an unnamed config object from a set of key,value args.""" + return Config('??').Apply(args, kwargs) + + @classmethod + def If(cls, condition, true_value, false_value=''): + """Returns a config value that selects a value based on the condition. + + Args: + condition: The variable name to select a value on. + true_value: The value to use if the variable is True. + false_value: The value to use if the resolved variable is False. + Returns: + A dynamic value. + """ + def Resolve(context): + test = context.Get(condition) + if test: + value = true_value + else: + value = false_value + return context.Substitute(value) + return Resolve + + @classmethod + def Optional(cls, value, alternate=''): + """Returns a dynamic value that defaults to an alternate. + + Args: + value: The main value to resolve. + alternate: The value to use if the main value does not resolve. + Returns: + value if it resolves, alternate otherwise. + """ + def Resolve(context): + try: + return context.Substitute(value) + except KeyError: + return context.Substitute(alternate) + return Resolve + + def __init__(self, name='--', literal=False, export=None, enabled=True): + super(Config, self).__init__(name=name, enabled=enabled, export=export) + self._literal = literal + self._formatter = _MissingToErrorFormatter() + self.fixup_hooks = [] + self.trail = [] + + @property + def literal(self): + return self._literal + + def Substitute(self, value): + return self._formatter.vformat(str(value), (), self) + + def Resolve(self, visitor, key, value): + """Resolves a value to it's final form. + + Raw values can be callable, simple values, or contain format strings. + Args: + visitor: The vistior asking to resolve a value. + key: The key being visited. + value: The unresolved value associated with the key. + Returns: + the fully resolved value. + """ + error = None + if callable(value): + value = value(self) + # Using existence of value.swapcase as a proxy for is a string + elif hasattr(value, 'swapcase'): + if not visitor.current_node.literal: + try: + value = self.Substitute(value) + except KeyError as e: + error = e + return self.Fixup(key, value), error + + def Fixup(self, key, value): + for hook in self.fixup_hooks: + value = hook(self, key, value) + return value + + @staticmethod + def ParseValue(value): + """Converts a string to a value. + + Takes a string from something like an environment variable, and tries to + build an internal typed value. Recognizes Null, booleans, and numbers as + special. + Args: + value: The the string value to interpret. + Returns: + the parsed form of the value. + """ + if value in _PARSE_CONSTANTS: + return _PARSE_CONSTANTS[value] + try: + return int(value) + except ValueError: + pass + try: + return float(value) + except ValueError: + pass + return value + + def _Set(self, key, value): + # early out if the value did not change, so we don't call change callbacks + if value == self._values.get(key, None): + return + self._values[key] = value + self.NotifyChanged() + return self + + def ApplyMap(self, arg): + for key, value in arg.items(): + self._Set(key, value) + return self + + def Apply(self, args, kwargs): + """Bulk set variables from arguments. + + Intended for internal use by the Set and From methods. + Args: + args: must be either a dict or something that can build a dict. + kwargs: must be a dict. + Returns: + self for easy chaining. + """ + if len(args) == 1: + arg = args[0] + if isinstance(arg, dict): + self.ApplyMap(arg) + else: + self.ApplyMap(dict(arg)) + elif len(args) > 1: + self.ApplyMap(dict(args)) + self.ApplyMap(kwargs) + return self + + def Set(self, *args, **kwargs): + return self.Apply(args, kwargs) + + def Trace(self): + return _Tracer(self) + + def __getitem__(self, key): + return self.Get(key) + + def __setitem__(self, key, value): + self._Set(key, value) + + def __contains__(self, key): + return self.Find(key) is not None + |