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.py b/gclient.py
index b95979f..35377d6 100755
--- a/gclient.py
+++ b/gclient.py
@@ -167,16 +167,13 @@
def from_dict(d, variables=None, verbose=False, conditions=None):
"""Creates a Hook instance from a dict like in the DEPS file."""
# Merge any local and inherited conditions.
- if conditions and d.get('condition'):
- condition = '(%s) and (%s)' % (conditions, d['condition'])
- else:
- condition = conditions or d.get('condition')
+ gclient_eval.UpdateCondition(d, 'and', conditions)
return Hook(
d['action'],
d.get('pattern'),
d.get('name'),
d.get('cwd'),
- condition,
+ d.get('condition'),
variables=variables,
# Always print the header if not printing to a TTY.
verbose=verbose or not setup_color.IS_TTY)
@@ -392,8 +389,6 @@
# Calculates properties:
self._dependencies = []
self._vars = {}
- self._os_dependencies = {}
- self._os_deps_hooks = {}
# A cache of the files affected by the current operation, necessary for
# hooks.
@@ -597,52 +592,6 @@
return False
return True
- @staticmethod
- def MergeWithOsDeps(deps, deps_os, target_os_list, process_all_deps):
- """Returns a new "deps" structure that is the deps sent in updated
- with information from deps_os (the deps_os section of the DEPS
- file) that matches the list of target os."""
- new_deps = deps.copy()
- for dep_os, os_deps in deps_os.iteritems():
- for key, value in os_deps.iteritems():
- if value is None:
- # Make this condition very visible, so it's not a silent failure.
- # It's unclear how to support None override in deps_os.
- logging.error('Ignoring %r:%r in %r deps_os', key, value, dep_os)
- continue
-
- # Normalize value to be a dict which contains |should_process| metadata.
- if isinstance(value, basestring):
- value = {'url': value}
- assert isinstance(value, collections.Mapping), (key, value)
- value['should_process'] = dep_os in target_os_list or process_all_deps
-
- # Handle collisions/overrides.
- if key in new_deps and new_deps[key] != value:
- # Normalize the existing new_deps entry.
- if isinstance(new_deps[key], basestring):
- new_deps[key] = {'url': new_deps[key]}
- assert isinstance(new_deps[key],
- collections.Mapping), (key, new_deps[key])
-
- # It's OK if the "override" sets the key to the same value.
- # This is mostly for legacy reasons to keep existing DEPS files
- # working. Often mac/ios and unix/android will do this.
- if value['url'] != new_deps[key]['url']:
- raise gclient_utils.Error(
- ('Value from deps_os (%r; %r: %r) conflicts with existing deps '
- 'entry (%r).') % (dep_os, key, value, new_deps[key]))
-
- # We'd otherwise overwrite |should_process| metadata, but a dep should
- # be processed if _any_ of its references call for that.
- value['should_process'] = (
- value['should_process'] or
- new_deps[key].get('should_process', True))
-
- new_deps[key] = value
-
- return new_deps
-
def _postprocess_deps(self, deps, rel_prefix):
"""Performs post-processing of deps compared to what's in the DEPS file."""
# Make sure the dict is mutable, e.g. in case it's frozen.
@@ -653,6 +602,7 @@
for d in self.custom_deps:
if d not in deps:
deps[d] = self.custom_deps[d]
+
# Make child deps conditional on any parent conditions. This ensures that,
# when flattened, recursed entries have the correct restrictions, even if
# not explicitly set in the recursed DEPS file. For instance, if
@@ -660,17 +610,8 @@
# recursively included by "src/ios_foo/DEPS" should also require
# "checkout_ios=True".
if self.condition:
- for dname, dval in deps.iteritems():
- if isinstance(dval, basestring):
- dval = {'url': dval}
- deps[dname] = dval
- else:
- assert isinstance(dval, collections.Mapping)
- if dval.get('condition'):
- dval['condition'] = '(%s) and (%s)' % (
- dval['condition'], self.condition)
- else:
- dval['condition'] = self.condition
+ for value in deps.itervalues():
+ gclient_eval.UpdateCondition(value, 'and', self.condition)
if rel_prefix:
logging.warning('use_relative_paths enabled.')
@@ -697,21 +638,10 @@
if dep_value is None:
continue
- condition = None
- condition_value = True
- if isinstance(dep_value, basestring):
- raw_url = dep_value
- dep_type = None
- else:
- # This should be guaranteed by schema checking in gclient_eval.
- assert isinstance(dep_value, collections.Mapping)
- raw_url = dep_value.get('url')
- # Take into account should_process metadata set by MergeWithOsDeps.
- should_process = (should_process and
- dep_value.get('should_process', True))
- condition = dep_value.get('condition')
- dep_type = dep_value.get('dep_type')
+ condition = dep_value.get('condition')
+ dep_type = dep_value.get('dep_type')
+ condition_value = True
if condition:
condition_value = gclient_eval.EvaluateCondition(
condition, self.get_vars())
@@ -732,6 +662,7 @@
self.custom_vars, should_process, use_relative_paths,
condition, condition_value))
else:
+ raw_url = dep_value.get('url')
url = raw_url.format(**self.get_vars()) if raw_url else None
deps_to_add.append(
GitDependency(
@@ -824,10 +755,6 @@
elif self._relative:
rel_prefix = os.path.dirname(self.name)
- deps = {}
- for key, value in local_scope.get('deps', {}).iteritems():
- deps[key.format(**self.get_vars())] = value
-
if 'recursion' in local_scope:
self.recursion_override = local_scope.get('recursion')
logging.warning(
@@ -857,19 +784,8 @@
# If present, save 'target_os' in the local_target_os property.
if 'target_os' in local_scope:
self.local_target_os = local_scope['target_os']
- # load os specific dependencies if defined. these dependencies may
- # override or extend the values defined by the 'deps' member.
- target_os_list = self.target_os
- if 'deps_os' in local_scope:
- for dep_os, os_deps in local_scope['deps_os'].iteritems():
- self._os_dependencies[dep_os] = self._deps_to_objects(
- self._postprocess_deps(os_deps, rel_prefix), use_relative_paths)
- if target_os_list and not self._get_option(
- 'do_not_merge_os_specific_entries', False):
- deps = self.MergeWithOsDeps(
- deps, local_scope['deps_os'], target_os_list,
- self._get_option('process_all_deps', False))
+ deps = local_scope.get('deps', {})
deps_to_add = self._deps_to_objects(
self._postprocess_deps(deps, rel_prefix), use_relative_paths)
@@ -879,21 +795,6 @@
for hook in local_scope.get('hooks', []):
if hook.get('name', '') not in hook_names_to_suppress:
hooks_to_run.append(hook)
- if 'hooks_os' in local_scope and target_os_list:
- hooks_os = local_scope['hooks_os']
-
- # Keep original contents of hooks_os for flatten.
- for hook_os, os_hooks in hooks_os.iteritems():
- self._os_deps_hooks[hook_os] = [
- Hook.from_dict(hook, variables=self.get_vars(), verbose=True,
- conditions=self.condition)
- for hook in os_hooks]
-
- # Specifically append these to ensure that hooks_os run after hooks.
- if not self._get_option('do_not_merge_os_specific_entries', False):
- for the_target_os in target_os_list:
- the_target_os_hooks = hooks_os.get(the_target_os, [])
- hooks_to_run.extend(the_target_os_hooks)
# add the replacements and any additions
for hook in self.custom_hooks:
@@ -1198,21 +1099,11 @@
@property
@gclient_utils.lockedmethod
- def os_dependencies(self):
- return dict(self._os_dependencies)
-
- @property
- @gclient_utils.lockedmethod
def deps_hooks(self):
return tuple(self._deps_hooks)
@property
@gclient_utils.lockedmethod
- def os_deps_hooks(self):
- return dict(self._os_deps_hooks)
-
- @property
- @gclient_utils.lockedmethod
def pre_deps_hooks(self):
return tuple(self._pre_deps_hooks)
@@ -2097,9 +1988,7 @@
self._allowed_hosts = set()
self._deps = {}
- self._deps_os = {}
self._hooks = []
- self._hooks_os = {}
self._pre_deps_hooks = []
self._vars = {}
@@ -2146,10 +2035,6 @@
for dep in self._deps.itervalues():
self._pin_dep(dep)
- for os_deps in self._deps_os.itervalues():
- for dep in os_deps.itervalues():
- self._pin_dep(dep)
-
def add_deps_file(dep):
# Only include DEPS files referenced by recursedeps.
if not (dep.parent is None or
@@ -2168,9 +2053,6 @@
self._deps_files.add((dep.url, deps_file, dep.hierarchy_data()))
for dep in self._deps.itervalues():
add_deps_file(dep)
- for os_deps in self._deps_os.itervalues():
- for dep in os_deps.itervalues():
- add_deps_file(dep)
gn_args_dep = self._deps.get(self._client.dependencies[0]._gn_args_from,
self._client.dependencies[0])
@@ -2178,10 +2060,8 @@
_GNSettingsToLines(gn_args_dep._gn_args_file, gn_args_dep._gn_args) +
_AllowedHostsToLines(self._allowed_hosts) +
_DepsToLines(self._deps) +
- _DepsOsToLines(self._deps_os) +
_HooksToLines('hooks', self._hooks) +
_HooksToLines('pre_deps_hooks', self._pre_deps_hooks) +
- _HooksOsToLines(self._hooks_os) +
_VarsToLines(self._vars) +
['# %s, %s' % (url, deps_file)
for url, deps_file, _ in sorted(self._deps_files)] +
@@ -2198,31 +2078,16 @@
if dep.url:
self._deps[dep.name] = dep
- def _add_os_dep(self, os_dep, dep_os):
- """Helper to add an OS-specific dependency to flattened DEPS.
-
- Arguments:
- os_dep (Dependency): dependency to add
- dep_os (str): name of the OS
- """
- assert (
- os_dep.name not in self._deps_os.get(dep_os, {}) or
- self._deps_os.get(dep_os, {}).get(os_dep.name) == os_dep), (
- os_dep.name, self._deps_os.get(dep_os, {}).get(os_dep.name))
- if os_dep.url:
- self._deps_os.setdefault(dep_os, {})[os_dep.name] = os_dep
-
- def _flatten_dep(self, dep, dep_os=None):
+ def _flatten_dep(self, dep):
"""Visits a dependency in order to flatten it (see CMDflatten).
Arguments:
dep (Dependency): dependency to process
- dep_os (str or None): name of the OS |dep| is specific to
"""
- logging.debug('_flatten_dep(%s, %s)', dep.name, dep_os)
+ logging.debug('_flatten_dep(%s)', dep.name)
- if not dep.deps_parsed:
- dep.ParseDepsFile()
+ assert dep.deps_parsed, (
+ "Attempted to flatten %s but it has not been processed." % dep.name)
self._allowed_hosts.update(dep.allowed_hosts)
@@ -2249,41 +2114,14 @@
self._vars[key] = (hierarchy + ' [custom_var override]', value)
self._pre_deps_hooks.extend([(dep, hook) for hook in dep.pre_deps_hooks])
-
- if dep_os:
- if dep.deps_hooks:
- self._hooks_os.setdefault(dep_os, []).extend(
- [(dep, hook) for hook in dep.deps_hooks])
- else:
- self._hooks.extend([(dep, hook) for hook in dep.deps_hooks])
+ self._hooks.extend([(dep, hook) for hook in dep.deps_hooks])
for sub_dep in dep.dependencies:
- if dep_os:
- self._add_os_dep(sub_dep, dep_os)
- else:
- self._add_dep(sub_dep)
+ self._add_dep(sub_dep)
- for hook_os, os_hooks in dep.os_deps_hooks.iteritems():
- self._hooks_os.setdefault(hook_os, []).extend(
- [(dep, hook) for hook in os_hooks])
-
- for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
- for os_dep in os_deps:
- self._add_os_dep(os_dep, sub_dep_os)
-
- # Process recursedeps. |deps_by_name| is a map where keys are dependency
- # names, and values are maps of OS names to |Dependency| instances.
- # |None| in place of OS name means the dependency is not OS-specific.
- deps_by_name = dict((d.name, {None: d}) for d in dep.dependencies)
- for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
- for os_dep in os_deps:
- assert sub_dep_os not in deps_by_name.get(os_dep.name, {}), (
- os_dep.name, sub_dep_os)
- deps_by_name.setdefault(os_dep.name, {})[sub_dep_os] = os_dep
+ deps_by_name = {d.name: d for d in dep.dependencies}
for recurse_dep_name in (dep.recursedeps or []):
- dep_info = deps_by_name[recurse_dep_name]
- for sub_dep_os, os_dep in dep_info.iteritems():
- self._flatten_dep(os_dep, dep_os=(sub_dep_os or dep_os))
+ self._flatten_dep(deps_by_name[recurse_dep_name])
def CMDflatten(parser, args):
@@ -2299,7 +2137,6 @@
'for checked out deps, NOT deps_os.'))
options, args = parser.parse_args(args)
- options.do_not_merge_os_specific_entries = True
options.nohooks = True
options.process_all_deps = True
client = GClient.LoadCurrentConfig(options)
@@ -2691,10 +2528,6 @@
help='override deps for the specified (comma-separated) '
'platform(s); \'all\' will process all deps_os '
'references')
- # TODO(phajdan.jr): use argparse.SUPPRESS to hide internal flags.
- parser.add_option('--do-not-merge-os-specific-entries', action='store_true',
- help='INTERNAL ONLY - disables merging of deps_os and '
- 'hooks_os to dependencies and hooks')
parser.add_option('--process-all-deps', action='store_true',
help='Check out all deps, even for different OS-es, '
'or with conditions evaluating to false')
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:
diff --git a/tests/gclient_eval_unittest.py b/tests/gclient_eval_unittest.py
index f341c98..f29945c 100755
--- a/tests/gclient_eval_unittest.py
+++ b/tests/gclient_eval_unittest.py
@@ -186,6 +186,49 @@
str(cm.exception))
+class UpdateConditionTest(unittest.TestCase):
+ def test_both_present(self):
+ info = {'condition': 'foo'}
+ gclient_eval.UpdateCondition(info, 'and', 'bar')
+ self.assertEqual(info, {'condition': '(foo) and (bar)'})
+
+ info = {'condition': 'foo'}
+ gclient_eval.UpdateCondition(info, 'or', 'bar')
+ self.assertEqual(info, {'condition': '(foo) or (bar)'})
+
+ def test_one_present_and(self):
+ # If one of info's condition or new_condition is present, and |op| == 'and'
+ # then the the result must be the present condition.
+ info = {'condition': 'foo'}
+ gclient_eval.UpdateCondition(info, 'and', None)
+ self.assertEqual(info, {'condition': 'foo'})
+
+ info = {}
+ gclient_eval.UpdateCondition(info, 'and', 'bar')
+ self.assertEqual(info, {'condition': 'bar'})
+
+ def test_both_absent_and(self):
+ # Nothing happens
+ info = {}
+ gclient_eval.UpdateCondition(info, 'and', None)
+ self.assertEqual(info, {})
+
+ def test_or(self):
+ # If one of info's condition and new_condition is not present, then there
+ # shouldn't be a condition. An absent value is treated as implicitly True.
+ info = {'condition': 'foo'}
+ gclient_eval.UpdateCondition(info, 'or', None)
+ self.assertEqual(info, {})
+
+ info = {}
+ gclient_eval.UpdateCondition(info, 'or', 'bar')
+ self.assertEqual(info, {})
+
+ info = {}
+ gclient_eval.UpdateCondition(info, 'or', None)
+ self.assertEqual(info, {})
+
+
class EvaluateConditionTest(unittest.TestCase):
def test_true(self):
self.assertTrue(gclient_eval.EvaluateCondition('True', {}))
@@ -573,8 +616,9 @@
for validate_syntax in True, False:
local_scope = self.callParse(validate_syntax=validate_syntax)
self.assertEqual({
- 'vars': collections.OrderedDict([('foo', 'bar')]),
- 'deps': collections.OrderedDict([('a_dep', 'abarb')]),
+ 'vars': {'foo': 'bar'},
+ 'deps': {'a_dep': {'url': 'abarb',
+ 'dep_type': 'git'}},
}, local_scope)
def test_no_expands_vars(self):
@@ -582,8 +626,9 @@
local_scope = self.callParse(False,
validate_syntax=validate_syntax)
self.assertEqual({
- 'vars': collections.OrderedDict([('foo', 'bar')]),
- 'deps': collections.OrderedDict([('a_dep', 'a{foo}b')]),
+ 'vars': {'foo': 'bar'},
+ 'deps': {'a_dep': {'url': 'a{foo}b',
+ 'dep_type': 'git'}},
}, local_scope)
def test_overrides_vars(self):
@@ -591,8 +636,9 @@
local_scope = self.callParse(validate_syntax=validate_syntax,
vars_override={'foo': 'baz'})
self.assertEqual({
- 'vars': collections.OrderedDict([('foo', 'bar')]),
- 'deps': collections.OrderedDict([('a_dep', 'abazb')]),
+ 'vars': {'foo': 'bar'},
+ 'deps': {'a_dep': {'url': 'abazb',
+ 'dep_type': 'git'}},
}, local_scope)
def test_no_extra_vars(self):
@@ -618,6 +664,171 @@
'<unknown>', {'baz': 'lalala'})
self.assertIn('baz', str(cm.exception))
+ def test_standardizes_deps_string_dep(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": "a_url@a_rev",',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git'}},
+ }, local_scope)
+
+ def test_standardizes_deps_dict_dep(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": {',
+ ' "url": "a_url@a_rev",',
+ ' "condition": "checkout_android",',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git',
+ 'condition': 'checkout_android'}},
+ }, local_scope)
+
+ def test_ignores_none_in_deps_os(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": "a_url@a_rev",',
+ '}',
+ 'deps_os = {',
+ ' "mac": {',
+ ' "a_dep": None,',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git'}},
+ }, local_scope)
+
+ def test_merges_deps_os_extra_dep(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": "a_url@a_rev",',
+ '}',
+ 'deps_os = {',
+ ' "mac": {',
+ ' "b_dep": "b_url@b_rev"',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git'},
+ 'b_dep': {'url': 'b_url@b_rev',
+ 'dep_type': 'git',
+ 'condition': 'checkout_mac'}},
+ }, local_scope)
+
+ def test_merges_deps_os_existing_dep_with_no_condition(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": "a_url@a_rev",',
+ '}',
+ 'deps_os = {',
+ ' "mac": {',
+ ' "a_dep": "a_url@a_rev"',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git'}},
+ }, local_scope)
+
+ def test_merges_deps_os_existing_dep_with_condition(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": {',
+ ' "url": "a_url@a_rev",',
+ ' "condition": "some_condition",',
+ ' },',
+ '}',
+ 'deps_os = {',
+ ' "mac": {',
+ ' "a_dep": "a_url@a_rev"',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {
+ 'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git',
+ 'condition': '(checkout_mac) or (some_condition)'},
+ },
+ }, local_scope)
+
+ def test_merges_deps_os_multiple_os(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'deps_os = {',
+ ' "win": {'
+ ' "a_dep": "a_url@a_rev"',
+ ' },',
+ ' "mac": {',
+ ' "a_dep": "a_url@a_rev"',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ 'deps': {
+ 'a_dep': {'url': 'a_url@a_rev',
+ 'dep_type': 'git',
+ 'condition': '(checkout_mac) or (checkout_win)'},
+ },
+ }, local_scope)
+
+ def test_fails_to_merge_same_dep_with_different_revisions(self):
+ for validate_syntax in True, False:
+ with self.assertRaises(gclient_eval.gclient_utils.Error) as cm:
+ gclient_eval.Parse('\n'.join([
+ 'deps = {',
+ ' "a_dep": {',
+ ' "url": "a_url@a_rev",',
+ ' "condition": "some_condition",',
+ ' },',
+ '}',
+ 'deps_os = {',
+ ' "mac": {',
+ ' "a_dep": "a_url@b_rev"',
+ ' },',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertIn('conflicts with existing deps', str(cm.exception))
+
+ def test_merges_hooks_os(self):
+ for validate_syntax in True, False:
+ local_scope = gclient_eval.Parse('\n'.join([
+ 'hooks = [',
+ ' {',
+ ' "action": ["a", "action"],',
+ ' },',
+ ']',
+ 'hooks_os = {',
+ ' "mac": [',
+ ' {',
+ ' "action": ["b", "action"]',
+ ' },',
+ ' ]',
+ '}',
+ ]), False, validate_syntax, '<unknown>')
+ self.assertEqual({
+ "hooks": [{"action": ["a", "action"]},
+ {"action": ["b", "action"], "condition": "checkout_mac"}],
+ }, local_scope)
+
+
if __name__ == '__main__':
level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
diff --git a/tests/gclient_smoketest.py b/tests/gclient_smoketest.py
index b566556..751ffad 100755
--- a/tests/gclient_smoketest.py
+++ b/tests/gclient_smoketest.py
@@ -875,6 +875,18 @@
' "url": "git://127.0.0.1:20000/git/repo_6",',
' },',
'',
+ ' # src -> src/mac_repo',
+ ' "src/mac_repo": {',
+ ' "url": "{repo5_var}",',
+ ' "condition": \'checkout_mac\',',
+ ' },',
+ '',
+ ' # src -> src/repo8 -> src/recursed_os_repo',
+ ' "src/recursed_os_repo": {',
+ ' "url": "/repo_5",',
+ ' "condition": \'(checkout_linux) or (checkout_mac)\',',
+ ' },',
+ '',
' # src -> src/repo2',
' "src/repo2": {',
' "url": "{git_base}repo_2@%s",' % (
@@ -893,41 +905,16 @@
' "url": "/repo_8",',
' },',
'',
- '}',
- '',
- 'deps_os = {',
- ' "mac": {',
- ' # src -> src/mac_repo',
- ' "src/mac_repo": {',
- ' "url": "{repo5_var}",',
- ' },',
- '',
- ' # src -> src/repo8 -> src/recursed_os_repo',
- ' "src/recursed_os_repo": {',
- ' "url": "/repo_5",',
- ' },',
- '',
+ ' # src -> src/unix_repo',
+ ' "src/unix_repo": {',
+ ' "url": "{repo5_var}",',
+ ' "condition": \'checkout_linux\',',
' },',
'',
- ' "unix": {',
- ' # src -> src/repo8 -> src/recursed_os_repo',
- ' "src/recursed_os_repo": {',
- ' "url": "/repo_5",',
- ' },',
- '',
- ' # src -> src/unix_repo',
- ' "src/unix_repo": {',
- ' "url": "{repo5_var}",',
- ' },',
- '',
- ' },',
- '',
- ' "win": {',
- ' # src -> src/win_repo',
- ' "src/win_repo": {',
- ' "url": "{repo5_var}",',
- ' },',
- '',
+ ' # src -> src/win_repo',
+ ' "src/win_repo": {',
+ ' "url": "{repo5_var}",',
+ ' "condition": \'checkout_win\',',
' },',
'',
'}',
@@ -957,26 +944,21 @@
' ]',
' },',
'',
+ ' # src',
+ ' {',
+ ' "pattern": ".",',
+ ' "condition": \'checkout_mac\',',
+ ' "cwd": ".",',
+ ' "action": [',
+ ' "python",',
+ ' "-c",',
+ ' "open(\'src/git_hooked_mac\', \'w\').write('
+ '\'git_hooked_mac\')",',
+ ' ]',
+ ' },',
+ '',
']',
'',
- 'hooks_os = {',
- ' "mac": [',
- ' # src',
- ' {',
- ' "pattern": ".",',
- ' "cwd": ".",',
- ' "action": [',
- ' "python",',
- ' "-c",',
- ' "open(\'src/git_hooked_mac\', \'w\').write('
- '\'git_hooked_mac\')",',
- ' ]',
- ' },',
- '',
- ' ],',
- '',
- '}',
- '',
'vars = {',
' # src',
' "DummyVariable": \'repo\',',
@@ -1055,6 +1037,18 @@
self.githash('repo_6', 1)),
' },',
'',
+ ' # src -> src/mac_repo',
+ ' "src/mac_repo": {',
+ ' "url": "{repo5_var}@%s",' % (self.githash('repo_5', 3)),
+ ' "condition": \'checkout_mac\',',
+ ' },',
+ '',
+ ' # src -> src/repo8 -> src/recursed_os_repo',
+ ' "src/recursed_os_repo": {',
+ ' "url": "/repo_5@%s",' % (self.githash('repo_5', 3)),
+ ' "condition": \'(checkout_linux) or (checkout_mac)\',',
+ ' },',
+ '',
' # src -> src/repo2',
' "src/repo2": {',
' "url": "{git_base}repo_2@%s",' % (
@@ -1073,41 +1067,16 @@
' "url": "/repo_8@%s",' % (self.githash('repo_8', 1)),
' },',
'',
- '}',
- '',
- 'deps_os = {',
- ' "mac": {',
- ' # src -> src/mac_repo',
- ' "src/mac_repo": {',
- ' "url": "{repo5_var}@%s",' % (self.githash('repo_5', 3)),
- ' },',
- '',
- ' # src -> src/repo8 -> src/recursed_os_repo',
- ' "src/recursed_os_repo": {',
- ' "url": "/repo_5@%s",' % (self.githash('repo_5', 3)),
- ' },',
- '',
+ ' # src -> src/unix_repo',
+ ' "src/unix_repo": {',
+ ' "url": "{repo5_var}@%s",' % (self.githash('repo_5', 3)),
+ ' "condition": \'checkout_linux\',',
' },',
'',
- ' "unix": {',
- ' # src -> src/repo8 -> src/recursed_os_repo',
- ' "src/recursed_os_repo": {',
- ' "url": "/repo_5@%s",' % (self.githash('repo_5', 3)),
- ' },',
- '',
- ' # src -> src/unix_repo',
- ' "src/unix_repo": {',
- ' "url": "{repo5_var}@%s",' % (self.githash('repo_5', 3)),
- ' },',
- '',
- ' },',
- '',
- ' "win": {',
- ' # src -> src/win_repo',
- ' "src/win_repo": {',
- ' "url": "{repo5_var}@%s",' % (self.githash('repo_5', 3)),
- ' },',
- '',
+ ' # src -> src/win_repo',
+ ' "src/win_repo": {',
+ ' "url": "{repo5_var}@%s",' % (self.githash('repo_5', 3)),
+ ' "condition": \'checkout_win\',',
' },',
'',
'}',
@@ -1137,26 +1106,21 @@
' ]',
' },',
'',
+ ' # src',
+ ' {',
+ ' "pattern": ".",',
+ ' "condition": \'checkout_mac\',',
+ ' "cwd": ".",',
+ ' "action": [',
+ ' "python",',
+ ' "-c",',
+ ' "open(\'src/git_hooked_mac\', \'w\').write('
+ '\'git_hooked_mac\')",',
+ ' ]',
+ ' },',
+ '',
']',
'',
- 'hooks_os = {',
- ' "mac": [',
- ' # src',
- ' {',
- ' "pattern": ".",',
- ' "cwd": ".",',
- ' "action": [',
- ' "python",',
- ' "-c",',
- ' "open(\'src/git_hooked_mac\', \'w\').write('
- '\'git_hooked_mac\')",',
- ' ]',
- ' },',
- '',
- ' ],',
- '',
- '}',
- '',
'vars = {',
' # src',
' "DummyVariable": \'repo\',',
@@ -1218,6 +1182,7 @@
with open(output_deps) as f:
deps_contents = f.read()
+ self.maxDiff = None
self.assertEqual([
'gclient_gn_args_file = "src/repo2/gclient.args"',
"gclient_gn_args = ['str_var']",
@@ -1227,6 +1192,30 @@
' "url": "git://127.0.0.1:20000/git/repo_10",',
' },',
'',
+ ' # src -> src/repo9 -> src/repo8 -> src/recursed_os_repo',
+ ' "src/recursed_os_repo": {',
+ ' "url": "/repo_5",',
+ ' "condition": \'(checkout_linux) or (checkout_mac)\',',
+ ' },',
+ '',
+ ' # src -> src/repo11',
+ ' "src/repo11": {',
+ ' "url": "/repo_11",',
+ ' "condition": \'(checkout_ios) or (checkout_mac)\',',
+ ' },',
+ '',
+ ' # src -> src/repo11 -> src/repo12',
+ ' "src/repo12": {',
+ ' "url": "/repo_12",',
+ ' "condition": \'(checkout_ios) or (checkout_mac)\',',
+ ' },',
+ '',
+ ' # src -> src/repo9 -> src/repo4',
+ ' "src/repo4": {',
+ ' "url": "/repo_4",',
+ ' "condition": \'checkout_android\',',
+ ' },',
+ '',
' # src -> src/repo6',
' "src/repo6": {',
' "url": "/repo_6",',
@@ -1249,56 +1238,6 @@
'',
'}',
'',
- 'deps_os = {',
- ' "android": {',
- ' # src -> src/repo9 -> src/repo4',
- ' "src/repo4": {',
- ' "url": "/repo_4",',
- ' },',
- '',
- ' },',
- '',
- ' "ios": {',
- ' # src -> src/repo11',
- ' "src/repo11": {',
- ' "url": "/repo_11",',
- ' },',
- '',
- ' # src -> src/repo11 -> src/repo12',
- ' "src/repo12": {',
- ' "url": "/repo_12",',
- ' },',
- '',
- ' },',
- '',
- ' "mac": {',
- ' # src -> src/repo9 -> src/repo8 -> src/recursed_os_repo',
- ' "src/recursed_os_repo": {',
- ' "url": "/repo_5",',
- ' },',
- '',
- ' # src -> src/repo11',
- ' "src/repo11": {',
- ' "url": "/repo_11",',
- ' },',
- '',
- ' # src -> src/repo11 -> src/repo12',
- ' "src/repo12": {',
- ' "url": "/repo_12",',
- ' },',
- '',
- ' },',
- '',
- ' "unix": {',
- ' # src -> src/repo9 -> src/repo8 -> src/recursed_os_repo',
- ' "src/recursed_os_repo": {',
- ' "url": "/repo_5",',
- ' },',
- '',
- ' },',
- '',
- '}',
- '',
'vars = {',
' # src -> src/repo9',
' "str_var": \'xyz\',',
diff --git a/tests/gclient_test.py b/tests/gclient_test.py
index 996346d..c797552 100755
--- a/tests/gclient_test.py
+++ b/tests/gclient_test.py
@@ -436,19 +436,10 @@
' }]\n')
write(
os.path.join('foo', 'DEPS'),
- 'target_os = ["baz"]\n'
- 'deps_os = {\n'
- ' "unix": { "foo/unix": "/unix", },\n'
- ' "baz": { "foo/baz": "/baz", },\n'
- ' "jaz": { "foo/jaz": "/jaz", },\n'
- '}')
+ 'target_os = ["baz"]\n')
write(
os.path.join('bar', 'DEPS'),
- 'deps_os = {\n'
- ' "unix": { "bar/unix": "/unix", },\n'
- ' "baz": { "bar/baz": "/baz", },\n'
- ' "jaz": { "bar/jaz": "/jaz", },\n'
- '}')
+ '')
parser = gclient.OptionParser()
options, _ = parser.parse_args(['--jobs', '1'])
@@ -457,15 +448,11 @@
obj = gclient.GClient.LoadCurrentConfig(options)
obj.RunOnDeps('None', [])
self.assertEqual(['unix'], sorted(obj.enforced_os))
- self.assertEquals(
- [
- ('bar', 'svn://example.com/bar'),
- ('bar/unix', 'svn://example.com/unix'),
- ('foo', 'svn://example.com/foo'),
- ('foo/baz', 'svn://example.com/baz'),
- ('foo/unix', 'svn://example.com/unix'),
- ],
- sorted(self._get_processed()))
+ self.assertEqual([('unix', 'baz'), ('unix',)],
+ [dep.target_os for dep in obj.dependencies])
+ self.assertEqual([('foo', 'svn://example.com/foo'),
+ ('bar', 'svn://example.com/bar')],
+ self._get_processed())
def testTargetOsForHooksInDepsFile(self):
"""Verifies that specifying a target_os value in a DEPS file runs the right
@@ -506,119 +493,15 @@
obj = gclient.GClient.LoadCurrentConfig(options)
obj.RunOnDeps('None', args)
self.assertEqual(['zippy'], sorted(obj.enforced_os))
- all_hooks = [h.action for h in obj.GetHooks(options)]
+ all_hooks = obj.GetHooks(options)
self.assertEquals(
[('.', 'svn://example.com/'),],
sorted(self._get_processed()))
- self.assertEquals(all_hooks,
- [('python', 'do_a')])
-
- # Test for OS that has extra hooks in hooks_os.
- parser = gclient.OptionParser()
- options, args = parser.parse_args(['--jobs', '1'])
- options.deps_os = 'blorp'
-
- obj = gclient.GClient.LoadCurrentConfig(options)
- obj.RunOnDeps('None', args)
- self.assertEqual(['blorp'], sorted(obj.enforced_os))
- all_hooks = [h.action for h in obj.GetHooks(options)]
- self.assertEquals(
- [('.', 'svn://example.com/'),],
- sorted(self._get_processed()))
- self.assertEquals(all_hooks,
+ self.assertEquals([h.action for h in all_hooks],
[('python', 'do_a'),
('python', 'do_b')])
-
-
- def testUpdateWithOsDeps(self):
- """Verifies that complicated deps_os constructs result in the
- correct data also with multple operating systems. Also see
- testDepsOsOverrideDepsInDepsFile."""
-
- test_data = [
- # Tuples of deps, deps_os, os_list and expected_deps.
- (
- # OS with no overrides at all.
- {'foo': 'default_foo'},
- {'os1': { 'foo': None } },
- ['os2'],
- {'foo': 'default_foo'}
- ),
- (
- # One OS wants to add a module.
- {'foo': 'default_foo'},
- {'os1': { 'bar': 'os1_bar' }},
- ['os1'],
- {'foo': 'default_foo',
- 'bar': {'should_process': True, 'url': 'os1_bar'}}
- ),
- (
- # One OS wants to add a module. One doesn't care.
- {'foo': 'default_foo'},
- {'os1': { 'bar': 'os1_bar' }},
- ['os1', 'os2'],
- {'foo': 'default_foo',
- 'bar': {'should_process': True, 'url': 'os1_bar'}}
- ),
- (
- # Two OSes want to add a module with the same definition.
- {'foo': 'default_foo'},
- {'os1': { 'bar': 'os12_bar' },
- 'os2': { 'bar': 'os12_bar' }},
- ['os1', 'os2'],
- {'foo': 'default_foo',
- 'bar': {'should_process': True, 'url': 'os12_bar'}}
- ),
- (
- # One OS doesn't need module, one OS wants the default.
- {'foo': 'default_foo'},
- {'os1': { 'foo': None },
- 'os2': {}},
- ['os1', 'os2'],
- {'foo': 'default_foo'}
- ),
- (
- # OS doesn't need module.
- {'foo': 'default_foo'},
- {'os1': { 'foo': None } },
- ['os1'],
- {'foo': 'default_foo'}
- ),
- (
- # No-op override. Regression test for http://crbug.com/735418 .
- {'foo': 'default_foo'},
- {'os1': { 'foo': 'default_foo' } },
- [],
- {'foo': {'should_process': True, 'url': 'default_foo'}}
- ),
- ]
- for deps, deps_os, target_os_list, expected_deps in test_data:
- orig_deps = copy.deepcopy(deps)
- result = gclient.Dependency.MergeWithOsDeps(
- deps, deps_os, target_os_list, False)
- self.assertEqual(result, expected_deps)
- self.assertEqual(deps, orig_deps)
-
- def testUpdateWithOsDepsInvalid(self):
- test_data = [
- # Tuples of deps, deps_os, os_list.
- (
- # OS wants a different version of module.
- {'foo': 'default_foo'},
- {'os1': { 'foo': 'os1_foo'} },
- ['os1'],
- ),
- (
- # One OS doesn't need module, another OS wants a special version.
- {'foo': 'default_foo'},
- {'os1': { 'foo': None },
- 'os2': { 'foo': 'os2_foo'}},
- ['os1', 'os2'],
- ),
- ]
- for deps, deps_os, target_os_list in test_data:
- with self.assertRaises(gclient_utils.Error):
- gclient.Dependency.MergeWithOsDeps(deps, deps_os, target_os_list, False)
+ self.assertEquals([h.condition for h in all_hooks],
+ [None, 'checkout_blorp'])
def testOverride(self):
"""Verifies expected behavior of OverrideURL."""