gclient: Merge hook_os with hooks and deps_os with os.
This is done in gclient_eval, so we can remove all code
that deals with deps_os and hooks_os from gclient.
Bug: 839925
Change-Id: I491819207a712d62008ff010e313add87d22c937
Reviewed-on: https://chromium-review.googlesource.com/1058375
Commit-Queue: Edward Lesmes <[email protected]>
Reviewed-by: Aaron Gable <[email protected]>
diff --git a/gclient_eval.py b/gclient_eval.py
index 4d0b507..5e2fd33 100644
--- a/gclient_eval.py
+++ b/gclient_eval.py
@@ -5,8 +5,11 @@
import ast
import cStringIO
import collections
+import logging
import tokenize
+import gclient_utils
+
from third_party import schema
@@ -345,31 +348,9 @@
return _GCLIENT_SCHEMA.validate(local_scope)
-def Parse(content, expand_vars, validate_syntax, filename, vars_override=None):
- """Parses DEPS strings.
-
- Executes the Python-like string stored in content, resulting in a Python
- dictionary specifyied by the schema above. Supports syntax validation and
- variable expansion.
-
- Args:
- content: str. DEPS file stored as a string.
- expand_vars: bool. Whether variables should be expanded to their values.
- validate_syntax: bool. Whether syntax should be validated using the schema
- defined above.
- filename: str. The name of the DEPS file, or a string describing the source
- of the content, e.g. '<string>', '<unknown>'.
- vars_override: dict, optional. A dictionary with overrides for the variables
- defined by the DEPS file.
-
- Returns:
- A Python dict with the parsed contents of the DEPS file, as specified by the
- schema above.
- """
- # TODO(ehmaldonado): Make validate_syntax = True the only case
- if validate_syntax:
- return Exec(content, expand_vars, filename, vars_override)
-
+def ExecLegacy(content, expand_vars=True, filename='<unknown>',
+ vars_override=None):
+ """Executes a DEPS file |content| using exec."""
local_scope = {}
global_scope = {'Var': lambda var_name: '{%s}' % var_name}
@@ -409,6 +390,119 @@
return _DeepFormat(local_scope)
+def _StandardizeDeps(deps_dict, vars_dict):
+ """"Standardizes the deps_dict.
+
+ For each dependency:
+ - Expands the variable in the dependency name.
+ - Ensures the dependency is a dictionary.
+ - Set's the 'dep_type' to be 'git' by default.
+ """
+ new_deps_dict = {}
+ for dep_name, dep_info in deps_dict.items():
+ dep_name = dep_name.format(**vars_dict)
+ if not isinstance(dep_info, collections.Mapping):
+ dep_info = {'url': dep_info}
+ dep_info.setdefault('dep_type', 'git')
+ new_deps_dict[dep_name] = dep_info
+ return new_deps_dict
+
+
+def _MergeDepsOs(deps_dict, os_deps_dict, os_name):
+ """Merges the deps in os_deps_dict into conditional dependencies in deps_dict.
+
+ The dependencies in os_deps_dict are transformed into conditional dependencies
+ using |'checkout_' + os_name|.
+ If the dependency is already present, the URL and revision must coincide.
+ """
+ for dep_name, dep_info in os_deps_dict.items():
+ # Make this condition very visible, so it's not a silent failure.
+ # It's unclear how to support None override in deps_os.
+ if dep_info['url'] is None:
+ logging.error('Ignoring %r:%r in %r deps_os', dep_name, dep_info, os_name)
+ continue
+
+ os_condition = 'checkout_' + (os_name if os_name != 'unix' else 'linux')
+ UpdateCondition(dep_info, 'and', os_condition)
+
+ if dep_name in deps_dict:
+ if deps_dict[dep_name]['url'] != dep_info['url']:
+ raise gclient_utils.Error(
+ 'Value from deps_os (%r; %r: %r) conflicts with existing deps '
+ 'entry (%r).' % (
+ os_name, dep_name, dep_info, deps_dict[dep_name]))
+
+ UpdateCondition(dep_info, 'or', deps_dict[dep_name].get('condition'))
+
+ deps_dict[dep_name] = dep_info
+
+
+def UpdateCondition(info_dict, op, new_condition):
+ """Updates info_dict's condition with |new_condition|.
+
+ An absent value is treated as implicitly True.
+ """
+ curr_condition = info_dict.get('condition')
+ # Easy case: Both are present.
+ if curr_condition and new_condition:
+ info_dict['condition'] = '(%s) %s (%s)' % (
+ curr_condition, op, new_condition)
+ # If |op| == 'and', and at least one condition is present, then use it.
+ elif op == 'and' and (curr_condition or new_condition):
+ info_dict['condition'] = curr_condition or new_condition
+ # Otherwise, no condition should be set
+ elif curr_condition:
+ del info_dict['condition']
+
+
+def Parse(content, expand_vars, validate_syntax, filename, vars_override=None):
+ """Parses DEPS strings.
+
+ Executes the Python-like string stored in content, resulting in a Python
+ dictionary specifyied by the schema above. Supports syntax validation and
+ variable expansion.
+
+ Args:
+ content: str. DEPS file stored as a string.
+ expand_vars: bool. Whether variables should be expanded to their values.
+ validate_syntax: bool. Whether syntax should be validated using the schema
+ defined above.
+ filename: str. The name of the DEPS file, or a string describing the source
+ of the content, e.g. '<string>', '<unknown>'.
+ vars_override: dict, optional. A dictionary with overrides for the variables
+ defined by the DEPS file.
+
+ Returns:
+ A Python dict with the parsed contents of the DEPS file, as specified by the
+ schema above.
+ """
+ if validate_syntax:
+ result = Exec(content, expand_vars, filename, vars_override)
+ else:
+ result = ExecLegacy(content, expand_vars, filename, vars_override)
+
+ vars_dict = result.get('vars', {})
+ if 'deps' in result:
+ result['deps'] = _StandardizeDeps(result['deps'], vars_dict)
+
+ if 'deps_os' in result:
+ deps = result.setdefault('deps', {})
+ for os_name, os_deps in result['deps_os'].iteritems():
+ os_deps = _StandardizeDeps(os_deps, vars_dict)
+ _MergeDepsOs(deps, os_deps, os_name)
+ del result['deps_os']
+
+ if 'hooks_os' in result:
+ hooks = result.setdefault('hooks', [])
+ for os_name, os_hooks in result['hooks_os'].iteritems():
+ for hook in os_hooks:
+ UpdateCondition(hook, 'and', 'checkout_' + os_name)
+ hooks.extend(os_hooks)
+ del result['hooks_os']
+
+ return result
+
+
def EvaluateCondition(condition, variables, referenced_variables=None):
"""Safely evaluates a boolean condition. Returns the result."""
if not referenced_variables: