def main(): """Build a shell and place it in the autoBisect cache.""" usage = 'Usage: %prog [options]' parser = OptionParser(usage) parser.disable_interspersed_args() parser.set_defaults( buildOptions="", ) # Specify how the shell will be built. # See buildOptions.py for details. parser.add_option('-b', '--build', dest='buildOptions', help="Specify build options, e.g. -b '--disable-debug --enable-optimize' " "(python buildOptions.py --help)") parser.add_option('-r', '--rev', dest='revision', help='Specify revision to build') options = parser.parse_args()[0] options.buildOptions = buildOptions.parseShellOptions(options.buildOptions) with LockDir(getLockDirPath(options.buildOptions.repoDir)): if options.revision: shell = CompiledShell(options.buildOptions, options.revision) else: localOrigHgHash = hgCmds.getRepoHashAndId(options.buildOptions.repoDir)[0] shell = CompiledShell(options.buildOptions, localOrigHgHash) obtainShell(shell, updateToRev=options.revision) print(shell.getShellCacheFullPath())
def main(): """Build a shell and place it in the autoBisect cache.""" usage = 'Usage: %prog [options]' parser = OptionParser(usage) parser.disable_interspersed_args() parser.set_defaults( buildOptions="", ) # Specify how the shell will be built. # See buildOptions.py for details. parser.add_option('-b', '--build', dest='buildOptions', help='Specify build options, e.g. -b "--disable-debug --enable-optimize" ' + '(python buildOptions.py --help)') parser.add_option('-r', '--rev', dest='revision', help='Specify revision to build') options = parser.parse_args()[0] options.buildOptions = buildOptions.parseShellOptions(options.buildOptions) with LockDir(getLockDirPath(options.buildOptions.repoDir)): if options.revision: shell = CompiledShell(options.buildOptions, options.revision) else: localOrigHgHash = hgCmds.getRepoHashAndId(options.buildOptions.repoDir)[0] shell = CompiledShell(options.buildOptions, localOrigHgHash) obtainShell(shell, updateToRev=options.revision) print shell.getShellCacheFullPath()
def ensureBuild(options): if options.existingBuildDir: # Pre-downloaded treeherder builds (browser only for now) bDir = options.existingBuildDir bType = 'local-build' bSrc = bDir bRev = '' manyTimedRunArgs = [] elif not options.useTreeherderBuilds: # Compiled browser options.buildOptions = buildBrowser.parseOptions( options.buildOptions.split()) bDir = options.buildOptions.objDir bType = platform.system() + "-" + os.path.basename( options.buildOptions.mozconfig) bSrc = repr(hgCmds.getRepoHashAndId(options.buildOptions.repoDir)) bRev = '' manyTimedRunArgs = [] success = buildBrowser.tryCompiling(options.buildOptions) if not success: raise Exception('Building a browser failed.') else: # Treeherder browser # Download from Treeherder and call it 'build' # FIXME: Put 'build' somewhere nicer, like ~/fuzzbuilds/. Don't re-download a build that's up to date. # FIXME: randomize branch selection, get appropriate builds, use appropriate known dirs bDir = 'build' bType = downloadBuild.defaultBuildType(options.repoName, None, True) bSrc = downloadBuild.downloadLatestBuild(bType, './', getJsShell=False, wantTests=True) bRev = '' # These two lines are only used for treeherder js shells: shell = os.path.join(bDir, "dist", "js.exe" if sps.isWin else "js") manyTimedRunArgs = [ "--random-flags", str(JS_SHELL_DEFAULT_TIMEOUT), "mozilla-central", shell ] return BuildInfo(bDir, bType, bSrc, bRev, manyTimedRunArgs)
def parseOpts(): usage = 'Usage: %prog [options]' parser = OptionParser(usage) # http://docs.python.org/library/optparse.html#optparse.OptionParser.disable_interspersed_args parser.disable_interspersed_args() parser.set_defaults( resetRepoFirst=False, startRepo=None, endRepo='default', testInitialRevs=True, output='', watchExitCode=None, useInterestingnessTests=False, parameters= '-e 42', # http://en.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy compilationFailedLabel='skip', buildOptions="", useTreeherderBinaries=False, nameOfTreeherderBranch='mozilla-inbound', ) # Specify how the shell will be built. # See buildOptions.py for details. parser.add_option( '-b', '--build', dest='buildOptions', help= 'Specify js shell build options, e.g. -b "--enable-debug --32" (python buildOptions.py --help)' ) parser.add_option( '-B', '--browser', dest='browserOptions', help='Specify browser build options, e.g. -b "-c mozconfig"') parser.add_option( '--resetToTipFirst', dest='resetRepoFirst', action='store_true', help='First reset to default tip overwriting all local changes. ' + 'Equivalent to first executing `hg update -C default`. ' + 'Defaults to "%default".') # Specify the revisions between which to bisect. parser.add_option( '-s', '--startRev', dest='startRepo', help= 'Earliest changeset/build numeric ID to consider (usually a "good" cset). ' + 'Defaults to the earliest revision known to work at all/available.') parser.add_option( '-e', '--endRev', dest='endRepo', help= 'Latest changeset/build numeric ID to consider (usually a "bad" cset). ' + 'Defaults to the head of the main branch, "default", or latest available build.' ) parser.add_option( '-k', '--skipInitialRevs', dest='testInitialRevs', action='store_false', help= 'Skip testing the -s and -e revisions and automatically trust them ' + 'as -g and -b.') # Specify the type of failure to look for. # (Optional -- by default, internalTestAndLabel will look for exit codes that indicate a crash or assert.) parser.add_option( '-o', '--output', dest='output', help='Stdout or stderr output to be observed. Defaults to "%default". ' + 'For assertions, set to "ssertion fail"') parser.add_option( '-w', '--watchExitCode', dest='watchExitCode', type='int', help='Look out for a specific exit code. Only this exit code will be ' + 'considered "bad".') parser.add_option( '-i', '--useInterestingnessTests', dest='useInterestingnessTests', action="store_true", help="Interpret the final arguments as an interestingness test.") # Specify parameters for the js shell. parser.add_option( '-p', '--parameters', dest='parameters', help= 'Specify parameters for the js shell, e.g. -p "-a --ion-eager testcase.js".' ) # Specify how to treat revisions that fail to compile. # (You might want to add these to kbew.knownBrokenRanges in knownBrokenEarliestWorking.py.) parser.add_option( '-l', '--compilationFailedLabel', dest='compilationFailedLabel', help='Specify how to treat revisions that fail to compile. ' + '(bad, good, or skip) Defaults to "%default"') parser.add_option( '-T', '--useTreeherderBinaries', dest='useTreeherderBinaries', action="store_true", help='Use treeherder binaries for quick bisection, assuming a fast ' + 'internet connection. Defaults to "%default"') parser.add_option( '-N', '--nameOfTreeherderBranch', dest='nameOfTreeherderBranch', help='Name of the branch to download. Defaults to "%default"') (options, args) = parser.parse_args() if options.browserOptions: assert not options.buildOptions options.browserOptions = buildBrowser.parseOptions( options.browserOptions.split()) options.skipRevs = ' + '.join( kbew.knownBrokenRangesBrowser(options.browserOptions)) else: options.buildOptions = buildOptions.parseShellOptions( options.buildOptions) options.skipRevs = ' + '.join( kbew.knownBrokenRanges(options.buildOptions)) options.paramList = [ sps.normExpUserPath(x) for x in options.parameters.split(' ') if x ] # First check that the testcase is present. if '-e 42' not in options.parameters and not os.path.isfile( options.paramList[-1]): print '\nList of parameters to be passed to the shell is: ' + ' '.join( options.paramList) print raise Exception('Testcase at ' + options.paramList[-1] + ' is not present.') assert options.compilationFailedLabel in ('bad', 'good', 'skip') extraFlags = [] if options.useInterestingnessTests: if len(args) < 1: print 'args are: ' + args parser.error('Not enough arguments.') if not options.browserOptions: for a in args: if a.startswith("--flags="): extraFlags = a[8:].split(' ') options.testAndLabel = externalTestAndLabel(options, args) else: assert not options.browserOptions # autoBisect doesn't have a built-in way to run the browser if len(args) >= 1: parser.error('Too many arguments.') options.testAndLabel = internalTestAndLabel(options) if options.browserOptions: earliestKnownQuery = kbew.earliestKnownWorkingRevForBrowser( options.browserOptions) else: earliestKnownQuery = kbew.earliestKnownWorkingRev( options.buildOptions, options.paramList + extraFlags, options.skipRevs) earliestKnown = '' if not options.useTreeherderBinaries: earliestKnown = hgCmds.getRepoHashAndId(options.buildOptions.repoDir, repoRev=earliestKnownQuery)[0] if options.startRepo is None: if options.useTreeherderBinaries: options.startRepo = 'default' else: options.startRepo = earliestKnown # elif not (options.useTreeherderBinaries or hgCmds.isAncestor(options.buildOptions.repoDir, earliestKnown, options.startRepo)): # raise Exception('startRepo is not a descendant of kbew.earliestKnownWorkingRev for this configuration') # # if not options.useTreeherderBinaries and not hgCmds.isAncestor(options.buildOptions.repoDir, earliestKnown, options.endRepo): # raise Exception('endRepo is not a descendant of kbew.earliestKnownWorkingRev for this configuration') if options.parameters == '-e 42': print "Note: since no parameters were specified, we're just ensuring the shell does not crash on startup/shutdown." if options.nameOfTreeherderBranch != 'mozilla-inbound' and not options.useTreeherderBinaries: raise Exception( 'Setting the name of branches only works for treeherder shell bisection.' ) return options
def findBlamedCset(options, repoDir, testRev): print "%s | Bisecting on: %s" % (sps.dateStr(), repoDir) hgPrefix = ['hg', '-R', repoDir] # Resolve names such as "tip", "default", or "52707" to stable hg hash ids, e.g. "9f2641871ce8". realStartRepo = sRepo = hgCmds.getRepoHashAndId( repoDir, repoRev=options.startRepo)[0] realEndRepo = eRepo = hgCmds.getRepoHashAndId(repoDir, repoRev=options.endRepo)[0] sps.vdump("Bisecting in the range " + sRepo + ":" + eRepo) # Refresh source directory (overwrite all local changes) to default tip if required. if options.resetRepoFirst: subprocess.check_call(hgPrefix + ['update', '-C', 'default']) # Throws exit code 255 if purge extension is not enabled in .hgrc: subprocess.check_call(hgPrefix + ['purge', '--all']) # Reset bisect ranges and set skip ranges. sps.captureStdout(hgPrefix + ['bisect', '-r']) if options.skipRevs: sps.captureStdout(hgPrefix + ['bisect', '--skip', options.skipRevs]) labels = {} # Specify `hg bisect` ranges. if options.testInitialRevs: currRev = eRepo # If testInitialRevs mode is set, compile and test the latest rev first. else: labels[sRepo] = ('good', 'assumed start rev is good') labels[eRepo] = ('bad', 'assumed end rev is bad') subprocess.check_call(hgPrefix + ['bisect', '-U', '-g', sRepo]) currRev = hgCmds.getCsetHashFromBisectMsg( fileManipulation.firstLine( sps.captureStdout(hgPrefix + ['bisect', '-U', '-b', eRepo])[0])) iterNum = 1 if options.testInitialRevs: iterNum -= 2 skipCount = 0 blamedRev = None while currRev is not None: startTime = time.time() label = testRev(currRev) labels[currRev] = label if label[0] == 'skip': skipCount += 1 # If we use "skip", we tell hg bisect to do a linear search to get around the skipping. # If the range is large, doing a bisect to find the start and endpoints of compilation # bustage would be faster. 20 total skips being roughly the time that the pair of # bisections would take. if skipCount > 20: print 'Skipped 20 times, stopping autoBisect.' break print label[0] + " (" + label[1] + ") ", if iterNum <= 0: print 'Finished testing the initial boundary revisions...', else: print "Bisecting for the n-th round where n is", iterNum, "and 2^n is", \ str(2**iterNum), "...", (blamedGoodOrBad, blamedRev, currRev, sRepo, eRepo) = \ bisectLabel(hgPrefix, options, label[0], currRev, sRepo, eRepo) if options.testInitialRevs: options.testInitialRevs = False assert currRev is None currRev = sRepo # If options.testInitialRevs is set, test earliest possible rev next. iterNum += 1 endTime = time.time() oneRunTime = endTime - startTime print 'This iteration took %.3f seconds to run.' % oneRunTime if blamedRev is not None: checkBlameParents(repoDir, blamedRev, blamedGoodOrBad, labels, testRev, realStartRepo, realEndRepo) sps.vdump("Resetting bisect") subprocess.check_call(hgPrefix + ['bisect', '-U', '-r']) sps.vdump("Resetting working directory") sps.captureStdout(hgPrefix + ['update', '-C', '-r', 'default'], ignoreStderr=True) hgCmds.destroyPyc(repoDir) print sps.dateStr()
def ensureBuild(options): if options.existingBuildDir: # Pre-downloaded treeherder builds (browser only for now) bDir = options.existingBuildDir bType = 'local-build' bSrc = bDir bRev = '' manyTimedRunArgs = [] elif not options.useTreeherderBuilds: if options.testType == "js": # Compiled js shells options.buildOptions = buildOptions.parseShellOptions(options.buildOptions) options.timeout = options.timeout or machineTimeoutDefaults(options) with LockDir(compileShell.getLockDirPath(options.buildOptions.repoDir)): bRev = hgCmds.getRepoHashAndId(options.buildOptions.repoDir)[0] cshell = compileShell.CompiledShell(options.buildOptions, bRev) compileShell.obtainShell(cshell, updateLatestTxt=True) bDir = cshell.getShellCacheDir() # Strip out first 3 chars or else the dir name in fuzzing jobs becomes: # js-js-dbg-opt-64-dm-linux # This is because options.testType gets prepended along with a dash later. bType = buildOptions.computeShellType(options.buildOptions)[3:] bSrc = ( 'Create another shell in shell-cache like this one:\n' + 'python -u %s -b "%s -R %s" -r %s\n\n' % ( os.path.join(path3, 'compileShell.py'), options.buildOptions.buildOptionsStr, options.buildOptions.repoDir, bRev ) + '==============================================\n' + '| Fuzzing %s js shell builds\n' % cshell.getRepoName() + '| DATE: %s\n' % sps.dateStr() + '==============================================\n\n') manyTimedRunArgs = mtrArgsCreation(options, cshell) print 'buildDir is: ' + bDir print 'buildSrc is: ' + bSrc else: # Compiled browser options.buildOptions = buildBrowser.parseOptions(options.buildOptions.split()) bDir = options.buildOptions.objDir bType = platform.system() + "-" + os.path.basename(options.buildOptions.mozconfig) bSrc = repr(hgCmds.getRepoHashAndId(options.buildOptions.repoDir)) bRev = '' manyTimedRunArgs = [] success = buildBrowser.tryCompiling(options.buildOptions) if not success: raise Exception('Building a browser failed.') else: # Treeherder js shells and browser # Download from Treeherder and call it 'build' # FIXME: Put 'build' somewhere nicer, like ~/fuzzbuilds/. Don't re-download a build that's up to date. # FIXME: randomize branch selection, get appropriate builds, use appropriate known dirs bDir = 'build' bType = downloadBuild.defaultBuildType(options.repoName, None, True) bSrc = downloadBuild.downloadLatestBuild(bType, './', getJsShell=(options.testType == 'js')) bRev = '' # These two lines are only used for treeherder js shells: shell = os.path.join(bDir, "dist", "js.exe" if sps.isWin else "js") manyTimedRunArgs = ["--random-flags", str(JS_SHELL_DEFAULT_TIMEOUT), "mozilla-central", shell] return BuildInfo(bDir, bType, bSrc, bRev, manyTimedRunArgs)
def ensureBuild(options): if options.existingBuildDir: # Pre-downloaded treeherder builds (browser only for now) bDir = options.existingBuildDir bType = 'local-build' bSrc = bDir bRev = '' manyTimedRunArgs = [] elif not options.useTreeherderBuilds: if options.testType == "js": # Compiled js shells options.buildOptions = buildOptions.parseShellOptions( options.buildOptions) options.timeout = options.timeout or machineTimeoutDefaults( options) with LockDir( compileShell.getLockDirPath(options.buildOptions.repoDir)): bRev = hgCmds.getRepoHashAndId(options.buildOptions.repoDir)[0] cshell = compileShell.CompiledShell(options.buildOptions, bRev) compileShell.obtainShell(cshell, updateLatestTxt=True) bDir = cshell.getShellCacheDir() # Strip out first 3 chars or else the dir name in fuzzing jobs becomes: # js-js-dbg-opt-64-dm-linux # This is because options.testType gets prepended along with a dash later. bType = buildOptions.computeShellType(options.buildOptions)[3:] bSrc = ( 'Create another shell in shell-cache like this one:\n' + 'python -u %s -b "%s -R %s" -r %s\n\n' % (os.path.join(path3, 'compileShell.py'), options.buildOptions.buildOptionsStr, options.buildOptions.repoDir, bRev) + '==============================================\n' + '| Fuzzing %s js shell builds\n' % cshell.getRepoName() + '| DATE: %s\n' % sps.dateStr() + '==============================================\n\n') manyTimedRunArgs = mtrArgsCreation(options, cshell) print 'buildDir is: ' + bDir print 'buildSrc is: ' + bSrc else: # Compiled browser options.buildOptions = buildBrowser.parseOptions( options.buildOptions.split()) bDir = options.buildOptions.objDir bType = platform.system() + "-" + os.path.basename( options.buildOptions.mozconfig) bSrc = repr(hgCmds.getRepoHashAndId(options.buildOptions.repoDir)) bRev = '' manyTimedRunArgs = [] success = buildBrowser.tryCompiling(options.buildOptions) if not success: raise Exception('Building a browser failed.') else: # Treeherder js shells and browser # Download from Treeherder and call it 'build' # FIXME: Put 'build' somewhere nicer, like ~/fuzzbuilds/. Don't re-download a build that's up to date. # FIXME: randomize branch selection, get appropriate builds, use appropriate known dirs bDir = 'build' bType = downloadBuild.defaultBuildType(options.repoName, None, True) bSrc = downloadBuild.downloadLatestBuild( bType, './', getJsShell=(options.testType == 'js')) bRev = '' # These two lines are only used for treeherder js shells: shell = os.path.join(bDir, "dist", "js.exe" if sps.isWin else "js") manyTimedRunArgs = [ "--random-flags", str(JS_SHELL_DEFAULT_TIMEOUT), "mozilla-central", shell ] return BuildInfo(bDir, bType, bSrc, bRev, manyTimedRunArgs)
def parseOpts(): usage = 'Usage: %prog [options]' parser = OptionParser(usage) # http://docs.python.org/library/optparse.html#optparse.OptionParser.disable_interspersed_args parser.disable_interspersed_args() parser.set_defaults( resetRepoFirst=False, startRepo=None, endRepo='default', testInitialRevs=True, output='', watchExitCode=None, useInterestingnessTests=False, parameters='-e 42', # http://en.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy compilationFailedLabel='skip', buildOptions="", useTreeherderBinaries=False, nameOfTreeherderBranch='mozilla-inbound', ) # Specify how the shell will be built. # See buildOptions.py for details. parser.add_option('-b', '--build', dest='buildOptions', help='Specify js shell build options, e.g. -b "--enable-debug --32" (python buildOptions.py --help)') parser.add_option('-B', '--browser', dest='browserOptions', help='Specify browser build options, e.g. -b "-c mozconfig"') parser.add_option('--resetToTipFirst', dest='resetRepoFirst', action='store_true', help='First reset to default tip overwriting all local changes. ' + 'Equivalent to first executing `hg update -C default`. ' + 'Defaults to "%default".') # Specify the revisions between which to bisect. parser.add_option('-s', '--startRev', dest='startRepo', help='Earliest changeset/build numeric ID to consider (usually a "good" cset). ' + 'Defaults to the earliest revision known to work at all/available.') parser.add_option('-e', '--endRev', dest='endRepo', help='Latest changeset/build numeric ID to consider (usually a "bad" cset). ' + 'Defaults to the head of the main branch, "default", or latest available build.') parser.add_option('-k', '--skipInitialRevs', dest='testInitialRevs', action='store_false', help='Skip testing the -s and -e revisions and automatically trust them ' + 'as -g and -b.') # Specify the type of failure to look for. # (Optional -- by default, internalTestAndLabel will look for exit codes that indicate a crash or assert.) parser.add_option('-o', '--output', dest='output', help='Stdout or stderr output to be observed. Defaults to "%default". ' + 'For assertions, set to "ssertion fail"') parser.add_option('-w', '--watchExitCode', dest='watchExitCode', type='int', help='Look out for a specific exit code. Only this exit code will be ' + 'considered "bad".') parser.add_option('-i', '--useInterestingnessTests', dest='useInterestingnessTests', action="store_true", help="Interpret the final arguments as an interestingness test.") # Specify parameters for the js shell. parser.add_option('-p', '--parameters', dest='parameters', help='Specify parameters for the js shell, e.g. -p "-a --ion-eager testcase.js".') # Specify how to treat revisions that fail to compile. # (You might want to add these to kbew.knownBrokenRanges in knownBrokenEarliestWorking.py.) parser.add_option('-l', '--compilationFailedLabel', dest='compilationFailedLabel', help='Specify how to treat revisions that fail to compile. ' + '(bad, good, or skip) Defaults to "%default"') parser.add_option('-T', '--useTreeherderBinaries', dest='useTreeherderBinaries', action="store_true", help='Use treeherder binaries for quick bisection, assuming a fast ' + 'internet connection. Defaults to "%default"') parser.add_option('-N', '--nameOfTreeherderBranch', dest='nameOfTreeherderBranch', help='Name of the branch to download. Defaults to "%default"') (options, args) = parser.parse_args() if options.browserOptions: assert not options.buildOptions options.browserOptions = buildBrowser.parseOptions(options.browserOptions.split()) options.skipRevs = ' + '.join(kbew.knownBrokenRangesBrowser(options.browserOptions)) else: options.buildOptions = buildOptions.parseShellOptions(options.buildOptions) options.skipRevs = ' + '.join(kbew.knownBrokenRanges(options.buildOptions)) options.paramList = [sps.normExpUserPath(x) for x in options.parameters.split(' ') if x] # First check that the testcase is present. if '-e 42' not in options.parameters and not os.path.isfile(options.paramList[-1]): print '\nList of parameters to be passed to the shell is: ' + ' '.join(options.paramList) print raise Exception('Testcase at ' + options.paramList[-1] + ' is not present.') assert options.compilationFailedLabel in ('bad', 'good', 'skip') extraFlags = [] if options.useInterestingnessTests: if len(args) < 1: print 'args are: ' + args parser.error('Not enough arguments.') if not options.browserOptions: for a in args: if a.startswith("--flags="): extraFlags = a[8:].split(' ') options.testAndLabel = externalTestAndLabel(options, args) else: assert not options.browserOptions # autoBisect doesn't have a built-in way to run the browser if len(args) >= 1: parser.error('Too many arguments.') options.testAndLabel = internalTestAndLabel(options) if options.browserOptions: earliestKnownQuery = kbew.earliestKnownWorkingRevForBrowser(options.browserOptions) else: earliestKnownQuery = kbew.earliestKnownWorkingRev(options.buildOptions, options.paramList + extraFlags, options.skipRevs) earliestKnown = '' if not options.useTreeherderBinaries: earliestKnown = hgCmds.getRepoHashAndId(options.buildOptions.repoDir, repoRev=earliestKnownQuery)[0] if options.startRepo is None: if options.useTreeherderBinaries: options.startRepo = 'default' else: options.startRepo = earliestKnown # elif not (options.useTreeherderBinaries or hgCmds.isAncestor(options.buildOptions.repoDir, earliestKnown, options.startRepo)): # raise Exception('startRepo is not a descendant of kbew.earliestKnownWorkingRev for this configuration') # # if not options.useTreeherderBinaries and not hgCmds.isAncestor(options.buildOptions.repoDir, earliestKnown, options.endRepo): # raise Exception('endRepo is not a descendant of kbew.earliestKnownWorkingRev for this configuration') if options.parameters == '-e 42': print "Note: since no parameters were specified, we're just ensuring the shell does not crash on startup/shutdown." if options.nameOfTreeherderBranch != 'mozilla-inbound' and not options.useTreeherderBinaries: raise Exception('Setting the name of branches only works for treeherder shell bisection.') return options
def findBlamedCset(options, repoDir, testRev): print sps.dateStr() hgPrefix = ['hg', '-R', repoDir] # Resolve names such as "tip", "default", or "52707" to stable hg hash ids, e.g. "9f2641871ce8". realStartRepo = sRepo = hgCmds.getRepoHashAndId(repoDir, repoRev=options.startRepo)[0] realEndRepo = eRepo = hgCmds.getRepoHashAndId(repoDir, repoRev=options.endRepo)[0] sps.vdump("Bisecting in the range " + sRepo + ":" + eRepo) # Refresh source directory (overwrite all local changes) to default tip if required. if options.resetRepoFirst: subprocess.check_call(hgPrefix + ['update', '-C', 'default']) # Throws exit code 255 if purge extension is not enabled in .hgrc: subprocess.check_call(hgPrefix + ['purge', '--all']) # Reset bisect ranges and set skip ranges. sps.captureStdout(hgPrefix + ['bisect', '-r']) if options.skipRevs: sps.captureStdout(hgPrefix + ['bisect', '--skip', options.skipRevs]) labels = {} # Specify `hg bisect` ranges. if options.testInitialRevs: currRev = eRepo # If testInitialRevs mode is set, compile and test the latest rev first. else: labels[sRepo] = ('good', 'assumed start rev is good') labels[eRepo] = ('bad', 'assumed end rev is bad') subprocess.check_call(hgPrefix + ['bisect', '-U', '-g', sRepo]) currRev = hgCmds.getCsetHashFromBisectMsg(fileManipulation.firstLine( sps.captureStdout(hgPrefix + ['bisect', '-U', '-b', eRepo])[0])) iterNum = 1 if options.testInitialRevs: iterNum -= 2 skipCount = 0 blamedRev = None while currRev is not None: startTime = time.time() label = testRev(currRev) labels[currRev] = label if label[0] == 'skip': skipCount += 1 # If we use "skip", we tell hg bisect to do a linear search to get around the skipping. # If the range is large, doing a bisect to find the start and endpoints of compilation # bustage would be faster. 20 total skips being roughly the time that the pair of # bisections would take. if skipCount > 20: print 'Skipped 20 times, stopping autoBisect.' break print label[0] + " (" + label[1] + ") ", if iterNum <= 0: print 'Finished testing the initial boundary revisions...', else: print "Bisecting for the n-th round where n is", iterNum, "and 2^n is", \ str(2**iterNum), "...", (blamedGoodOrBad, blamedRev, currRev, sRepo, eRepo) = \ bisectLabel(hgPrefix, options, label[0], currRev, sRepo, eRepo) if options.testInitialRevs: options.testInitialRevs = False assert currRev is None currRev = sRepo # If options.testInitialRevs is set, test earliest possible rev next. iterNum += 1 endTime = time.time() oneRunTime = endTime - startTime print 'This iteration took %.3f seconds to run.' % oneRunTime if blamedRev is not None: checkBlameParents(repoDir, blamedRev, blamedGoodOrBad, labels, testRev, realStartRepo, realEndRepo) sps.vdump("Resetting bisect") subprocess.check_call(hgPrefix + ['bisect', '-U', '-r']) sps.vdump("Resetting working directory") sps.captureStdout(hgPrefix + ['update', '-C', '-r', 'default'], ignoreStderr=True) hgCmds.destroyPyc(repoDir) print sps.dateStr()