summaryrefslogtreecommitdiffstats
path: root/chromium/tools/cr/cr/config.py
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/tools/cr/cr/config.py
parente90d7c4b152c56919d963987e2503f9909a666d2 (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.py240
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
+