diff --git a/gclient.py b/gclient.py index f2d90f692..03177c3f0 100755 --- a/gclient.py +++ b/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 diff --git a/gclient_eval.py b/gclient_eval.py index 2ee93f124..602190b08 100644 --- a/gclient_eval.py +++ b/gclient_eval.py @@ -277,7 +277,7 @@ def _gclient_eval(node_or_string, filename='', vars_dict=None): return _convert(node_or_string) -def Exec(content, filename='', vars_override=None): +def Exec(content, filename='', 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='', 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='', vars_override=None): return _GCLIENT_SCHEMA.validate(local_scope) -def ExecLegacy(content, filename='', vars_override=None): +def ExecLegacy(content, filename='', 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='', 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='', 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. '', ''. 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: diff --git a/tests/gclient_eval_unittest.py b/tests/gclient_eval_unittest.py index 6c999d05a..4d178253a 100755 --- a/tests/gclient_eval_unittest.py +++ b/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, '', 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, '', 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, '', 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: