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

Side by Side 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: WIP: Early feedback request Created 5 years, 10 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 unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """An interface for holding state and result of revisions in a bisect job. 5 """An interface for holding state and result of revisions in a bisect job.
6 6
7 When implementing support for tests other than perf, one should extend this 7 When implementing support for tests other than perf, one should extend this
8 class so that the bisect module and recipe can use it. 8 class so that the bisect module and recipe can use it.
9 9
10 See perf_revision_state for an example. 10 See perf_revision_state for an example.
11 """ 11 """
12 import hashlib
13 import os
14 import re
15
16 from . import depot_config
12 17
13 class RevisionState(object): 18 class RevisionState(object):
qyearsley 2015/02/22 20:37:55 The other files have two blank lines between impor
RobertoCN 2015/02/24 20:01:14 Done.
14 """Abstracts the state of a single revision on a bisect job.""" 19 """Abstracts the state of a single revision on a bisect job."""
15 20
16 def __init__(self, revision_string, bisector): 21 def __init__(self, revision_string, bisector, depot='chromium',
22 base_revision=None, deps_revision=None):
17 """Create a new instance to track the state of a revision. 23 """Create a new instance to track the state of a revision.
18 24
25 There are two use cases for this constructor:
26 - Creating a revision state for a chromium revision, OR
27 - Creating a revision state for a chromium revision plus a deps change.
qyearsley 2015/02/22 20:37:55 I'm not sure, but for other people without context
RobertoCN 2015/02/24 20:01:14 Done.
28 In the first case a revision_string and a bisector are needed.
29 In the second case, revision_string must be None, and all of depot,
30 base_revision and deps_revision must be provided.
31
19 Args: 32 Args:
20 revision_string: should be in the following format: 33 revision_string (str): A git hash or a commit position in the chromium
21 [(chromium|src)@](<commit_pos>|<commit_hash)(,<repo>@<commit>)* 34 repository. If None, all kwargs must be given.
22 E.g.: 35 bisector (Bisector): The object performing the bisection.
23 'a0b1c2ffff89009909090' (full or abbrev. commit hash) 36 depot (dict): One of the entries in depot_config.DEPOT_DEPS_NAME that
24 '123456' 37 specifies which dependency to do the DEPS change on. It is expected to
25 'src@123456' 38 contain the 'chromium' string instead of None when not bisecting any
26 'chromium@123456' 39 dependencies.
27 'src@abc01234ffff,v8@00af5ceb888ff' 40 base_revision (RevisionState): The revision state to patch with the deps
28 bisector: an instance of Bisector, the object performing the bisection. 41 change.
42 depot_revision: The commit hash of the dependency repo to put in place of
43 the one set for the base_revision.
29 """ 44 """
45 # TODO(robertocn): Evaluate if the logic of this constructor should be
46 # split into separate methods.
30 super(RevisionState, self).__init__() 47 super(RevisionState, self).__init__()
31 self.bisector = bisector 48 self.bisector = bisector
32 self._good = None 49 self._good = None
50 self.deps = None
33 self.build_status_url = None 51 self.build_status_url = None
34 self.in_progress = False 52 self.in_progress = False
35 self.aborted = False 53 self.aborted = False
36 self.next_revision = None 54 self.next_revision = None
37 self.previous_revision = None 55 self.previous_revision = None
38 self.revision_string = revision_string 56 self.revision_string = revision_string
39 self.commit_hash, self.commit_pos = self._commit_from_rev_string()
40 self.build_job_name = None 57 self.build_job_name = None
41 self.test_job_name = None 58 self.test_job_name = None
42 self.built = False 59 self.built = False
60 if not self.revision_string:
61 assert base_revision
62 assert base_revision.deps_file_contents
63 assert depot != 'chromium'
64 assert deps_revision
65 self.needs_patch = True
66 self.depot = depot['deps_var']
67 self.revision_string = base_revision.revision_string + ',' + self.depot
68 self.revision_string += '@' + deps_revision
69 self.deps_patch, self.deps_file_contents = self.bisector.make_deps_patch(
70 base_revision.commit_hash, base_revision.deps_file_contents,
71 self.depot, deps_revision)
72 self.deps_sha = hashlib.sha1(self.deps_patch).hexdigest()
73 self.deps_patch += self.bisector.make_deps_sha_file(self.deps_sha)
74 self.deps = dict(base_revision.deps)
75 self.deps[self.depot] = deps_revision
76 else:
77 self.needs_patch = False
78 self.depot = depot
79 self.commit_hash, self.commit_pos = self._commit_from_rev_string()
43 self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix() 80 self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix()
44 81
45 @property 82 @property
46 def good(self): 83 def good(self):
47 return self._good == True 84 return self._good == True
48 85
49 @property 86 @property
50 def bad(self): 87 def bad(self):
51 return self._good == False 88 return self._good == False
52 89
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 """Uses `git show` to see if a given commit contains a DEPS change.""" 130 """Uses `git show` to see if a given commit contains a DEPS change."""
94 api = self.bisector.api 131 api = self.bisector.api
95 step_result = api.m.git('show', '--name-only', '--pretty=format:', 132 step_result = api.m.git('show', '--name-only', '--pretty=format:',
96 self.commit_hash, stdout=api.m.raw_io.output()) 133 self.commit_hash, stdout=api.m.raw_io.output())
97 if self.bisector.dummy_builds: 134 if self.bisector.dummy_builds:
98 return False 135 return False
99 if 'DEPS' in step_result.stdout.splitlines(): 136 if 'DEPS' in step_result.stdout.splitlines():
100 return True 137 return True
101 return False 138 return False
102 139
140
141 def _gen_deps_local_scope(self):
142 """Defines the Var and From functions in a dict for calling exec.
143
144 This is needed for executing the DEPS file.
145 """
146 deps_data = {
147 'Var': lambda _: deps_data["vars"][_],
qyearsley 2015/02/22 20:37:55 Single quotes.
RobertoCN 2015/02/24 20:01:14 Done.
148 'From': lambda *args: None,
149 }
150 return deps_data
151
152 def read_deps(self):
153 """Sets the dependencies for this revision from the contents of DEPS."""
154 if self.deps:
155 return
156 deps_contents = self.m.git.cat_file_at_revision(depot_config.FILE_DEPS)
157 try:
158 deps_data = self._gen_deps_local_scope()
159 exec(deps_contents, {}, deps_data)
160 deps_data = deps_data['deps']
161 except ImportError:
162 # TODO(robertocn): Implement manual parsing of DEPS when exec fails.
163 raise NotImplementedError('Path not implemented to manually parse DEPS')
164
165 rxp = re.compile(".git@(?P<revision>[a-fA-F0-9]+)")
qyearsley 2015/02/22 20:37:55 1. Variable name could probably be improved. 2. No
RobertoCN 2015/02/24 20:01:14 Done.
166 results = {}
167 for depot_name, depot_data in depot_config.DEPOT_DEPS_NAME.iteritems():
168 if (depot_data.get('platform') and
169 depot_data.get('platform') != os.name):
170 # TODO(robertocn) we shouldn't be checking the os of the bot running the
171 # bisector, but the os the tester would be running on.
172 continue
173
174 if depot_data.get('recurse') and self.depot in depot_data.get('from'):
175 depot_data_src = depot_data.get('src') or depot_data.get('src_old')
176 src_dir = deps_data.get(depot_data_src)
177 if src_dir:
178 re_results = rxp.search(src_dir)
179 if re_results:
180 results[depot_name] = re_results.group('revision')
181 else:
182 warning_text = ('Could not parse revision for %s while bisecting '
183 '%s' % (depot_name, self.depot))
184 if not warning_text in self.bisector.warnings:
185 self.bisector.warnings.append(warning_text)
186 else:
187 results[depot_name] = None
188 self.deps = results
189 return
190
103 def update_status(self): 191 def update_status(self):
104 """Checks on the pending jobs and updates status accordingly. 192 """Checks on the pending jobs and updates status accordingly.
105 193
106 This method will check for the build to complete and then trigger the test, 194 This method will check for the build to complete and then trigger the test,
107 or will wait for the test as appropriate. 195 or will wait for the test as appropriate.
108 196
109 To wait for the test we try to get the buildbot job url from GS, and if 197 To wait for the test we try to get the buildbot job url from GS, and if
110 available, we query the status of such job. 198 available, we query the status of such job.
111 """ 199 """
112 if not self.in_progress: 200 if not self.in_progress:
(...skipping 17 matching lines...) Expand all
130 result = api.gsutil_file_exists(self.build_url) 218 result = api.gsutil_file_exists(self.build_url)
131 if self.bisector.dummy_builds: 219 if self.bisector.dummy_builds:
132 return self.in_progress 220 return self.in_progress
133 return result 221 return result
134 222
135 def _gs_suffix(self): 223 def _gs_suffix(self):
136 """Provides the expected right half of the build filename. 224 """Provides the expected right half of the build filename.
137 225
138 This takes into account whether the build has a deps patch. 226 This takes into account whether the build has a deps patch.
139 """ 227 """
140 # TODO: Implement the logic for deps patch changes. 228 name_parts = [self.commit_hash]
141 return self.commit_hash + '.zip' 229 if self.needs_patch:
230 name_parts.append(self.deps_sha)
231 return '%s.zip' % '_'.join(name_parts)
142 232
143 def _commit_from_rev_string(self): 233 def _commit_from_rev_string(self):
144 """Gets the chromium repo commit hash and position for this revision. 234 """Gets the chromium repo commit hash and position for this revision.
145 235
146 If there are specified dependency revisions in the string, we don't compute 236 If there are specified dependency revisions in the string, we don't compute
147 either the position or hash""" 237 either the position or hash"""
148 pieces = self.revision_string.split(',') 238 pieces = self.revision_string.split(',')
149 if len(pieces) > 1: 239 if len(pieces) > 1:
150 return None, None 240 return None, None
151 if (pieces[0].startswith('chromium@') or 241 if (pieces[0].startswith('chromium@') or
(...skipping 22 matching lines...) Expand all
174 int(s, 16) 264 int(s, 16)
175 return True 265 return True
176 266
177 def _get_pos_from_hash(self, sha): 267 def _get_pos_from_hash(self, sha):
178 api = self.bisector.api 268 api = self.bisector.api
179 return api.m.commit_position.chromium_commit_position_from_hash(sha) 269 return api.m.commit_position.chromium_commit_position_from_hash(sha)
180 270
181 def _get_hash_from_pos(self, pos): 271 def _get_hash_from_pos(self, pos):
182 api = self.bisector.api 272 api = self.bisector.api
183 return api.m.commit_position.chromium_hash_from_commit_position(pos) 273 return api.m.commit_position.chromium_hash_from_commit_position(pos)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698