gclient: add support for native boolean variables

Bug: 570091
Change-Id: I195f5f798d9869f385437db4e0bc5f4c5d4853ae
Reviewed-on: https://chromium-review.googlesource.com/687496
Commit-Queue: Paweł Hajdan Jr. <phajdan.jr@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
diff --git a/gclient_eval.py b/gclient_eval.py
index c1dee2a..110cdac 100644
--- a/gclient_eval.py
+++ b/gclient_eval.py
@@ -120,7 +120,9 @@
     schema.Optional('use_relative_paths'): bool,
 
     # Variables that can be referenced using Var() - see 'deps'.
-    schema.Optional('vars'): {schema.Optional(basestring): basestring},
+    schema.Optional('vars'): {
+        schema.Optional(basestring): schema.Or(basestring, bool),
+    },
 })
 
 
@@ -237,27 +239,58 @@
       elif node.id in _allowed_names:
         return _allowed_names[node.id]
       elif node.id in variables:
+        value = variables[node.id]
+
+        # Allow using "native" types, without wrapping everything in strings.
+        # Note that schema constraints still apply to variables.
+        if not isinstance(value, basestring):
+          return value
+
+        # Recursively evaluate the variable reference.
         return EvaluateCondition(
             variables[node.id],
             variables,
             referenced_variables.union([node.id]))
       else:
-        raise ValueError(
-            'invalid name %r (inside %r)' % (node.id, condition))
+        # Implicitly convert unrecognized names to strings.
+        # If we want to change this, we'll need to explicitly distinguish
+        # between arguments for GN to be passed verbatim, and ones to
+        # be evaluated.
+        return node.id
     elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or):
       if len(node.values) != 2:
         raise ValueError(
             'invalid "or": exactly 2 operands required (inside %r)' % (
                 condition))
-      return _convert(node.values[0]) or _convert(node.values[1])
+      left = _convert(node.values[0])
+      right = _convert(node.values[1])
+      if not isinstance(left, bool):
+        raise ValueError(
+            'invalid "or" operand %r (inside %r)' % (left, condition))
+      if not isinstance(right, bool):
+        raise ValueError(
+            'invalid "or" operand %r (inside %r)' % (right, condition))
+      return left or right
     elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And):
       if len(node.values) != 2:
         raise ValueError(
             'invalid "and": exactly 2 operands required (inside %r)' % (
                 condition))
-      return _convert(node.values[0]) and _convert(node.values[1])
+      left = _convert(node.values[0])
+      right = _convert(node.values[1])
+      if not isinstance(left, bool):
+        raise ValueError(
+            'invalid "and" operand %r (inside %r)' % (left, condition))
+      if not isinstance(right, bool):
+        raise ValueError(
+            'invalid "and" operand %r (inside %r)' % (right, condition))
+      return left and right
     elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
-      return not _convert(node.operand)
+      value = _convert(node.operand)
+      if not isinstance(value, bool):
+        raise ValueError(
+            'invalid "not" operand %r (inside %r)' % (value, condition))
+      return not value
     elif isinstance(node, ast.Compare):
       if len(node.ops) != 1:
         raise ValueError(