Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(284)

Unified Diff: scripts/slave/recipe_modules/auto_bisect/revision_state.py

Issue 940123005: Adding ability to bisect recipe to bisect into dependency repos. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@hax
Patch Set: Addressing feedback, adding TODOs Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: scripts/slave/recipe_modules/auto_bisect/revision_state.py
diff --git a/scripts/slave/recipe_modules/auto_bisect/revision_state.py b/scripts/slave/recipe_modules/auto_bisect/revision_state.py
index 9c9d2dd8314a27218154d480ede84a61aa98ffad..58bacacff66b658bbecef5f3d4e96e64328f4c7e 100644
--- a/scripts/slave/recipe_modules/auto_bisect/revision_state.py
+++ b/scripts/slave/recipe_modules/auto_bisect/revision_state.py
@@ -9,37 +9,81 @@ class so that the bisect module and recipe can use it.
See perf_revision_state for an example.
"""
+import hashlib
+import os
+import re
+
+from . import depot_config
+
class RevisionState(object):
"""Abstracts the state of a single revision on a bisect job."""
- def __init__(self, revision_string, bisector):
+ def __init__(self, revision_string, bisector, depot='chromium',
+ dependency_depot_name=None,base_revision=None,
+ deps_revision=None):
"""Create a new instance to track the state of a revision.
+ There are two use cases for this constructor:
+ - Creating a revision state for a chromium revision, OR
+ - Creating a revision state for a chromium revision plus an explicitly
+ specified revision of a dependency repository (a DEPS change).
+ In the first case a revision_string and a bisector are needed.
+ In the second case, revision_string must be None, and all of depot,
+ base_revision and deps_revision must be provided.
+
Args:
- revision_string: should be in the following format:
- [(chromium|src)@](<commit_pos>|<commit_hash)(,<repo>@<commit>)*
- E.g.:
- 'a0b1c2ffff89009909090' (full or abbrev. commit hash)
- '123456'
- 'src@123456'
- 'chromium@123456'
- 'src@abc01234ffff,v8@00af5ceb888ff'
- bisector: an instance of Bisector, the object performing the bisection.
+ revision_string (str): A git hash or a commit position in the chromium
+ repository. If None, all kwargs must be given.
+ bisector (Bisector): The object performing the bisection.
+ depot (dict): One of the entries in depot_config.DEPOT_DEPS_NAME that
+ specifies which dependency to do the DEPS change on. It is expected to
+ contain the 'chromium' string instead of None when not bisecting any
+ dependencies.
+ base_revision (RevisionState): The revision state to patch with the deps
+ change.
+ depot_revision: The commit hash of the dependency repo to put in place of
+ the one set for the base_revision.
"""
+ # TODO(robertocn): Evaluate if the logic of this constructor should be
+ # split into separate methods.
super(RevisionState, self).__init__()
self.bisector = bisector
self._good = None
+ self.deps = None
self.build_status_url = None
self.in_progress = False
self.aborted = False
self.next_revision = None
self.previous_revision = None
self.revision_string = revision_string
- self.commit_hash, self.commit_pos = self._commit_from_rev_string()
self.build_job_name = None
self.test_job_name = None
self.built = False
+ self.patch_file = None
+ if not self.revision_string:
+ assert base_revision
+ assert base_revision.deps_file_contents
+ assert depot != 'chromium'
+ assert deps_revision
+ self.needs_patch = True
+ self.depot = depot
+ self.revision_string = (base_revision.revision_string + ',' +
+ dependency_depot_name)
+ self.revision_string += '@' + deps_revision
+ self.deps_patch, self.deps_file_contents = self.bisector.make_deps_patch(
+ base_revision, base_revision.deps_file_contents,
+ self.depot, deps_revision)
+ self.commit_hash = base_revision.commit_hash
+ self.commit_pos = base_revision.commit_pos
+ self.deps_sha = hashlib.sha1(self.deps_patch).hexdigest()
+ self.deps_sha_patch = self.bisector.make_deps_sha_file(self.deps_sha)
+ self.deps = dict(base_revision.deps)
+ self.deps[dependency_depot_name] = deps_revision
+ else:
+ self.needs_patch = False
+ self.depot = depot
+ self.commit_hash, self.commit_pos = self._commit_from_rev_string()
self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix()
@property
@@ -92,14 +136,70 @@ class RevisionState(object):
def deps_change(self):
"""Uses `git show` to see if a given commit contains a DEPS change."""
api = self.bisector.api
+ name = 'Checking DEPS for '+self.commit_hash
step_result = api.m.git('show', '--name-only', '--pretty=format:',
- self.commit_hash, stdout=api.m.raw_io.output())
- if self.bisector.dummy_builds:
+ self.commit_hash, stdout=api.m.raw_io.output(), name=name)
+ if self.bisector.dummy_builds and not self.commit_hash.startswith('dcdc'):
return False
if 'DEPS' in step_result.stdout.splitlines(): # pragma: no cover
return True
return False # pragma: no cover
+
+ def _gen_deps_local_scope(self):
+ """Defines the Var and From functions in a dict for calling exec.
+
+ This is needed for executing the DEPS file.
+ """
+ deps_data = {
+ 'Var': lambda _: deps_data['vars'][_],
+ 'From': lambda *args: None,
+ }
+ return deps_data
+
+ def read_deps(self):
+ """Sets the dependencies for this revision from the contents of DEPS."""
+ api = self.bisector.api
+ if self.deps:
+ return
+ step_result = api.m.git.cat_file_at_commit(depot_config.DEPS_FILENAME,
+ self.commit_hash,
+ stdout=api.m.raw_io.output())
+ self.deps_file_contents = step_result.stdout
+ try:
+ deps_data = self._gen_deps_local_scope()
+ exec(self.deps_file_contents or 'deps = {}', {}, deps_data)
+ deps_data = deps_data['deps']
+ except ImportError: # pragma: no cover
+ # TODO(robertocn): Implement manual parsing of DEPS when exec fails.
+ raise NotImplementedError('Path not implemented to manually parse DEPS')
+
+ revision_regex = re.compile('.git@(?P<revision>[a-fA-F0-9]+)')
+ results = {}
+ for depot_name, depot_data in depot_config.DEPOT_DEPS_NAME.iteritems():
+ if (depot_data.get('platform') and
+ depot_data.get('platform') != os.name):
+ # TODO(robertocn) we shouldn't be checking the os of the bot running the
+ # bisector, but the os the tester would be running on.
+ continue
+
+ if depot_data.get('recurse') and self.depot in depot_data.get('from'):
+ depot_data_src = depot_data.get('src') or depot_data.get('src_old')
+ src_dir = deps_data.get(depot_data_src)
+ if src_dir:
+ re_results = revision_regex.search(src_dir)
+ if re_results:
+ results[depot_name] = re_results.group('revision')
+ else:
+ warning_text = ('Could not parse revision for %s while bisecting '
+ '%s' % (depot_name, self.depot))
+ if not warning_text in self.bisector.warnings:
+ self.bisector.warnings.append(warning_text)
+ else:
+ results[depot_name] = None
+ self.deps = results
+ return
+
def update_status(self):
"""Checks on the pending jobs and updates status accordingly.
@@ -137,17 +237,14 @@ class RevisionState(object):
This takes into account whether the build has a deps patch.
"""
- # TODO: Implement the logic for deps patch changes.
- return self.commit_hash + '.zip'
+ name_parts = [self.commit_hash]
+ if self.needs_patch:
+ name_parts.append(self.deps_sha)
+ return '%s.zip' % '_'.join(name_parts)
def _commit_from_rev_string(self):
- """Gets the chromium repo commit hash and position for this revision.
-
- If there are specified dependency revisions in the string, we don't compute
- either the position or hash"""
+ """Gets the chromium repo commit hash and position for this revision."""
pieces = self.revision_string.split(',')
- if len(pieces) > 1:
- return None, None # pragma: no cover
if (pieces[0].startswith('chromium@') or
pieces[0].startswith('src@') or
not '@' in pieces[0]):

Powered by Google App Engine
This is Rietveld 408576698