Chromium Code Reviews| Index: build/build-bisect.py |
| diff --git a/build/build-bisect.py b/build/build-bisect.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..38fc54f85fd1182f2f2cbf2d2c9eaf38661ec8b8 |
| --- /dev/null |
| +++ b/build/build-bisect.py |
| @@ -0,0 +1,160 @@ |
| +#!/usr/bin/python2.5 |
| +# Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Snapshot Build Bisect Tool |
| + |
| +This script bisects the Mac snapshot archive using binary search. It starts at |
| +a bad revision (it will try to guess HEAD) and asks for a last known-good |
| +revision. It will then binary search across this revision range by downloading, |
| +unzipping, and opening Chromium for you. After testing the specific revision, |
| +it will ask you whether it is good or bad before continuing the search. |
| + |
| +Currently this only works on Mac, but with some effort it could be ported to |
| +other platforms. |
| +""" |
| + |
| +# Base URL to download snapshots from. |
| +BUILD_BASE_URL = \ |
| + "http://build.chromium.org/buildbot/snapshots/chromium-rel-mac" |
| + |
| +# Location of the latest build revision number |
| +BUILD_LATEST_URL = "%s/LATEST" % BUILD_BASE_URL |
| + |
| +# The location of the builds. |
| +BUILD_ARCHIVE_URL = "/%d/" |
| + |
| +# Name of the build archive. |
| +BUILD_ZIP_NAME = "chrome-mac.zip" |
| + |
| +# Directory name inside the archive. |
| +BUILD_DIR_NAME = "chrome-mac" |
| + |
| +# Name of the executable. |
| +BUILD_EXE_NAME = "Chromium.app" |
| + |
| +# URL to the ViewVC commit page. |
| +BUILD_VIEWVC_URL = "http://src.chromium.org/viewvc/chrome?view=rev&revision=%d" |
| + |
| +############################################################################### |
| + |
| +import math |
| +import os |
| +import re |
| +import shutil |
| +import sys |
| +import urllib |
| + |
| +def ParseDirectoryIndex(url): |
| + """Parses the HTML directory listing into a list of revision numbers.""" |
| + handle = urllib.urlopen(url) |
| + dirindex = handle.read() |
| + handle.close() |
| + return re.findall(r'<a href="([0-9]*)/">\1/</a>', dirindex) |
| + |
| +def GetRevList(good, bad): |
| + """Gets the list of revision numbers between |good| and |bad|.""" |
| + # Download the main revlist. |
| + revlist = ParseDirectoryIndex(BUILD_BASE_URL) |
| + revlist = map(int, revlist) |
| + revlist = filter(lambda r: range(good, bad).__contains__(int(r)), revlist) |
| + revlist.sort() |
| + return revlist |
| + |
| +def TryRevision(rev): |
| + """Downloads revision |rev|, unzips it, and opens it for the user to test.""" |
| + # Clear anything that's currently there. |
| + try: |
| + os.remove(BUILD_ZIP_NAME) |
| + shutil.rmtree(BUILD_DIR_NAME, True) |
| + except Exception, e: |
| + pass |
| + |
| + # Download the file. |
| + download_url = BUILD_BASE_URL + (BUILD_ARCHIVE_URL % rev) + BUILD_ZIP_NAME |
| + try: |
| + urllib.urlretrieve(download_url, BUILD_ZIP_NAME) |
| + except Exception, e: |
| + print("Could not retrieve the download. Sorry.") |
| + print("Tried to get: %s" % download_url) |
| + sys.exit(-1) |
| + |
| + # Unzip the file. |
| + os.system("unzip -q %s" % BUILD_ZIP_NAME) |
| + |
| + # Tell Finder to open the app. |
| + os.system("open %s/%s" % (BUILD_DIR_NAME, BUILD_EXE_NAME)) |
| + |
| +def AskIsGoodBuild(rev): |
| + """Annoyingly ask the user whether build |rev| is good or bad.""" |
| + while True: |
| + check = raw_input("Build %d [g/b]: " % int(rev))[0] |
| + if (check == "g" or check == "b"): |
| + return (check == "g") |
| + else: |
| + print("Just answer the question...") |
| + |
| +def main(): |
| + print("chrome-bisect: Perform binary search on the snapshot builds") |
| + |
| + # Pick a starting point, try to get HEAD for this. |
| + bad_rev = 0 |
| + try: |
| + nh = urllib.urlopen(BUILD_LATEST_URL) |
| + latest = int(nh.read()) |
| + nh.close() |
| + bad_rev = raw_input("Bad revision [HEAD:%d]: " % latest) |
| + if (bad_rev == ""): |
| + bad_rev = latest |
| + bad_rev = int(bad_rev) |
| + except Exception, e: |
| + print("Could not determine latest revision. This could be bad...") |
| + bad_rev = int(raw_input("Bad revision: ")) |
| + |
| + # Find out when we were good. |
| + good_rev = 0 |
| + try: |
| + good_rev = int(raw_input("Last known good [0]: ")) |
| + except Exception, e: |
| + pass |
| + |
| + # Get a list of revisions to bisect across. |
| + revlist = GetRevList(good_rev, bad_rev) |
| + |
| + # If we don't have a |good_rev|, set it to be the first revision possible. |
| + if good_rev == 0: |
| + good_rev = revlist[0] |
| + |
| + # These are indexes of |revlist|. |
| + good = 0 |
| + bad = len(revlist) - 1 |
| + |
| + # Binary search time! |
| + while good < bad: |
| + candidates = revlist[good:bad] |
| + num_poss = len(candidates) |
| + if num_poss > 10: |
| + print("%d candidates. %d tries left." % |
| + (num_poss, round(math.log(num_poss, 2)))) |
| + else: |
| + print("Candidates: %s" % revlist[good:bad]) |
| + |
| + # Cut the problem in half... |
| + test = int((bad - good) / 2) + good |
| + test_rev = revlist[test] |
| + |
| + # Let the user give this revision a spin. |
| + TryRevision(test_rev) |
| + if AskIsGoodBuild(test_rev): |
| + good = test + 1 |
| + else: |
| + bad = test |
| + |
| + # We're done. Let the user know the results in an official manner. |
| + print("You are probably looking for build %d." % revlist[bad]) |
|
Evan Martin
2009/09/03 20:18:49
Maybe point out that this build is the earliest *b
|
| + print("This is the ViewVC URL for the potential bustage:") |
| + print(BUILD_VIEWVC_URL % revlist[bad]) |
| + |
| +if __name__ == '__main__': |
| + main() |