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

Side by Side Diff: tools/bisect-builds.py

Issue 851333004: Add support to bisect nonofficial Android builds from tip of tree bot. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Snapshot Build Bisect Tool 6 """Snapshot Build Bisect Tool
7 7
8 This script bisects a snapshot archive using binary search. It starts at 8 This script bisects a snapshot archive using binary search. It starts at
9 a bad revision (it will try to guess HEAD) and asks for a last known-good 9 a bad revision (it will try to guess HEAD) and asks for a last known-good
10 revision. It will then binary search across this revision range by downloading, 10 revision. It will then binary search across this revision range by downloading,
(...skipping 14 matching lines...) Expand all
25 25
26 # Base URL for downloading official builds. 26 # Base URL for downloading official builds.
27 GOOGLE_APIS_URL = 'commondatastorage.googleapis.com' 27 GOOGLE_APIS_URL = 'commondatastorage.googleapis.com'
28 28
29 # The base URL for official builds. 29 # The base URL for official builds.
30 OFFICIAL_BASE_URL = 'http://%s/%s' % (GOOGLE_APIS_URL, GS_BUCKET_NAME) 30 OFFICIAL_BASE_URL = 'http://%s/%s' % (GOOGLE_APIS_URL, GS_BUCKET_NAME)
31 31
32 # URL template for viewing changelogs between revisions. 32 # URL template for viewing changelogs between revisions.
33 CHANGELOG_URL = ('https://chromium.googlesource.com/chromium/src/+log/%s..%s') 33 CHANGELOG_URL = ('https://chromium.googlesource.com/chromium/src/+log/%s..%s')
34 34
35 # GS bucket name for tip of tree android builds.
36 ANDROID_TOT_BUCKET_NAME = ('chrome-android-tot/bisect')
37
35 # GS bucket name for android unsigned official builds. 38 # GS bucket name for android unsigned official builds.
36 ANDROID_BUCKET_NAME = 'chrome-unsigned/android-C4MPAR1' 39 ANDROID_BUCKET_NAME = 'chrome-unsigned/android-C4MPAR1'
37 40
38 # The base URL for android official builds. 41 # The base URL for android official builds.
39 ANDROID_OFFICIAL_BASE_URL = 'http://%s/%s' % (GOOGLE_APIS_URL, ANDROID_BUCKET_NA ME) 42 ANDROID_OFFICIAL_BASE_URL = 'http://%s/%s' % (GOOGLE_APIS_URL, ANDROID_BUCKET_NA ME)
40 43
41 # URL to convert SVN revision to git hash. 44 # URL to convert SVN revision to git hash.
42 CRREV_URL = ('https://cr-rev.appspot.com/_ah/api/crrev/v1/redirect/') 45 CRREV_URL = ('https://cr-rev.appspot.com/_ah/api/crrev/v1/redirect/')
43 46
44 # URL template for viewing changelogs between official versions. 47 # URL template for viewing changelogs between official versions.
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 self.is_official = is_official 137 self.is_official = is_official
135 self.is_asan = is_asan 138 self.is_asan = is_asan
136 self.build_type = 'release' 139 self.build_type = 'release'
137 self.flash_path = flash_path 140 self.flash_path = flash_path
138 # Dictionary which stores svn revision number as key and it's 141 # Dictionary which stores svn revision number as key and it's
139 # corresponding git hash as value. This data is populated in 142 # corresponding git hash as value. This data is populated in
140 # _FetchAndParse and used later in GetDownloadURL while downloading 143 # _FetchAndParse and used later in GetDownloadURL while downloading
141 # the build. 144 # the build.
142 self.githash_svn_dict = {} 145 self.githash_svn_dict = {}
143 self.pdf_path = pdf_path 146 self.pdf_path = pdf_path
144 # android_apk defaults to Chrome.apk
145 self.android_apk = android_apk if android_apk else 'Chrome.apk'
146 # The name of the ZIP file in a revision directory on the server. 147 # The name of the ZIP file in a revision directory on the server.
147 self.archive_name = None 148 self.archive_name = None
148 149 # Whether the build should be downloaded using gsutil.
150 self.download_with_gsutil = False
149 # If the script is run from a local Chromium checkout, 151 # If the script is run from a local Chromium checkout,
150 # "--use-local-repo" option can be used to make the script run faster. 152 # "--use-local-repo" option can be used to make the script run faster.
151 # It uses "git svn find-rev <SHA1>" command to convert git hash to svn 153 # It uses "git svn find-rev <SHA1>" command to convert git hash to svn
152 # revision number. 154 # revision number.
153 self.use_local_repo = use_local_repo 155 self.use_local_repo = use_local_repo
154 156
155 # If the script is being used for android builds. 157 # If the script is being used for android builds.
156 self.android = self.platform.startswith('android') 158 self.is_android = self.platform.startswith('android')
159 # android_apk defaults to Chrome.apk
160 if self.is_android:
161 self.android_apk = android_apk if android_apk else 'Chrome.apk'
157 162
158 # Set some internal members: 163 # Set some internal members:
159 # _listing_platform_dir = Directory that holds revisions. Ends with a '/'. 164 # _listing_platform_dir = Directory that holds revisions. Ends with a '/'.
160 # _archive_extract_dir = Uncompressed directory in the archive_name file. 165 # _archive_extract_dir = Uncompressed directory in the archive_name file.
161 # _binary_name = The name of the executable to run. 166 # _binary_name = The name of the executable to run.
162 if self.platform in ('linux', 'linux64', 'linux-arm'): 167 if self.platform in ('linux', 'linux64', 'linux-arm'):
163 self._binary_name = 'chrome' 168 self._binary_name = 'chrome'
164 elif self.platform in ('mac', 'mac64'): 169 elif self.platform in ('mac', 'mac64'):
165 self.archive_name = 'chrome-mac.zip' 170 self.archive_name = 'chrome-mac.zip'
166 self._archive_extract_dir = 'chrome-mac' 171 self._archive_extract_dir = 'chrome-mac'
167 elif self.platform in ('win', 'win64'): 172 elif self.platform in ('win', 'win64'):
168 self.archive_name = 'chrome-win32.zip' 173 self.archive_name = 'chrome-win32.zip'
169 self._archive_extract_dir = 'chrome-win32' 174 self._archive_extract_dir = 'chrome-win32'
170 self._binary_name = 'chrome.exe' 175 self._binary_name = 'chrome.exe'
171 elif self.android: 176 elif self.is_android:
172 pass 177 pass
173 else: 178 else:
174 raise Exception('Invalid platform: %s' % self.platform) 179 raise Exception('Invalid platform: %s' % self.platform)
175 180
176 if is_official: 181 if is_official:
177 if self.platform == 'linux': 182 if self.platform == 'linux':
178 self._listing_platform_dir = 'precise32/' 183 self._listing_platform_dir = 'precise32/'
179 self.archive_name = 'chrome-precise32.zip' 184 self.archive_name = 'chrome-precise32.zip'
180 self._archive_extract_dir = 'chrome-precise32' 185 self._archive_extract_dir = 'chrome-precise32'
181 elif self.platform == 'linux64': 186 elif self.platform == 'linux64':
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 self._listing_platform_dir = 'Linux/' 221 self._listing_platform_dir = 'Linux/'
217 elif self.platform == 'linux64': 222 elif self.platform == 'linux64':
218 self._listing_platform_dir = 'Linux_x64/' 223 self._listing_platform_dir = 'Linux_x64/'
219 elif self.platform == 'linux-arm': 224 elif self.platform == 'linux-arm':
220 self._listing_platform_dir = 'Linux_ARM_Cross-Compile/' 225 self._listing_platform_dir = 'Linux_ARM_Cross-Compile/'
221 elif self.platform == 'mac': 226 elif self.platform == 'mac':
222 self._listing_platform_dir = 'Mac/' 227 self._listing_platform_dir = 'Mac/'
223 self._binary_name = 'Chromium.app/Contents/MacOS/Chromium' 228 self._binary_name = 'Chromium.app/Contents/MacOS/Chromium'
224 elif self.platform == 'win': 229 elif self.platform == 'win':
225 self._listing_platform_dir = 'Win/' 230 self._listing_platform_dir = 'Win/'
231 elif self.platform == 'android-arm':
232 self.archive_name = 'bisect_android.zip'
233 # Need to download builds using gsutil instead of visiting url for
234 # authentication reasons.
235 self.download_with_gsutil = True
226 236
227 def GetASANPlatformDir(self): 237 def GetASANPlatformDir(self):
228 """ASAN builds are in directories like "linux-release", or have filenames 238 """ASAN builds are in directories like "linux-release", or have filenames
229 like "asan-win32-release-277079.zip". This aligns to our platform names 239 like "asan-win32-release-277079.zip". This aligns to our platform names
230 except in the case of Windows where they use "win32" instead of "win".""" 240 except in the case of Windows where they use "win32" instead of "win"."""
231 if self.platform == 'win': 241 if self.platform == 'win':
232 return 'win32' 242 return 'win32'
233 else: 243 else:
234 return self.platform 244 return self.platform
235 245
236 def GetListingURL(self, marker=None): 246 def GetListingURL(self, marker=None):
237 """Returns the URL for a directory listing, with an optional marker.""" 247 """Returns the URL for a directory listing, with an optional marker."""
238 marker_param = '' 248 marker_param = ''
239 if marker: 249 if marker:
240 marker_param = '&marker=' + str(marker) 250 marker_param = '&marker=' + str(marker)
241 if self.is_asan: 251 if self.is_asan:
242 prefix = '%s-%s' % (self.GetASANPlatformDir(), self.build_type) 252 prefix = '%s-%s' % (self.GetASANPlatformDir(), self.build_type)
243 return self.base_url + '/?delimiter=&prefix=' + prefix + marker_param 253 return self.base_url + '/?delimiter=&prefix=' + prefix + marker_param
244 else: 254 else:
245 return (self.base_url + '/?delimiter=/&prefix=' + 255 return (self.base_url + '/?delimiter=/&prefix=' +
246 self._listing_platform_dir + marker_param) 256 self._listing_platform_dir + marker_param)
247 257
248 def GetDownloadURL(self, revision): 258 def GetDownloadURL(self, revision):
249 """Gets the download URL for a build archive of a specific revision.""" 259 """Gets the download URL for a build archive of a specific revision."""
250 if self.is_asan: 260 if self.is_asan:
251 return '%s/%s-%s/%s-%d.zip' % ( 261 return '%s/%s-%s/%s-%d.zip' % (
252 ASAN_BASE_URL, self.GetASANPlatformDir(), self.build_type, 262 ASAN_BASE_URL, self.GetASANPlatformDir(), self.build_type,
253 self.GetASANBaseName(), revision) 263 self.GetASANBaseName(), revision)
254 if self.is_official: 264 if self.is_official:
255 if self.android: 265 if self.is_android:
256 official_base_url = ANDROID_OFFICIAL_BASE_URL 266 official_base_url = ANDROID_OFFICIAL_BASE_URL
257 else: 267 else:
258 official_base_url = OFFICIAL_BASE_URL 268 official_base_url = OFFICIAL_BASE_URL
259 return '%s/%s/%s%s' % ( 269 return '%s/%s/%s%s' % (
260 official_base_url, revision, self._listing_platform_dir, 270 official_base_url, revision, self._listing_platform_dir,
261 self.archive_name) 271 self.archive_name)
262 else: 272 else:
263 if str(revision) in self.githash_svn_dict: 273 if self.is_android:
264 revision = self.githash_svn_dict[str(revision)] 274 # These files need to be downloaded through gsutil.
265 return '%s/%s%s/%s' % (self.base_url, self._listing_platform_dir, 275 return ('gs://%s/%s/%s' % (ANDROID_TOT_BUCKET_NAME, revision,
266 revision, self.archive_name) 276 self.archive_name))
277 else:
278 if str(revision) in self.githash_svn_dict:
279 revision = self.githash_svn_dict[str(revision)]
280 return '%s/%s%s/%s' % (self.base_url, self._listing_platform_dir,
281 revision, self.archive_name)
267 282
268 def GetLastChangeURL(self): 283 def GetLastChangeURL(self):
269 """Returns a URL to the LAST_CHANGE file.""" 284 """Returns a URL to the LAST_CHANGE file."""
270 return self.base_url + '/' + self._listing_platform_dir + 'LAST_CHANGE' 285 return self.base_url + '/' + self._listing_platform_dir + 'LAST_CHANGE'
271 286
272 def GetASANBaseName(self): 287 def GetASANBaseName(self):
273 """Returns the base name of the ASAN zip file.""" 288 """Returns the base name of the ASAN zip file."""
274 if 'linux' in self.platform: 289 if 'linux' in self.platform:
275 return 'asan-symbolized-%s-%s' % (self.GetASANPlatformDir(), 290 return 'asan-symbolized-%s-%s' % (self.GetASANPlatformDir(),
276 self.build_type) 291 self.build_type)
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
450 self.good_revision = FixChromiumRevForBlink(revlist, 465 self.good_revision = FixChromiumRevForBlink(revlist,
451 revlist_all, 466 revlist_all,
452 self, 467 self,
453 self.good_revision) 468 self.good_revision)
454 self.bad_revision = FixChromiumRevForBlink(revlist, 469 self.bad_revision = FixChromiumRevForBlink(revlist,
455 revlist_all, 470 revlist_all,
456 self, 471 self,
457 self.bad_revision) 472 self.bad_revision)
458 return revlist 473 return revlist
459 474
475 def _GetHashToNumberDict(self):
476 """Gets the mapping of git hashes to git numbers from Google Storage."""
477 gs_file = 'gs://%s/gitnumbers_dict.json' % ANDROID_TOT_BUCKET_NAME
478 local_file = 'gitnumbers_dict.json'
479 GsutilDownload(gs_file, local_file)
480 json_data = open(local_file).read()
481 os.remove(local_file)
482 return json.loads(json_data)
483
484 def GetAndroidToTRevisions(self):
485 """Gets the ordered list of revisions between self.good_revision and
486 self.bad_revision from the Android tip of tree GS bucket.
487 """
488 # Dictionary that maps git hashes to git numbers. The git numbers
489 # let us order the revisions.
490 hash_to_num = self._GetHashToNumberDict()
491 try:
492 good_rev_num = hash_to_num[self.good_revision]
493 bad_rev_num = hash_to_num[self.bad_revision]
494 except KeyError:
495 exit('Error. Make sure the good and bad revisions are valid git hashes.')
496
497 # List of all builds by their git hashes in the storage bucket.
498 hash_list = GsutilList(ANDROID_TOT_BUCKET_NAME)
499
500 # Get list of builds that we want to bisect over.
501 final_list = []
502 minnum = min(good_rev_num, bad_rev_num)
503 maxnum = max(good_rev_num, bad_rev_num)
504 for githash in hash_list:
505 if len(githash) != 40:
506 continue
507 gitnumber = hash_to_num[githash]
508 if minnum < gitnumber < maxnum:
509 final_list.append(githash)
510 return sorted(final_list, key=lambda h: hash_to_num[h])
511
460 def GetOfficialBuildsList(self): 512 def GetOfficialBuildsList(self):
461 """Gets the list of official build numbers between self.good_revision and 513 """Gets the list of official build numbers between self.good_revision and
462 self.bad_revision.""" 514 self.bad_revision."""
463 515
464 def CheckDepotToolsInPath():
465 delimiter = ';' if sys.platform.startswith('win') else ':'
466 path_list = os.environ['PATH'].split(delimiter)
467 for path in path_list:
468 if path.rstrip(os.path.sep).endswith('depot_tools'):
469 return path
470 return None
471
472 def RunGsutilCommand(args):
473 gsutil_path = CheckDepotToolsInPath()
474 if gsutil_path is None:
475 print ('Follow the instructions in this document '
476 'http://dev.chromium.org/developers/how-tos/install-depot-tools'
477 ' to install depot_tools and then try again.')
478 sys.exit(1)
479 gsutil_path = os.path.join(gsutil_path, 'third_party', 'gsutil', 'gsutil')
480 gsutil = subprocess.Popen([sys.executable, gsutil_path] + args,
481 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
482 env=None)
483 stdout, stderr = gsutil.communicate()
484 if gsutil.returncode:
485 if (re.findall(r'status[ |=]40[1|3]', stderr) or
486 stderr.startswith(CREDENTIAL_ERROR_MESSAGE)):
487 print ('Follow these steps to configure your credentials and try'
488 ' running the bisect-builds.py again.:\n'
489 ' 1. Run "python %s config" and follow its instructions.\n'
490 ' 2. If you have a @google.com account, use that account.\n'
491 ' 3. For the project-id, just enter 0.' % gsutil_path)
492 sys.exit(1)
493 else:
494 raise Exception('Error running the gsutil command: %s' % stderr)
495 return stdout
496
497 def GsutilList(bucket):
498 query = 'gs://%s/' % bucket
499 stdout = RunGsutilCommand(['ls', query])
500 return [url[len(query):].strip('/') for url in stdout.splitlines()]
501
502 # Download the revlist and filter for just the range between good and bad. 516 # Download the revlist and filter for just the range between good and bad.
503 minrev = min(self.good_revision, self.bad_revision) 517 minrev = min(self.good_revision, self.bad_revision)
504 maxrev = max(self.good_revision, self.bad_revision) 518 maxrev = max(self.good_revision, self.bad_revision)
505 if self.android: 519 if self.is_android:
506 gs_bucket_name = ANDROID_BUCKET_NAME 520 gs_bucket_name = ANDROID_BUCKET_NAME
507 else: 521 else:
508 gs_bucket_name = GS_BUCKET_NAME 522 gs_bucket_name = GS_BUCKET_NAME
509 build_numbers = GsutilList(gs_bucket_name) 523 build_numbers = GsutilList(gs_bucket_name)
510 revision_re = re.compile(r'(\d\d\.\d\.\d{4}\.\d+)') 524 revision_re = re.compile(r'(\d\d\.\d\.\d{4}\.\d+)')
511 build_numbers = filter(lambda b: revision_re.search(b), build_numbers) 525 build_numbers = filter(lambda b: revision_re.search(b), build_numbers)
512 final_list = [] 526 final_list = []
513 parsed_build_numbers = [LooseVersion(x) for x in build_numbers] 527 parsed_build_numbers = [LooseVersion(x) for x in build_numbers]
514 connection = httplib.HTTPConnection(GOOGLE_APIS_URL) 528 connection = httplib.HTTPConnection(GOOGLE_APIS_URL)
515 for build_number in sorted(parsed_build_numbers): 529 for build_number in sorted(parsed_build_numbers):
516 if build_number > maxrev: 530 if build_number > maxrev:
517 break 531 break
518 if build_number < minrev: 532 if build_number < minrev:
519 continue 533 continue
520 path = ('/' + gs_bucket_name + '/' + str(build_number) + '/' + 534 path = ('/' + gs_bucket_name + '/' + str(build_number) + '/' +
521 self._listing_platform_dir + self.archive_name) 535 self._listing_platform_dir + self.archive_name)
522 connection.request('HEAD', path) 536 connection.request('HEAD', path)
523 response = connection.getresponse() 537 response = connection.getresponse()
524 if response.status == 200: 538 if response.status == 200:
525 final_list.append(str(build_number)) 539 final_list.append(str(build_number))
526 response.read() 540 response.read()
527 connection.close() 541 connection.close()
528 return final_list 542 return final_list
529 543
Robert Sesek 2015/02/04 17:27:54 nit: add another blank line
mikecase (-- gone --) 2015/02/04 22:50:44 Done.
544 def CheckDepotToolsInPath():
545 delimiter = ';' if sys.platform.startswith('win') else ':'
Robert Sesek 2015/02/04 17:27:54 nit: indent is wrong
mikecase (-- gone --) 2015/02/04 22:50:44 Good eye.
546 path_list = os.environ['PATH'].split(delimiter)
547 for path in path_list:
548 if path.rstrip(os.path.sep).endswith('depot_tools'):
549 return path
550 return None
551
552
553 def RunGsutilCommand(args):
554 gsutil_path = CheckDepotToolsInPath()
555 if gsutil_path is None:
556 print ('Follow the instructions in this document '
557 'http://dev.chromium.org/developers/how-tos/install-depot-tools'
558 ' to install depot_tools and then try again.')
559 sys.exit(1)
560 gsutil_path = os.path.join(gsutil_path, 'third_party', 'gsutil', 'gsutil')
561 gsutil = subprocess.Popen([sys.executable, gsutil_path] + args,
562 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
563 env=None)
564 stdout, stderr = gsutil.communicate()
565 if gsutil.returncode:
566 if (re.findall(r'status[ |=]40[1|3]', stderr) or
567 stderr.startswith(CREDENTIAL_ERROR_MESSAGE)):
568 print ('Follow these steps to configure your credentials and try'
569 ' running the bisect-builds.py again.:\n'
570 ' 1. Run "python %s config" and follow its instructions.\n'
571 ' 2. If you have a @google.com account, use that account.\n'
572 ' 3. For the project-id, just enter 0.' % gsutil_path)
573 sys.exit(1)
574 else:
575 raise Exception('Error running the gsutil command: %s' % stderr)
576 return stdout
577
578
579 def GsutilList(bucket):
580 query = 'gs://%s/' % bucket
581 stdout = RunGsutilCommand(['ls', query])
582 return [url[len(query):].strip('/') for url in stdout.splitlines()]
583
584
585 def GsutilDownload(gs_download_url, filename):
586 RunGsutilCommand(['cp', gs_download_url, filename])
587
588
530 def UnzipFilenameToDir(filename, directory): 589 def UnzipFilenameToDir(filename, directory):
531 """Unzip |filename| to |directory|.""" 590 """Unzip |filename| to |directory|."""
532 cwd = os.getcwd() 591 cwd = os.getcwd()
533 if not os.path.isabs(filename): 592 if not os.path.isabs(filename):
534 filename = os.path.join(cwd, filename) 593 filename = os.path.join(cwd, filename)
535 zf = zipfile.ZipFile(filename) 594 zf = zipfile.ZipFile(filename)
536 # Make base. 595 # Make base.
537 if not os.path.isdir(directory): 596 if not os.path.isdir(directory):
538 os.mkdir(directory) 597 os.mkdir(directory)
539 os.chdir(directory) 598 os.chdir(directory)
540 # Extract files. 599 # Extract files.
541 for info in zf.infolist(): 600 for info in zf.infolist():
542 name = info.filename 601 name = info.filename
543 if name.endswith('/'): # dir 602 if name.endswith('/'): # dir
544 if not os.path.isdir(name): 603 if not os.path.isdir(name):
545 os.makedirs(name) 604 os.makedirs(name)
546 else: # file 605 else: # file
547 directory = os.path.dirname(name) 606 directory = os.path.dirname(name)
548 if not os.path.isdir(directory): 607 if directory and not os.path.isdir(directory):
549 os.makedirs(directory) 608 os.makedirs(directory)
550 out = open(name, 'wb') 609 out = open(name, 'wb')
551 out.write(zf.read(name)) 610 out.write(zf.read(name))
552 out.close() 611 out.close()
553 # Set permissions. Permission info in external_attr is shifted 16 bits. 612 # Set permissions. Permission info in external_attr is shifted 16 bits.
554 os.chmod(name, info.external_attr >> 16L) 613 os.chmod(name, info.external_attr >> 16L)
555 os.chdir(cwd) 614 os.chdir(cwd)
556 615
557 616
558 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None): 617 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None):
(...skipping 14 matching lines...) Expand all
573 size = blocknum * blocksize 632 size = blocknum * blocksize
574 if totalsize == -1: # Total size not known. 633 if totalsize == -1: # Total size not known.
575 progress = 'Received %d bytes' % size 634 progress = 'Received %d bytes' % size
576 else: 635 else:
577 size = min(totalsize, size) 636 size = min(totalsize, size)
578 progress = 'Received %d of %d bytes, %.2f%%' % ( 637 progress = 'Received %d of %d bytes, %.2f%%' % (
579 size, totalsize, 100.0 * size / totalsize) 638 size, totalsize, 100.0 * size / totalsize)
580 # Send a \r to let all progress messages use just one line of output. 639 # Send a \r to let all progress messages use just one line of output.
581 sys.stdout.write('\r' + progress) 640 sys.stdout.write('\r' + progress)
582 sys.stdout.flush() 641 sys.stdout.flush()
583
584 download_url = context.GetDownloadURL(rev) 642 download_url = context.GetDownloadURL(rev)
585 try: 643 try:
586 urllib.urlretrieve(download_url, filename, ReportHook) 644 if context.download_with_gsutil:
645 GsutilDownload(download_url, filename)
646 else:
647 urllib.urlretrieve(download_url, filename, ReportHook)
587 if progress_event and progress_event.isSet(): 648 if progress_event and progress_event.isSet():
588 print 649 print
650
589 except RuntimeError: 651 except RuntimeError:
590 pass 652 pass
591 653
592 654
655 def RunADBCommand(args):
656 cmd = ['adb'] + args
657 adb = subprocess.Popen(['adb'] + args,
658 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
659 env=None)
660 stdout, stderr = adb.communicate()
661 return stdout
662
663
593 def IsADBInstalled(): 664 def IsADBInstalled():
594 """Checks if ADB is in the environment path.""" 665 """Checks if ADB is in the environment path."""
595 try: 666 try:
596 adb_output = subprocess.check_output(['adb', 'version']) 667 adb_output = RunADBCommand(['version'])
597 return ('Android Debug Bridge' in adb_output) 668 return ('Android Debug Bridge' in adb_output)
598 except OSError: 669 except OSError:
599 return False 670 return False
600 671
601 672
602 def GetAndroidDeviceList(): 673 def GetAndroidDeviceList():
603 """Returns the list of Android devices attached to the host machine.""" 674 """Returns the list of Android devices attached to the host machine."""
604 lines = subprocess.check_output(['adb', 'devices']).split('\n')[1:] 675 lines = RunADBCommand(['devices']).split('\n')[1:]
605 devices = [] 676 devices = []
606 for line in lines: 677 for line in lines:
607 m = re.match('^(.*?)\s+device$', line) 678 m = re.match('^(.*?)\s+device$', line)
608 if not m: 679 if not m:
609 continue 680 continue
610 devices.append(m.group(1)) 681 devices.append(m.group(1))
611 return devices 682 return devices
612 683
613 684
614 def RunAndroidRevision(context, revision, apk_file): 685 def RunAndroidRevision(context, revision, zip_file):
615 """Given a Chrome apk, install it on a local device, and launch Chrome.""" 686 """Given a Chrome apk, install it on a local device, and launch Chrome."""
616 devices = GetAndroidDeviceList() 687 devices = GetAndroidDeviceList()
617 if len(devices) is not 1: 688 if len(devices) is not 1:
618 sys.exit('Please have 1 Android device plugged in. %d devices found' 689 sys.exit('Please have 1 Android device plugged in. %d devices found'
619 % len(devices)) 690 % len(devices))
620 691
621 devnull = open(os.devnull, 'w') 692 if context.is_official:
693 # Downloaded file is just the .apk in this case.
694 apk_file = zip_file
695 else:
696 cwd = os.getcwd()
697 tempdir = tempfile.mkdtemp(prefix='bisect_tmp')
698 UnzipFilenameToDir(zip_file, tempdir)
699 os.chdir(tempdir)
700 apk_file = context.android_apk
701
622 package_name = ANDROID_CHROME_PACKAGE_NAME[context.android_apk] 702 package_name = ANDROID_CHROME_PACKAGE_NAME[context.android_apk]
623 703 print 'Installing...'
624 print 'Installing new Chrome version...' 704 RunADBCommand(['install', '-r', '-d', apk_file])
625 subprocess.call(['adb', 'install', '-r', '-d', apk_file],
626 stdout=devnull, stderr=devnull)
627 705
628 print 'Launching Chrome...\n' 706 print 'Launching Chrome...\n'
629 subprocess.call(['adb', 'shell', 'am', 'start', '-a', 707 RunADBCommand(['shell', 'am', 'start', '-a',
630 'android.intent.action.VIEW', '-n', package_name + 708 'android.intent.action.VIEW', '-n', package_name +
631 '/com.google.android.apps.chrome.Main'], stdout=devnull, stderr=devnull) 709 '/com.google.android.apps.chrome.Main'])
632 710
633 711
634 def RunRevision(context, revision, zip_file, profile, num_runs, command, args): 712 def RunRevision(context, revision, zip_file, profile, num_runs, command, args):
635 """Given a zipped revision, unzip it and run the test.""" 713 """Given a zipped revision, unzip it and run the test."""
636 print 'Trying revision %s...' % str(revision) 714 print 'Trying revision %s...' % str(revision)
637 715
638 if context.android: 716 if context.is_android:
639 RunAndroidRevision(context, revision, zip_file) 717 RunAndroidRevision(context, revision, zip_file)
640 # TODO(mikecase): Support running command to auto-bisect Android. 718 # TODO(mikecase): Support running command to auto-bisect Android.
641 return (None, None, None) 719 return (None, None, None)
642 720
643 # Create a temp directory and unzip the revision into it. 721 # Create a temp directory and unzip the revision into it.
644 cwd = os.getcwd() 722 cwd = os.getcwd()
645 tempdir = tempfile.mkdtemp(prefix='bisect_tmp') 723 tempdir = tempfile.mkdtemp(prefix='bisect_tmp')
646 UnzipFilenameToDir(zip_file, tempdir) 724 UnzipFilenameToDir(zip_file, tempdir)
647 os.chdir(tempdir) 725 os.chdir(tempdir)
648 726
(...skipping 26 matching lines...) Expand all
675 replace('%s', ' '.join(testargs))) 753 replace('%s', ' '.join(testargs)))
676 754
677 results = [] 755 results = []
678 for _ in range(num_runs): 756 for _ in range(num_runs):
679 subproc = subprocess.Popen(runcommand, 757 subproc = subprocess.Popen(runcommand,
680 bufsize=-1, 758 bufsize=-1,
681 stdout=subprocess.PIPE, 759 stdout=subprocess.PIPE,
682 stderr=subprocess.PIPE) 760 stderr=subprocess.PIPE)
683 (stdout, stderr) = subproc.communicate() 761 (stdout, stderr) = subproc.communicate()
684 results.append((subproc.returncode, stdout, stderr)) 762 results.append((subproc.returncode, stdout, stderr))
685
686 os.chdir(cwd) 763 os.chdir(cwd)
687 try: 764 try:
688 shutil.rmtree(tempdir, True) 765 shutil.rmtree(tempdir, True)
689 except Exception: 766 except Exception:
690 pass 767 pass
691 768
692 for (returncode, stdout, stderr) in results: 769 for (returncode, stdout, stderr) in results:
693 if returncode: 770 if returncode:
694 return (returncode, stdout, stderr) 771 return (returncode, stdout, stderr)
695 return results[0] 772 return results[0]
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
813 890
814 print 'Downloading list of known revisions...', 891 print 'Downloading list of known revisions...',
815 if not context.use_local_repo and not context.is_official: 892 if not context.use_local_repo and not context.is_official:
816 print '(use --use-local-repo for speed if you have a local checkout)' 893 print '(use --use-local-repo for speed if you have a local checkout)'
817 else: 894 else:
818 print 895 print
819 _GetDownloadPath = lambda rev: os.path.join(cwd, 896 _GetDownloadPath = lambda rev: os.path.join(cwd,
820 '%s-%s' % (str(rev), context.archive_name)) 897 '%s-%s' % (str(rev), context.archive_name))
821 if context.is_official: 898 if context.is_official:
822 revlist = context.GetOfficialBuildsList() 899 revlist = context.GetOfficialBuildsList()
900 elif context.is_android: # Android non-official
901 revlist = context.GetAndroidToTRevisions()
823 else: 902 else:
824 revlist = context.GetRevList() 903 revlist = context.GetRevList()
825 904
826 # Get a list of revisions to bisect across. 905 # Get a list of revisions to bisect across.
827 if len(revlist) < 2: # Don't have enough builds to bisect. 906 if len(revlist) < 2: # Don't have enough builds to bisect.
828 msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist 907 msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist
829 raise RuntimeError(msg) 908 raise RuntimeError(msg)
830 909
831 # Figure out our bookends and first pivot point; fetch the pivot revision. 910 # Figure out our bookends and first pivot point; fetch the pivot revision.
832 minrev = 0 911 minrev = 0
(...skipping 362 matching lines...) Expand 10 before | Expand all | Expand 10 after
1195 elif opts.blink: 1274 elif opts.blink:
1196 base_url = WEBKIT_BASE_URL 1275 base_url = WEBKIT_BASE_URL
1197 else: 1276 else:
1198 base_url = CHROMIUM_BASE_URL 1277 base_url = CHROMIUM_BASE_URL
1199 1278
1200 # Create the context. Initialize 0 for the revisions as they are set below. 1279 # Create the context. Initialize 0 for the revisions as they are set below.
1201 context = PathContext(base_url, opts.archive, opts.good, opts.bad, 1280 context = PathContext(base_url, opts.archive, opts.good, opts.bad,
1202 opts.official_builds, opts.asan, opts.use_local_repo, 1281 opts.official_builds, opts.asan, opts.use_local_repo,
1203 opts.flash_path, opts.pdf_path, opts.apk) 1282 opts.flash_path, opts.pdf_path, opts.apk)
1204 1283
1205 # TODO(mikecase): Add support to bisect on nonofficial builds for Android. 1284 if context.is_android and not opts.official_builds:
1206 if context.android and not opts.official_builds: 1285 if(context.platform != 'android-arm' or
Robert Sesek 2015/02/04 17:27:54 nit: space before (
mikecase (-- gone --) 2015/02/04 22:50:44 Done.
1207 sys.exit('Can only bisect on official builds for Android.') 1286 context.android_apk != 'Chrome.apk'):
1287 sys.exit('For non-official builds, can only bisect'
1288 ' Chrome.apk arm builds.')
1208 1289
1209 # If bisecting Android, we make sure we have ADB setup. 1290 # If bisecting Android, we make sure we have ADB setup.
1210 if context.android: 1291 if context.is_android:
1211 if opts.adb_path: 1292 if opts.adb_path:
1212 os.environ['PATH'] = '%s:%s' % (os.path.dirname(opts.adb_path), 1293 os.environ['PATH'] = '%s:%s' % (os.path.dirname(opts.adb_path),
1213 os.environ['PATH']) 1294 os.environ['PATH'])
1214 if not IsADBInstalled(): 1295 if not IsADBInstalled():
1215 sys.exit('Please have "adb" in PATH or use adb_path command line option' 1296 sys.exit('Please have "adb" in PATH or use adb_path command line option'
1216 'to bisect Android builds.') 1297 'to bisect Android builds.')
1217 1298
1218 # Pick a starting point, try to get HEAD for this. 1299 # Pick a starting point, try to get HEAD for this.
1219 if not opts.bad: 1300 if not opts.bad:
1220 context.bad_revision = '999.0.0.0' 1301 context.bad_revision = '999.0.0.0'
1221 context.bad_revision = GetChromiumRevision( 1302 context.bad_revision = GetChromiumRevision(
1222 context, context.GetLastChangeURL()) 1303 context, context.GetLastChangeURL())
1223 1304
1224 # Find out when we were good. 1305 # Find out when we were good.
1225 if not opts.good: 1306 if not opts.good:
1226 context.good_revision = '0.0.0.0' if opts.official_builds else 0 1307 context.good_revision = '0.0.0.0' if opts.official_builds else 0
1227 1308
1228 if opts.flash_path: 1309 if opts.flash_path:
1229 msg = 'Could not find Flash binary at %s' % opts.flash_path 1310 msg = 'Could not find Flash binary at %s' % opts.flash_path
1230 assert os.path.exists(opts.flash_path), msg 1311 assert os.path.exists(opts.flash_path), msg
1231 1312
1232 if opts.pdf_path: 1313 if opts.pdf_path:
1233 msg = 'Could not find PDF binary at %s' % opts.pdf_path 1314 msg = 'Could not find PDF binary at %s' % opts.pdf_path
1234 assert os.path.exists(opts.pdf_path), msg 1315 assert os.path.exists(opts.pdf_path), msg
1235 1316
1236 if opts.official_builds: 1317 if opts.official_builds:
1237 context.good_revision = LooseVersion(context.good_revision) 1318 context.good_revision = LooseVersion(context.good_revision)
1238 context.bad_revision = LooseVersion(context.bad_revision) 1319 context.bad_revision = LooseVersion(context.bad_revision)
1320 elif context.is_android:
1321 # Revisions are git hashes and should be left as strings.
1322 pass
1239 else: 1323 else:
1240 context.good_revision = int(context.good_revision) 1324 context.good_revision = int(context.good_revision)
1241 context.bad_revision = int(context.bad_revision) 1325 context.bad_revision = int(context.bad_revision)
1242 1326
1243 if opts.times < 1: 1327 if opts.times < 1:
1244 print('Number of times to run (%d) must be greater than or equal to 1.' % 1328 print('Number of times to run (%d) must be greater than or equal to 1.' %
1245 opts.times) 1329 opts.times)
1246 parser.print_help() 1330 parser.print_help()
1247 return 1 1331 return 1
1248 1332
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1294 1378
1295 print 'CHANGELOG URL:' 1379 print 'CHANGELOG URL:'
1296 if opts.official_builds: 1380 if opts.official_builds:
1297 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) 1381 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev)
1298 else: 1382 else:
1299 PrintChangeLog(min_chromium_rev, max_chromium_rev) 1383 PrintChangeLog(min_chromium_rev, max_chromium_rev)
1300 1384
1301 1385
1302 if __name__ == '__main__': 1386 if __name__ == '__main__':
1303 sys.exit(main()) 1387 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698