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

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: Further expansion of example.py for auto_bisect. 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 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
17
12 18
13 class RevisionState(object): 19 class RevisionState(object):
14 """Abstracts the state of a single revision on a bisect job.""" 20 """Abstracts the state of a single revision on a bisect job."""
15 21
16 def __init__(self, revision_string, bisector): 22 def __init__(self, revision_string, bisector, depot='chromium',
23 dependency_depot_name=None,base_revision=None,
24 deps_revision=None):
17 """Create a new instance to track the state of a revision. 25 """Create a new instance to track the state of a revision.
18 26
27 There are two use cases for this constructor:
28 - Creating a revision state for a chromium revision, OR
29 - Creating a revision state for a chromium revision plus an explicitly
30 specified revision of a dependency repository (a DEPS change).
31 In the first case a revision_string and a bisector are needed.
32 In the second case, revision_string must be None, and all of depot,
33 base_revision and deps_revision must be provided.
34
19 Args: 35 Args:
20 revision_string: should be in the following format: 36 revision_string (str): A git hash or a commit position in the chromium
21 [(chromium|src)@](<commit_pos>|<commit_hash)(,<repo>@<commit>)* 37 repository. If None, all kwargs must be given.
22 E.g.: 38 bisector (Bisector): The object performing the bisection.
23 'a0b1c2ffff89009909090' (full or abbrev. commit hash) 39 depot (dict): One of the entries in depot_config.DEPOT_DEPS_NAME that
24 '123456' 40 specifies which dependency to do the DEPS change on. It is expected to
25 'src@123456' 41 contain the 'chromium' string instead of None when not bisecting any
26 'chromium@123456' 42 dependencies.
27 'src@abc01234ffff,v8@00af5ceb888ff' 43 base_revision (RevisionState): The revision state to patch with the deps
28 bisector: an instance of Bisector, the object performing the bisection. 44 change.
45 depot_revision: The commit hash of the dependency repo to put in place of
46 the one set for the base_revision.
29 """ 47 """
48 # TODO(robertocn): Evaluate if the logic of this constructor should be
49 # split into separate methods.
30 super(RevisionState, self).__init__() 50 super(RevisionState, self).__init__()
31 self.bisector = bisector 51 self.bisector = bisector
32 self._good = None 52 self._good = None
53 self.deps = None
33 self.build_status_url = None 54 self.build_status_url = None
34 self.in_progress = False 55 self.in_progress = False
35 self.aborted = False 56 self.aborted = False
36 self.next_revision = None 57 self.next_revision = None
37 self.previous_revision = None 58 self.previous_revision = None
38 self.revision_string = revision_string 59 self.revision_string = revision_string
39 self.commit_hash, self.commit_pos = self._commit_from_rev_string()
40 self.build_job_name = None 60 self.build_job_name = None
41 self.test_job_name = None 61 self.test_job_name = None
42 self.built = False 62 self.built = False
63 if not self.revision_string:
64 assert base_revision
65 assert base_revision.deps_file_contents
66 assert depot != 'chromium'
67 assert deps_revision
68 self.needs_patch = True
69 self.depot = depot
70 self.revision_string = (base_revision.revision_string + ',' +
71 dependency_depot_name)
72 self.revision_string += '@' + deps_revision
73 self.deps_patch, self.deps_file_contents = self.bisector.make_deps_patch(
74 base_revision, base_revision.deps_file_contents,
75 self.depot, deps_revision)
76 self.commit_hash = base_revision.commit_hash
77 self.commit_pos = base_revision.commit_pos
78 self.deps_sha = hashlib.sha1(self.deps_patch).hexdigest()
79 self.deps_patch += self.bisector.make_deps_sha_file(self.deps_sha)
80 self.deps = dict(base_revision.deps)
81 self.deps[dependency_depot_name] = deps_revision
82 else:
83 self.needs_patch = False
84 self.depot = depot
85 self.commit_hash, self.commit_pos = self._commit_from_rev_string()
43 self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix() 86 self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix()
44 87
45 @property 88 @property
46 def good(self): 89 def good(self):
47 return self._good == True 90 return self._good == True
48 91
49 @property 92 @property
50 def bad(self): 93 def bad(self):
51 return self._good == False 94 return self._good == False
52 95
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 parallel. 128 parallel.
86 """ 129 """
87 assert self.in_progress 130 assert self.in_progress
88 self.in_progress = False 131 self.in_progress = False
89 self.aborted = True 132 self.aborted = True
90 # TODO: actually kill buildbot job if it's the test step. 133 # TODO: actually kill buildbot job if it's the test step.
91 134
92 def deps_change(self): 135 def deps_change(self):
93 """Uses `git show` to see if a given commit contains a DEPS change.""" 136 """Uses `git show` to see if a given commit contains a DEPS change."""
94 api = self.bisector.api 137 api = self.bisector.api
138 name = 'Checking DEPS for '+self.commit_hash
95 step_result = api.m.git('show', '--name-only', '--pretty=format:', 139 step_result = api.m.git('show', '--name-only', '--pretty=format:',
96 self.commit_hash, stdout=api.m.raw_io.output()) 140 self.commit_hash, stdout=api.m.raw_io.output(), name=name)
97 if self.bisector.dummy_builds: 141 if self.bisector.dummy_builds and not self.commit_hash.startswith('dcdc'):
98 return False 142 return False
99 if 'DEPS' in step_result.stdout.splitlines(): 143 if 'DEPS' in step_result.stdout.splitlines():
100 return True 144 return True
101 return False 145 return False
102 146
147
148 def _gen_deps_local_scope(self):
149 """Defines the Var and From functions in a dict for calling exec.
150
151 This is needed for executing the DEPS file.
152 """
153 deps_data = {
154 'Var': lambda _: deps_data['vars'][_],
155 'From': lambda *args: None,
156 }
157 return deps_data
158
159 def read_deps(self):
160 """Sets the dependencies for this revision from the contents of DEPS."""
161 api = self.bisector.api
162 if self.deps:
163 return
164 step_result = api.m.git.cat_file_at_commit(depot_config.DEPS_FILENAME,
165 self.commit_hash,
166 stdout=api.m.raw_io.output())
167 self.deps_file_contents = step_result.stdout
168 try:
169 deps_data = self._gen_deps_local_scope()
170 exec(self.deps_file_contents or 'deps = {}', {}, deps_data)
171 deps_data = deps_data['deps']
172 except ImportError:
173 # TODO(robertocn): Implement manual parsing of DEPS when exec fails.
174 raise NotImplementedError('Path not implemented to manually parse DEPS')
175
176 revision_regex = re.compile('.git@(?P<revision>[a-fA-F0-9]+)')
177 results = {}
178 for depot_name, depot_data in depot_config.DEPOT_DEPS_NAME.iteritems():
179 if (depot_data.get('platform') and
180 depot_data.get('platform') != os.name):
181 # TODO(robertocn) we shouldn't be checking the os of the bot running the
182 # bisector, but the os the tester would be running on.
183 continue
184
185 if depot_data.get('recurse') and self.depot in depot_data.get('from'):
186 depot_data_src = depot_data.get('src') or depot_data.get('src_old')
187 src_dir = deps_data.get(depot_data_src)
188 if src_dir:
189 re_results = revision_regex.search(src_dir)
190 if re_results:
191 results[depot_name] = re_results.group('revision')
192 else:
193 warning_text = ('Could not parse revision for %s while bisecting '
194 '%s' % (depot_name, self.depot))
195 if not warning_text in self.bisector.warnings:
196 self.bisector.warnings.append(warning_text)
197 else:
198 results[depot_name] = None
199 self.deps = results
200 return
201
103 def update_status(self): 202 def update_status(self):
104 """Checks on the pending jobs and updates status accordingly. 203 """Checks on the pending jobs and updates status accordingly.
105 204
106 This method will check for the build to complete and then trigger the test, 205 This method will check for the build to complete and then trigger the test,
107 or will wait for the test as appropriate. 206 or will wait for the test as appropriate.
108 207
109 To wait for the test we try to get the buildbot job url from GS, and if 208 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. 209 available, we query the status of such job.
111 """ 210 """
112 if not self.in_progress: 211 if not self.in_progress:
(...skipping 17 matching lines...) Expand all
130 result = api.gsutil_file_exists(self.build_url) 229 result = api.gsutil_file_exists(self.build_url)
131 if self.bisector.dummy_builds: 230 if self.bisector.dummy_builds:
132 return self.in_progress 231 return self.in_progress
133 return result 232 return result
134 233
135 def _gs_suffix(self): 234 def _gs_suffix(self):
136 """Provides the expected right half of the build filename. 235 """Provides the expected right half of the build filename.
137 236
138 This takes into account whether the build has a deps patch. 237 This takes into account whether the build has a deps patch.
139 """ 238 """
140 # TODO: Implement the logic for deps patch changes. 239 name_parts = [self.commit_hash]
141 return self.commit_hash + '.zip' 240 if self.needs_patch:
241 name_parts.append(self.deps_sha)
242 return '%s.zip' % '_'.join(name_parts)
142 243
143 def _commit_from_rev_string(self): 244 def _commit_from_rev_string(self):
144 """Gets the chromium repo commit hash and position for this revision. 245 """Gets the chromium repo commit hash and position for this revision."""
145
146 If there are specified dependency revisions in the string, we don't compute
147 either the position or hash"""
148 pieces = self.revision_string.split(',') 246 pieces = self.revision_string.split(',')
149 if len(pieces) > 1:
150 return None, None
151 if (pieces[0].startswith('chromium@') or 247 if (pieces[0].startswith('chromium@') or
152 pieces[0].startswith('src@') or 248 pieces[0].startswith('src@') or
153 not '@' in pieces[0]): 249 not '@' in pieces[0]):
154 hash_or_pos = pieces[0].split('@')[-1] 250 hash_or_pos = pieces[0].split('@')[-1]
155 if self._check_if_hash(hash_or_pos): 251 if self._check_if_hash(hash_or_pos):
156 commit_pos = self._get_pos_from_hash(hash_or_pos) 252 commit_pos = self._get_pos_from_hash(hash_or_pos)
157 commit_hash = self._get_hash_from_pos(commit_pos) 253 commit_hash = self._get_hash_from_pos(commit_pos)
158 else: 254 else:
159 commit_hash = self._get_hash_from_pos(hash_or_pos) 255 commit_hash = self._get_hash_from_pos(hash_or_pos)
160 commit_pos = self._get_pos_from_hash(commit_hash) 256 commit_pos = self._get_pos_from_hash(commit_hash)
(...skipping 13 matching lines...) Expand all
174 int(s, 16) 270 int(s, 16)
175 return True 271 return True
176 272
177 def _get_pos_from_hash(self, sha): 273 def _get_pos_from_hash(self, sha):
178 api = self.bisector.api 274 api = self.bisector.api
179 return api.m.commit_position.chromium_commit_position_from_hash(sha) 275 return api.m.commit_position.chromium_commit_position_from_hash(sha)
180 276
181 def _get_hash_from_pos(self, pos): 277 def _get_hash_from_pos(self, pos):
182 api = self.bisector.api 278 api = self.bisector.api
183 return api.m.commit_position.chromium_hash_from_commit_position(pos) 279 return api.m.commit_position.chromium_hash_from_commit_position(pos)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698