Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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) |
| OLD | NEW |