Эх сурвалжийг харах

gclient: Make built-in vars available for expansion.

Make it possible to refer to built-in variables without having to declare
then in DEPS files.

Bug: None
Change-Id: I5403963052463befc074f29750de56cce13927ce
Reviewed-on: https://chromium-review.googlesource.com/c/1312234
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
Edward Lemur 6 жил өмнө
parent
commit
8f8a50d00a

+ 12 - 8
gclient.py

@@ -681,7 +681,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
       try:
         local_scope = gclient_eval.Parse(
             deps_content, self._get_option('validate_syntax', False),
-            filepath, self.get_vars())
+            filepath, self.get_vars(), self.get_builtin_vars())
       except SyntaxError as e:
         gclient_utils.SyntaxErrorToError(filepath, e)
 
@@ -1192,11 +1192,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
       d = d.parent
     return tuple(out)
 
-  def get_vars(self):
-    """Returns a dictionary of effective variable values
-    (DEPS file contents with applied custom_vars overrides)."""
-    # Provide some built-in variables.
-    result = {
+  def get_builtin_vars(self):
+    return {
         'checkout_android': 'android' in self.target_os,
         'checkout_chromeos': 'chromeos' in self.target_os,
         'checkout_fuchsia': 'fuchsia' in self.target_os,
@@ -1216,15 +1213,22 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
         'checkout_x64': 'x64' in self.target_cpu,
         'host_cpu': detect_host_arch.HostArch(),
     }
-    # Variable precedence:
-    # - built-in
+
+  def get_vars(self):
+    """Returns a dictionary of effective variable values
+    (DEPS file contents with applied custom_vars overrides)."""
+    # Variable precedence (last has highest):
     # - DEPS vars
     # - parents, from first to last
+    # - built-in
     # - custom_vars overrides
+    result = {}
     result.update(self._vars)
     if self.parent:
       parent_vars = self.parent.get_vars()
       result.update(parent_vars)
+    # Provide some built-in variables.
+    result.update(self.get_builtin_vars())
     result.update(self.custom_vars or {})
     return result
 

+ 24 - 14
gclient_eval.py

@@ -277,7 +277,7 @@ def _gclient_eval(node_or_string, filename='<unknown>', vars_dict=None):
   return _convert(node_or_string)
 
 
-def Exec(content, filename='<unknown>', vars_override=None):
+def Exec(content, filename='<unknown>', vars_override=None, builtin_vars=None):
   """Safely execs a set of assignments."""
   def _validate_statement(node, local_scope):
     if not isinstance(node, ast.Assign):
@@ -334,11 +334,15 @@ def Exec(content, filename='<unknown>', vars_override=None):
     # Update the parsed vars with the overrides, but only if they are already
     # present (overrides do not introduce new variables).
     vars_dict.update(value)
-    if vars_override:
-      vars_dict.update({
-        k: v
-        for k, v in vars_override.iteritems()
-        if k in vars_dict})
+
+  if builtin_vars:
+    vars_dict.update(builtin_vars)
+
+  if vars_override:
+    vars_dict.update({
+      k: v
+      for k, v in vars_override.iteritems()
+      if k in vars_dict})
 
   for name, node in statements.iteritems():
     value = _gclient_eval(node, filename, vars_dict)
@@ -347,7 +351,8 @@ def Exec(content, filename='<unknown>', vars_override=None):
   return _GCLIENT_SCHEMA.validate(local_scope)
 
 
-def ExecLegacy(content, filename='<unknown>', vars_override=None):
+def ExecLegacy(content, filename='<unknown>', vars_override=None,
+               builtin_vars=None):
   """Executes a DEPS file |content| using exec."""
   local_scope = {}
   global_scope = {'Var': lambda var_name: '{%s}' % var_name}
@@ -358,11 +363,10 @@ def ExecLegacy(content, filename='<unknown>', vars_override=None):
   # as "exec a in b, c" (See https://bugs.python.org/issue21591).
   eval(compile(content, filename, 'exec'), global_scope, local_scope)
 
-  if 'vars' not in local_scope:
-    return local_scope
-
   vars_dict = {}
-  vars_dict.update(local_scope['vars'])
+  vars_dict.update(local_scope.get('vars', {}))
+  if builtin_vars:
+    vars_dict.update(builtin_vars)
   if vars_override:
     vars_dict.update({
         k: v
@@ -370,6 +374,9 @@ def ExecLegacy(content, filename='<unknown>', vars_override=None):
         if k in vars_dict
     })
 
+  if not vars_dict:
+    return local_scope
+
   def _DeepFormat(node):
     if isinstance(node, basestring):
       return node.format(**vars_dict)
@@ -453,7 +460,8 @@ def UpdateCondition(info_dict, op, new_condition):
     del info_dict['condition']
 
 
-def Parse(content, validate_syntax, filename, vars_override=None):
+def Parse(content, validate_syntax, filename, vars_override=None,
+          builtin_vars=None):
   """Parses DEPS strings.
 
   Executes the Python-like string stored in content, resulting in a Python
@@ -468,15 +476,17 @@ def Parse(content, validate_syntax, filename, vars_override=None):
       of the content, e.g. '<string>', '<unknown>'.
     vars_override: dict, optional. A dictionary with overrides for the variables
       defined by the DEPS file.
+    builtin_vars: dict, optional. A dictionary with variables that are provided
+      by default.
 
   Returns:
     A Python dict with the parsed contents of the DEPS file, as specified by the
     schema above.
   """
   if validate_syntax:
-    result = Exec(content, filename, vars_override)
+    result = Exec(content, filename, vars_override, builtin_vars)
   else:
-    result = ExecLegacy(content, filename, vars_override)
+    result = ExecLegacy(content, filename, vars_override, builtin_vars)
 
   vars_dict = result.get('vars', {})
   if 'deps' in result:

+ 49 - 0
tests/gclient_eval_unittest.py

@@ -701,6 +701,55 @@ class ParseTest(unittest.TestCase):
                                'condition': 'baz'}},
       }, local_scope)
 
+  def test_has_builtin_vars(self):
+    builtin_vars = {'builtin_var': 'foo'}
+    deps_file = '\n'.join([
+      'deps = {',
+      '  "a_dep": "a{builtin_var}b",',
+      '}',
+    ])
+    for validate_syntax in False, True:
+      local_scope = gclient_eval.Parse(
+          deps_file, validate_syntax, '<unknown>', None, builtin_vars)
+      self.assertEqual({
+        'deps': {'a_dep': {'url': 'afoob',
+                           'dep_type': 'git'}},
+      }, local_scope)
+
+  def test_declaring_builtin_var_has_no_effect(self):
+    builtin_vars = {'builtin_var': 'foo'}
+    deps_file = '\n'.join([
+        'vars = {',
+        '  "builtin_var": "bar",',
+        '}',
+        'deps = {',
+        '  "a_dep": "a{builtin_var}b",',
+        '}',
+    ])
+    for validate_syntax in False, True:
+      local_scope = gclient_eval.Parse(
+          deps_file, validate_syntax, '<unknown>', None, builtin_vars)
+      self.assertEqual({
+        'vars': {'builtin_var': 'bar'},
+        'deps': {'a_dep': {'url': 'afoob',
+                           'dep_type': 'git'}},
+      }, local_scope)
+
+  def test_override_builtin_var(self):
+    builtin_vars = {'builtin_var': 'foo'}
+    vars_override = {'builtin_var': 'override'}
+    deps_file = '\n'.join([
+      'deps = {',
+      '  "a_dep": "a{builtin_var}b",',
+      '}',
+    ])
+    for validate_syntax in False, True:
+      local_scope = gclient_eval.Parse(
+          deps_file, validate_syntax, '<unknown>', vars_override, builtin_vars)
+      self.assertEqual({
+        'deps': {'a_dep': {'url': 'aoverrideb',
+                           'dep_type': 'git'}},
+      }, local_scope, str(local_scope))
 
   def test_expands_vars(self):
     for validate_syntax in True, False: