| Index: infra/recipe_modules/gclient/api.py
|
| diff --git a/infra/recipe_modules/gclient/api.py b/infra/recipe_modules/gclient/api.py
|
| deleted file mode 100644
|
| index fdeae226b431c2f304851855c3597d9a7cfb0dd4..0000000000000000000000000000000000000000
|
| --- a/infra/recipe_modules/gclient/api.py
|
| +++ /dev/null
|
| @@ -1,309 +0,0 @@
|
| -# Copyright 2013 The Chromium Authors. All rights reserved.
|
| -# Use of this source code is governed by a BSD-style license that can be
|
| -# found in the LICENSE file.
|
| -
|
| -from recipe_engine import recipe_api
|
| -
|
| -
|
| -class RevisionResolver(object):
|
| - """Resolves the revision based on build properties."""
|
| -
|
| - def resolve(self, properties): # pragma: no cover
|
| - raise NotImplementedError()
|
| -
|
| -
|
| -class RevisionFallbackChain(RevisionResolver):
|
| - """Specify that a given project's sync revision follows the fallback chain."""
|
| - def __init__(self, default=None):
|
| - self._default = default
|
| -
|
| - def resolve(self, properties):
|
| - """Resolve the revision via the revision fallback chain.
|
| -
|
| - If the given revision was set using the revision_fallback_chain() function,
|
| - this function will follow the chain, looking at relevant build properties
|
| - until it finds one set or reaches the end of the chain and returns the
|
| - default. If the given revision was not set using revision_fallback_chain(),
|
| - this function just returns it as-is.
|
| - """
|
| - return (properties.get('parent_got_revision') or
|
| - properties.get('orig_revision') or
|
| - properties.get('revision') or
|
| - self._default)
|
| -
|
| -
|
| -def jsonish_to_python(spec, is_top=False):
|
| - ret = ''
|
| - if is_top: # We're the 'top' level, so treat this dict as a suite.
|
| - ret = '\n'.join(
|
| - '%s = %s' % (k, jsonish_to_python(spec[k])) for k in sorted(spec)
|
| - )
|
| - else:
|
| - if isinstance(spec, dict):
|
| - ret += '{'
|
| - ret += ', '.join(
|
| - "%s: %s" % (repr(str(k)), jsonish_to_python(spec[k]))
|
| - for k in sorted(spec)
|
| - )
|
| - ret += '}'
|
| - elif isinstance(spec, list):
|
| - ret += '['
|
| - ret += ', '.join(jsonish_to_python(x) for x in spec)
|
| - ret += ']'
|
| - elif isinstance(spec, basestring):
|
| - ret = repr(str(spec))
|
| - else:
|
| - ret = repr(spec)
|
| - return ret
|
| -
|
| -class GclientApi(recipe_api.RecipeApi):
|
| - # Singleton object to indicate to checkout() that we should run a revert if
|
| - # we detect that we're on the tryserver.
|
| - RevertOnTryserver = object()
|
| -
|
| - def __init__(self, **kwargs):
|
| - super(GclientApi, self).__init__(**kwargs)
|
| - self.USE_MIRROR = None
|
| - self._spec_alias = None
|
| -
|
| - def __call__(self, name, cmd, infra_step=True, **kwargs):
|
| - """Wrapper for easy calling of gclient steps."""
|
| - assert isinstance(cmd, (list, tuple))
|
| - prefix = 'gclient '
|
| - if self.spec_alias:
|
| - prefix = ('[spec: %s] ' % self.spec_alias) + prefix
|
| -
|
| - return self.m.python(prefix + name,
|
| - self.m.depot_tools.gclient_py,
|
| - cmd,
|
| - infra_step=infra_step,
|
| - **kwargs)
|
| -
|
| - @property
|
| - def use_mirror(self):
|
| - """Indicates if gclient will use mirrors in its configuration."""
|
| - if self.USE_MIRROR is None:
|
| - self.USE_MIRROR = self.m.properties.get('use_mirror', True)
|
| - return self.USE_MIRROR
|
| -
|
| - @use_mirror.setter
|
| - def use_mirror(self, val): # pragma: no cover
|
| - self.USE_MIRROR = val
|
| -
|
| - @property
|
| - def spec_alias(self):
|
| - """Optional name for the current spec for step naming."""
|
| - return self._spec_alias
|
| -
|
| - @spec_alias.setter
|
| - def spec_alias(self, name):
|
| - self._spec_alias = name
|
| -
|
| - @spec_alias.deleter
|
| - def spec_alias(self):
|
| - self._spec_alias = None
|
| -
|
| - def get_config_defaults(self):
|
| - ret = {
|
| - 'USE_MIRROR': self.use_mirror
|
| - }
|
| - ret['CACHE_DIR'] = self.m.path['root'].join('git_cache')
|
| - return ret
|
| -
|
| - def resolve_revision(self, revision):
|
| - if hasattr(revision, 'resolve'):
|
| - return revision.resolve(self.m.properties)
|
| - return revision
|
| -
|
| - def sync(self, cfg, with_branch_heads=False, **kwargs):
|
| - revisions = []
|
| - for i, s in enumerate(cfg.solutions):
|
| - if s.safesync_url: # prefer safesync_url in gclient mode
|
| - continue
|
| - if i == 0 and s.revision is None:
|
| - s.revision = RevisionFallbackChain()
|
| -
|
| - if s.revision is not None and s.revision != '':
|
| - fixed_revision = self.resolve_revision(s.revision)
|
| - if fixed_revision:
|
| - revisions.extend(['--revision', '%s@%s' % (s.name, fixed_revision)])
|
| -
|
| - for name, revision in sorted(cfg.revisions.items()):
|
| - fixed_revision = self.resolve_revision(revision)
|
| - if fixed_revision:
|
| - revisions.extend(['--revision', '%s@%s' % (name, fixed_revision)])
|
| -
|
| - test_data_paths = set(cfg.got_revision_mapping.keys() +
|
| - [s.name for s in cfg.solutions])
|
| - step_test_data = lambda: (
|
| - self.test_api.output_json(test_data_paths, cfg.GIT_MODE))
|
| - try:
|
| - if not cfg.GIT_MODE:
|
| - args = ['sync', '--nohooks', '--force', '--verbose']
|
| - if cfg.delete_unversioned_trees:
|
| - args.append('--delete_unversioned_trees')
|
| - if with_branch_heads:
|
| - args.append('--with_branch_heads')
|
| - self('sync', args + revisions + ['--output-json', self.m.json.output()],
|
| - step_test_data=step_test_data,
|
| - **kwargs)
|
| - else:
|
| - # clean() isn't used because the gclient sync flags passed in checkout()
|
| - # do much the same thing, and they're more correct than doing a separate
|
| - # 'gclient revert' because it makes sure the other args are correct when
|
| - # a repo was deleted and needs to be re-cloned (notably
|
| - # --with_branch_heads), whereas 'revert' uses default args for clone
|
| - # operations.
|
| - #
|
| - # TODO(mmoss): To be like current official builders, this step could
|
| - # just delete the whole <slave_name>/build/ directory and start each
|
| - # build from scratch. That might be the least bad solution, at least
|
| - # until we have a reliable gclient method to produce a pristine working
|
| - # dir for git-based builds (e.g. maybe some combination of 'git
|
| - # reset/clean -fx' and removing the 'out' directory).
|
| - j = '-j2' if self.m.platform.is_win else '-j8'
|
| - args = ['sync', '--verbose', '--with_branch_heads', '--nohooks', j,
|
| - '--reset', '--force', '--upstream', '--no-nag-max']
|
| - if cfg.delete_unversioned_trees:
|
| - args.append('--delete_unversioned_trees')
|
| - self('sync', args + revisions +
|
| - ['--output-json', self.m.json.output()],
|
| - step_test_data=step_test_data,
|
| - **kwargs)
|
| - finally:
|
| - result = self.m.step.active_result
|
| - data = result.json.output
|
| - for path, info in data['solutions'].iteritems():
|
| - # gclient json paths always end with a slash
|
| - path = path.rstrip('/')
|
| - if path in cfg.got_revision_mapping:
|
| - propname = cfg.got_revision_mapping[path]
|
| - result.presentation.properties[propname] = info['revision']
|
| -
|
| - return result
|
| -
|
| - def inject_parent_got_revision(self, gclient_config=None, override=False):
|
| - """Match gclient config to build revisions obtained from build_properties.
|
| -
|
| - Args:
|
| - gclient_config (gclient config object) - The config to manipulate. A value
|
| - of None manipulates the module's built-in config (self.c).
|
| - override (bool) - If True, will forcibly set revision and custom_vars
|
| - even if the config already contains values for them.
|
| - """
|
| - cfg = gclient_config or self.c
|
| -
|
| - for prop, custom_var in cfg.parent_got_revision_mapping.iteritems():
|
| - val = str(self.m.properties.get(prop, ''))
|
| - # TODO(infra): Fix coverage.
|
| - if val: # pragma: no cover
|
| - # Special case for 'src', inject into solutions[0]
|
| - if custom_var is None:
|
| - # This is not covered because we are deprecating this feature and
|
| - # it is no longer used by the public recipes.
|
| - if cfg.solutions[0].revision is None or override: # pragma: no cover
|
| - cfg.solutions[0].revision = val
|
| - else:
|
| - if custom_var not in cfg.solutions[0].custom_vars or override:
|
| - cfg.solutions[0].custom_vars[custom_var] = val
|
| -
|
| - def checkout(self, gclient_config=None, revert=RevertOnTryserver,
|
| - inject_parent_got_revision=True, with_branch_heads=False,
|
| - **kwargs):
|
| - """Return a step generator function for gclient checkouts."""
|
| - cfg = gclient_config or self.c
|
| - assert cfg.complete()
|
| -
|
| - if revert is self.RevertOnTryserver:
|
| - revert = self.m.hacky_tryserver_detection.is_tryserver
|
| -
|
| - if inject_parent_got_revision:
|
| - self.inject_parent_got_revision(cfg, override=True)
|
| -
|
| - spec_string = jsonish_to_python(cfg.as_jsonish(), True)
|
| -
|
| - self('setup', ['config', '--spec', spec_string], **kwargs)
|
| -
|
| - sync_step = None
|
| - try:
|
| - if not cfg.GIT_MODE:
|
| - try:
|
| - if revert:
|
| - self.revert(**kwargs)
|
| - finally:
|
| - sync_step = self.sync(cfg, with_branch_heads=with_branch_heads,
|
| - **kwargs)
|
| - else:
|
| - sync_step = self.sync(cfg, with_branch_heads=with_branch_heads,
|
| - **kwargs)
|
| -
|
| - cfg_cmds = [
|
| - ('user.name', 'local_bot'),
|
| - ('user.email', 'local_bot@example.com'),
|
| - ]
|
| - for var, val in cfg_cmds:
|
| - name = 'recurse (git config %s)' % var
|
| - self(name, ['recurse', 'git', 'config', var, val], **kwargs)
|
| -
|
| - finally:
|
| - cwd = kwargs.get('cwd', self.m.path['slave_build'])
|
| - if 'checkout' not in self.m.path:
|
| - self.m.path['checkout'] = cwd.join(
|
| - *cfg.solutions[0].name.split(self.m.path.sep))
|
| -
|
| - return sync_step
|
| -
|
| - def revert(self, **kwargs):
|
| - """Return a gclient_safe_revert step."""
|
| - # Not directly calling gclient, so don't use self().
|
| - alias = self.spec_alias
|
| - prefix = '%sgclient ' % (('[spec: %s] ' % alias) if alias else '')
|
| -
|
| - return self.m.python(prefix + 'revert',
|
| - self.m.path['build'].join('scripts', 'slave', 'gclient_safe_revert.py'),
|
| - ['.', self.m.path['depot_tools'].join('gclient',
|
| - platform_ext={'win': '.bat'})],
|
| - infra_step=True,
|
| - **kwargs
|
| - )
|
| -
|
| - def runhooks(self, args=None, name='runhooks', **kwargs):
|
| - args = args or []
|
| - assert isinstance(args, (list, tuple))
|
| - return self(
|
| - name, ['runhooks'] + list(args), infra_step=False, **kwargs)
|
| -
|
| - @property
|
| - def is_blink_mode(self):
|
| - """ Indicates wether the caller is to use the Blink config rather than the
|
| - Chromium config. This may happen for one of two reasons:
|
| - 1. The builder is configured to always use TOT Blink. (factory property
|
| - top_of_tree_blink=True)
|
| - 2. A try job comes in that applies to the Blink tree. (patch_project is
|
| - blink)
|
| - """
|
| - return (
|
| - self.m.properties.get('top_of_tree_blink') or
|
| - self.m.properties.get('patch_project') == 'blink')
|
| -
|
| - def break_locks(self):
|
| - """Remove all index.lock files. If a previous run of git crashed, bot was
|
| - reset, etc... we might end up with leftover index.lock files.
|
| - """
|
| - self.m.python.inline(
|
| - 'cleanup index.lock',
|
| - """
|
| - import os, sys
|
| -
|
| - build_path = sys.argv[1]
|
| - if os.path.exists(build_path):
|
| - for (path, dir, files) in os.walk(build_path):
|
| - for cur_file in files:
|
| - if cur_file.endswith('index.lock'):
|
| - path_to_file = os.path.join(path, cur_file)
|
| - print 'deleting %s' % path_to_file
|
| - os.remove(path_to_file)
|
| - """,
|
| - args=[self.m.path['slave_build']],
|
| - infra_step=True,
|
| - )
|
|
|