aboutsummaryrefslogtreecommitdiff
path: root/binary_search_tool/run_bisect.py
diff options
context:
space:
mode:
Diffstat (limited to 'binary_search_tool/run_bisect.py')
-rwxr-xr-xbinary_search_tool/run_bisect.py777
1 files changed, 420 insertions, 357 deletions
diff --git a/binary_search_tool/run_bisect.py b/binary_search_tool/run_bisect.py
index 249b9cf5..f54e00e1 100755
--- a/binary_search_tool/run_bisect.py
+++ b/binary_search_tool/run_bisect.py
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""The unified package/object bisecting tool."""
-from __future__ import print_function
import abc
import argparse
@@ -17,293 +16,336 @@ import sys
from binary_search_tool import binary_search_state
from binary_search_tool import common
-
from cros_utils import command_executer
from cros_utils import logger
class Bisector(object, metaclass=abc.ABCMeta):
- """The abstract base class for Bisectors."""
-
- def __init__(self, options, overrides=None):
- """Constructor for Bisector abstract base class
-
- Args:
- options: positional arguments for specific mode (board, remote, etc.)
- overrides: optional dict of overrides for argument defaults
- """
- self.options = options
- self.overrides = overrides
- if not overrides:
- self.overrides = {}
- self.logger = logger.GetLogger()
- self.ce = command_executer.GetCommandExecuter()
-
- def _PrettyPrintArgs(self, args, overrides):
- """Output arguments in a nice, human readable format
-
- Will print and log all arguments for the bisecting tool and make note of
- which arguments have been overridden.
-
- Example output:
- ./run_bisect.py package daisy 172.17.211.184 -I "" -t cros_pkg/my_test.sh
- Performing ChromeOS Package bisection
- Method Config:
- board : daisy
- remote : 172.17.211.184
-
- Bisection Config: (* = overridden)
- get_initial_items : cros_pkg/get_initial_items.sh
- switch_to_good : cros_pkg/switch_to_good.sh
- switch_to_bad : cros_pkg/switch_to_bad.sh
- * test_setup_script :
- * test_script : cros_pkg/my_test.sh
- prune : True
- noincremental : False
- file_args : True
-
- Args:
- args: The args to be given to binary_search_state.Run. This represents
- how the bisection tool will run (with overridden arguments already
- added in).
- overrides: The dict of overriden arguments provided by the user. This is
- provided so the user can be told which arguments were
- overriden and with what value.
- """
- # Output method config (board, remote, etc.)
- options = vars(self.options)
- out = '\nPerforming %s bisection\n' % self.method_name
- out += 'Method Config:\n'
- max_key_len = max([len(str(x)) for x in options.keys()])
- for key in sorted(options):
- val = options[key]
- key_str = str(key).rjust(max_key_len)
- val_str = str(val)
- out += ' %s : %s\n' % (key_str, val_str)
-
- # Output bisection config (scripts, prune, etc.)
- out += '\nBisection Config: (* = overridden)\n'
- max_key_len = max([len(str(x)) for x in args.keys()])
- # Print args in common._ArgsDict order
- args_order = [x['dest'] for x in common.GetArgsDict().values()]
- for key in sorted(args, key=args_order.index):
- val = args[key]
- key_str = str(key).rjust(max_key_len)
- val_str = str(val)
- changed_str = '*' if key in overrides else ' '
-
- out += ' %s %s : %s\n' % (changed_str, key_str, val_str)
-
- out += '\n'
- self.logger.LogOutput(out)
-
- def ArgOverride(self, args, overrides, pretty_print=True):
- """Override arguments based on given overrides and provide nice output
-
- Args:
- args: dict of arguments to be passed to binary_search_state.Run (runs
- dict.update, causing args to be mutated).
- overrides: dict of arguments to update args with
- pretty_print: if True print out args/overrides to user in pretty format
- """
- args.update(overrides)
- if pretty_print:
- self._PrettyPrintArgs(args, overrides)
-
- @abc.abstractmethod
- def PreRun(self):
- pass
-
- @abc.abstractmethod
- def Run(self):
- pass
-
- @abc.abstractmethod
- def PostRun(self):
- pass
+ """The abstract base class for Bisectors."""
+
+ def __init__(self, options, overrides=None):
+ """Constructor for Bisector abstract base class
+
+ Args:
+ options: positional arguments for specific mode (board, remote, etc.)
+ overrides: optional dict of overrides for argument defaults
+ """
+ self.options = options
+ self.overrides = overrides
+ if not overrides:
+ self.overrides = {}
+ self.logger = logger.GetLogger()
+ self.ce = command_executer.GetCommandExecuter()
+
+ def _PrettyPrintArgs(self, args, overrides):
+ """Output arguments in a nice, human readable format
+
+ Will print and log all arguments for the bisecting tool and make note of
+ which arguments have been overridden.
+
+ Example output:
+ ./run_bisect.py package daisy 172.17.211.184 -I "" -t cros_pkg/my_test.sh
+ Performing ChromeOS Package bisection
+ Method Config:
+ board : daisy
+ remote : 172.17.211.184
+
+ Bisection Config: (* = overridden)
+ get_initial_items : cros_pkg/get_initial_items.sh
+ switch_to_good : cros_pkg/switch_to_good.sh
+ switch_to_bad : cros_pkg/switch_to_bad.sh
+ * test_setup_script :
+ * test_script : cros_pkg/my_test.sh
+ prune : True
+ noincremental : False
+ file_args : True
+
+ Args:
+ args: The args to be given to binary_search_state.Run. This represents
+ how the bisection tool will run (with overridden arguments already
+ added in).
+ overrides: The dict of overriden arguments provided by the user. This is
+ provided so the user can be told which arguments were
+ overriden and with what value.
+ """
+ # Output method config (board, remote, etc.)
+ options = vars(self.options)
+ out = "\nPerforming %s bisection\n" % self.method_name
+ out += "Method Config:\n"
+ max_key_len = max([len(str(x)) for x in options.keys()])
+ for key in sorted(options):
+ val = options[key]
+ key_str = str(key).rjust(max_key_len)
+ val_str = str(val)
+ out += " %s : %s\n" % (key_str, val_str)
+
+ # Output bisection config (scripts, prune, etc.)
+ out += "\nBisection Config: (* = overridden)\n"
+ max_key_len = max([len(str(x)) for x in args.keys()])
+ # Print args in common._ArgsDict order
+ args_order = [x["dest"] for x in common.GetArgsDict().values()]
+ for key in sorted(args, key=args_order.index):
+ val = args[key]
+ key_str = str(key).rjust(max_key_len)
+ val_str = str(val)
+ changed_str = "*" if key in overrides else " "
+
+ out += " %s %s : %s\n" % (changed_str, key_str, val_str)
+
+ out += "\n"
+ self.logger.LogOutput(out)
+
+ def ArgOverride(self, args, overrides, pretty_print=True):
+ """Override arguments based on given overrides and provide nice output
+
+ Args:
+ args: dict of arguments to be passed to binary_search_state.Run (runs
+ dict.update, causing args to be mutated).
+ overrides: dict of arguments to update args with
+ pretty_print: if True print out args/overrides to user in pretty format
+ """
+ args.update(overrides)
+ if pretty_print:
+ self._PrettyPrintArgs(args, overrides)
+
+ @abc.abstractmethod
+ def PreRun(self):
+ pass
+
+ @abc.abstractmethod
+ def Run(self):
+ pass
+
+ @abc.abstractmethod
+ def PostRun(self):
+ pass
class BisectPackage(Bisector):
- """The class for package bisection steps."""
-
- cros_pkg_setup = 'cros_pkg/setup.sh'
- cros_pkg_cleanup = 'cros_pkg/%s_cleanup.sh'
-
- def __init__(self, options, overrides):
- super(BisectPackage, self).__init__(options, overrides)
- self.method_name = 'ChromeOS Package'
- self.default_kwargs = {
- 'get_initial_items': 'cros_pkg/get_initial_items.sh',
- 'switch_to_good': 'cros_pkg/switch_to_good.sh',
- 'switch_to_bad': 'cros_pkg/switch_to_bad.sh',
- 'test_setup_script': 'cros_pkg/test_setup.sh',
- 'test_script': 'cros_pkg/interactive_test.sh',
- 'noincremental': False,
- 'prune': True,
- 'file_args': True
- }
- self.setup_cmd = ' '.join(
- (self.cros_pkg_setup, self.options.board, self.options.remote))
- self.ArgOverride(self.default_kwargs, self.overrides)
-
- def PreRun(self):
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- self.setup_cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Package bisector setup failed w/ error %d' % ret)
- return 1
- return 0
-
- def Run(self):
- return binary_search_state.Run(**self.default_kwargs)
-
- def PostRun(self):
- cmd = self.cros_pkg_cleanup % self.options.board
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Package bisector cleanup failed w/ error %d' % ret)
- return 1
-
- self.logger.LogOutput(('Cleanup successful! To restore the bisection '
- 'environment run the following:\n'
- ' cd %s; %s') % (os.getcwd(), self.setup_cmd))
- return 0
+ """The class for package bisection steps."""
+
+ cros_pkg_setup = "cros_pkg/setup.sh"
+ cros_pkg_cleanup = "cros_pkg/%s_cleanup.sh"
+
+ def __init__(self, options, overrides):
+ super(BisectPackage, self).__init__(options, overrides)
+ self.method_name = "ChromeOS Package"
+ self.default_kwargs = {
+ "get_initial_items": "cros_pkg/get_initial_items.sh",
+ "switch_to_good": "cros_pkg/switch_to_good.sh",
+ "switch_to_bad": "cros_pkg/switch_to_bad.sh",
+ "test_setup_script": "cros_pkg/test_setup.sh",
+ "test_script": "cros_pkg/interactive_test.sh",
+ "noincremental": False,
+ "prune": True,
+ "file_args": True,
+ }
+ self.setup_cmd = " ".join(
+ (self.cros_pkg_setup, self.options.board, self.options.remote)
+ )
+ self.ArgOverride(self.default_kwargs, self.overrides)
+
+ def PreRun(self):
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ self.setup_cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Package bisector setup failed w/ error %d" % ret
+ )
+ return 1
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(**self.default_kwargs)
+
+ def PostRun(self):
+ cmd = self.cros_pkg_cleanup % self.options.board
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Package bisector cleanup failed w/ error %d" % ret
+ )
+ return 1
+
+ self.logger.LogOutput(
+ (
+ "Cleanup successful! To restore the bisection "
+ "environment run the following:\n"
+ " cd %s; %s"
+ )
+ % (os.getcwd(), self.setup_cmd)
+ )
+ return 0
class BisectObject(Bisector):
- """The class for object bisection steps."""
-
- sysroot_wrapper_setup = 'sysroot_wrapper/setup.sh'
- sysroot_wrapper_cleanup = 'sysroot_wrapper/cleanup.sh'
-
- def __init__(self, options, overrides):
- super(BisectObject, self).__init__(options, overrides)
- self.method_name = 'ChromeOS Object'
- self.default_kwargs = {
- 'get_initial_items': 'sysroot_wrapper/get_initial_items.sh',
- 'switch_to_good': 'sysroot_wrapper/switch_to_good.sh',
- 'switch_to_bad': 'sysroot_wrapper/switch_to_bad.sh',
- 'test_setup_script': 'sysroot_wrapper/test_setup.sh',
- 'test_script': 'sysroot_wrapper/interactive_test.sh',
- 'noincremental': False,
- 'prune': True,
- 'file_args': True
- }
- self.options = options
- if options.dir:
- os.environ['BISECT_DIR'] = options.dir
- self.options.dir = os.environ.get('BISECT_DIR', '/tmp/sysroot_bisect')
- self.setup_cmd = ' '.join(
- (self.sysroot_wrapper_setup, self.options.board, self.options.remote,
- self.options.package, str(self.options.reboot).lower(),
- shlex.quote(self.options.use_flags)))
-
- self.ArgOverride(self.default_kwargs, overrides)
-
- def PreRun(self):
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- self.setup_cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Object bisector setup failed w/ error %d' % ret)
- return 1
-
- os.environ['BISECT_STAGE'] = 'TRIAGE'
- return 0
-
- def Run(self):
- return binary_search_state.Run(**self.default_kwargs)
-
- def PostRun(self):
- cmd = self.sysroot_wrapper_cleanup
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Object bisector cleanup failed w/ error %d' % ret)
- return 1
- self.logger.LogOutput(('Cleanup successful! To restore the bisection '
- 'environment run the following:\n'
- ' cd %s; %s') % (os.getcwd(), self.setup_cmd))
- return 0
+ """The class for object bisection steps."""
+
+ sysroot_wrapper_setup = "sysroot_wrapper/setup.sh"
+ sysroot_wrapper_cleanup = "sysroot_wrapper/cleanup.sh"
+
+ def __init__(self, options, overrides):
+ super(BisectObject, self).__init__(options, overrides)
+ self.method_name = "ChromeOS Object"
+ self.default_kwargs = {
+ "get_initial_items": "sysroot_wrapper/get_initial_items.sh",
+ "switch_to_good": "sysroot_wrapper/switch_to_good.sh",
+ "switch_to_bad": "sysroot_wrapper/switch_to_bad.sh",
+ "test_setup_script": "sysroot_wrapper/test_setup.sh",
+ "test_script": "sysroot_wrapper/interactive_test.sh",
+ "noincremental": False,
+ "prune": True,
+ "file_args": True,
+ }
+ self.options = options
+ if options.dir:
+ os.environ["BISECT_DIR"] = options.dir
+ self.options.dir = os.environ.get("BISECT_DIR", "/tmp/sysroot_bisect")
+ self.setup_cmd = " ".join(
+ (
+ self.sysroot_wrapper_setup,
+ self.options.board,
+ self.options.remote,
+ self.options.package,
+ str(self.options.reboot).lower(),
+ shlex.quote(self.options.use_flags),
+ )
+ )
+
+ self.ArgOverride(self.default_kwargs, overrides)
+
+ def PreRun(self):
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ self.setup_cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Object bisector setup failed w/ error %d" % ret
+ )
+ return 1
+
+ os.environ["BISECT_STAGE"] = "TRIAGE"
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(**self.default_kwargs)
+
+ def PostRun(self):
+ cmd = self.sysroot_wrapper_cleanup
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Object bisector cleanup failed w/ error %d" % ret
+ )
+ return 1
+ self.logger.LogOutput(
+ (
+ "Cleanup successful! To restore the bisection "
+ "environment run the following:\n"
+ " cd %s; %s"
+ )
+ % (os.getcwd(), self.setup_cmd)
+ )
+ return 0
class BisectAndroid(Bisector):
- """The class for Android bisection steps."""
-
- android_setup = 'android/setup.sh'
- android_cleanup = 'android/cleanup.sh'
- default_dir = os.path.expanduser('~/ANDROID_BISECT')
-
- def __init__(self, options, overrides):
- super(BisectAndroid, self).__init__(options, overrides)
- self.method_name = 'Android'
- self.default_kwargs = {
- 'get_initial_items': 'android/get_initial_items.sh',
- 'switch_to_good': 'android/switch_to_good.sh',
- 'switch_to_bad': 'android/switch_to_bad.sh',
- 'test_setup_script': 'android/test_setup.sh',
- 'test_script': 'android/interactive_test.sh',
- 'prune': True,
- 'file_args': True,
- 'noincremental': False,
- }
- self.options = options
- if options.dir:
- os.environ['BISECT_DIR'] = options.dir
- self.options.dir = os.environ.get('BISECT_DIR', self.default_dir)
-
- num_jobs = "NUM_JOBS='%s'" % self.options.num_jobs
- device_id = ''
- if self.options.device_id:
- device_id = "ANDROID_SERIAL='%s'" % self.options.device_id
-
- self.setup_cmd = ' '.join(
- (num_jobs, device_id, self.android_setup, self.options.android_src))
-
- self.ArgOverride(self.default_kwargs, overrides)
-
- def PreRun(self):
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- self.setup_cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Android bisector setup failed w/ error %d' % ret)
- return 1
-
- os.environ['BISECT_STAGE'] = 'TRIAGE'
- return 0
-
- def Run(self):
- return binary_search_state.Run(**self.default_kwargs)
-
- def PostRun(self):
- cmd = self.android_cleanup
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Android bisector cleanup failed w/ error %d' % ret)
- return 1
- self.logger.LogOutput(('Cleanup successful! To restore the bisection '
- 'environment run the following:\n'
- ' cd %s; %s') % (os.getcwd(), self.setup_cmd))
- return 0
+ """The class for Android bisection steps."""
+
+ android_setup = "android/setup.sh"
+ android_cleanup = "android/cleanup.sh"
+ default_dir = os.path.expanduser("~/ANDROID_BISECT")
+
+ def __init__(self, options, overrides):
+ super(BisectAndroid, self).__init__(options, overrides)
+ self.method_name = "Android"
+ self.default_kwargs = {
+ "get_initial_items": "android/get_initial_items.sh",
+ "switch_to_good": "android/switch_to_good.sh",
+ "switch_to_bad": "android/switch_to_bad.sh",
+ "test_setup_script": "android/test_setup.sh",
+ "test_script": "android/interactive_test.sh",
+ "prune": True,
+ "file_args": True,
+ "noincremental": False,
+ }
+ self.options = options
+ if options.dir:
+ os.environ["BISECT_DIR"] = options.dir
+ self.options.dir = os.environ.get("BISECT_DIR", self.default_dir)
+
+ num_jobs = "NUM_JOBS='%s'" % self.options.num_jobs
+ device_id = ""
+ if self.options.device_id:
+ device_id = "ANDROID_SERIAL='%s'" % self.options.device_id
+
+ self.setup_cmd = " ".join(
+ (num_jobs, device_id, self.android_setup, self.options.android_src)
+ )
+
+ self.ArgOverride(self.default_kwargs, overrides)
+
+ def PreRun(self):
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ self.setup_cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Android bisector setup failed w/ error %d" % ret
+ )
+ return 1
+
+ os.environ["BISECT_STAGE"] = "TRIAGE"
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(**self.default_kwargs)
+
+ def PostRun(self):
+ cmd = self.android_cleanup
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Android bisector cleanup failed w/ error %d" % ret
+ )
+ return 1
+ self.logger.LogOutput(
+ (
+ "Cleanup successful! To restore the bisection "
+ "environment run the following:\n"
+ " cd %s; %s"
+ )
+ % (os.getcwd(), self.setup_cmd)
+ )
+ return 0
def Run(bisector):
- log = logger.GetLogger()
+ log = logger.GetLogger()
- log.LogOutput('Setting up Bisection tool')
- ret = bisector.PreRun()
- if ret:
- return ret
+ log.LogOutput("Setting up Bisection tool")
+ ret = bisector.PreRun()
+ if ret:
+ return ret
- log.LogOutput('Running Bisection tool')
- ret = bisector.Run()
- if ret:
- return ret
+ log.LogOutput("Running Bisection tool")
+ ret = bisector.Run()
+ if ret:
+ return ret
- log.LogOutput('Cleaning up Bisection tool')
- ret = bisector.PostRun()
- if ret:
- return ret
+ log.LogOutput("Cleaning up Bisection tool")
+ ret = bisector.PostRun()
+ if ret:
+ return ret
- return 0
+ return 0
_HELP_EPILOG = """
@@ -318,92 +360,113 @@ See below for full override argument reference:
def Main(argv):
- override_parser = argparse.ArgumentParser(
- add_help=False,
- argument_default=argparse.SUPPRESS,
- usage='run_bisect.py {mode} [options]')
- common.BuildArgParser(override_parser, override=True)
-
- epilog = _HELP_EPILOG + override_parser.format_help()
- parser = argparse.ArgumentParser(
- epilog=epilog, formatter_class=RawTextHelpFormatter)
- subparsers = parser.add_subparsers(
- title='Bisect mode',
- description=('Which bisection method to '
- 'use. Each method has '
- 'specific setup and '
- 'arguments. Please consult '
- 'the README for more '
- 'information.'))
-
- parser_package = subparsers.add_parser('package')
- parser_package.add_argument('board', help='Board to target')
- parser_package.add_argument('remote', help='Remote machine to test on')
- parser_package.set_defaults(handler=BisectPackage)
-
- parser_object = subparsers.add_parser('object')
- parser_object.add_argument('board', help='Board to target')
- parser_object.add_argument('remote', help='Remote machine to test on')
- parser_object.add_argument('package', help='Package to emerge and test')
- parser_object.add_argument(
- '--use_flags',
- required=False,
- default='',
- help='Use flags passed to emerge')
- parser_object.add_argument(
- '--noreboot',
- action='store_false',
- dest='reboot',
- help='Do not reboot after updating the package (default: False)')
- parser_object.add_argument(
- '--dir',
- help=('Bisection directory to use, sets '
- '$BISECT_DIR if provided. Defaults to '
- 'current value of $BISECT_DIR (or '
- '/tmp/sysroot_bisect if $BISECT_DIR is '
- 'empty).'))
- parser_object.set_defaults(handler=BisectObject)
-
- parser_android = subparsers.add_parser('android')
- parser_android.add_argument('android_src', help='Path to android source tree')
- parser_android.add_argument(
- '--dir',
- help=('Bisection directory to use, sets '
- '$BISECT_DIR if provided. Defaults to '
- 'current value of $BISECT_DIR (or '
- '~/ANDROID_BISECT/ if $BISECT_DIR is '
- 'empty).'))
- parser_android.add_argument(
- '-j',
- '--num_jobs',
- type=int,
- default=1,
- help=('Number of jobs that make and various '
- 'scripts for bisector can spawn. Setting '
- 'this value too high can freeze up your '
- 'machine!'))
- parser_android.add_argument(
- '--device_id',
- default='',
- help=('Device id for device used for testing. '
- 'Use this if you have multiple Android '
- 'devices plugged into your machine.'))
- parser_android.set_defaults(handler=BisectAndroid)
-
- options, remaining = parser.parse_known_args(argv)
- if remaining:
- overrides = override_parser.parse_args(remaining)
- overrides = vars(overrides)
- else:
- overrides = {}
-
- subcmd = options.handler
- del options.handler
-
- bisector = subcmd(options, overrides)
- return Run(bisector)
-
-
-if __name__ == '__main__':
- os.chdir(os.path.dirname(__file__))
- sys.exit(Main(sys.argv[1:]))
+ override_parser = argparse.ArgumentParser(
+ add_help=False,
+ argument_default=argparse.SUPPRESS,
+ usage="run_bisect.py {mode} [options]",
+ )
+ common.BuildArgParser(override_parser, override=True)
+
+ epilog = _HELP_EPILOG + override_parser.format_help()
+ parser = argparse.ArgumentParser(
+ epilog=epilog, formatter_class=RawTextHelpFormatter
+ )
+ subparsers = parser.add_subparsers(
+ title="Bisect mode",
+ description=(
+ "Which bisection method to "
+ "use. Each method has "
+ "specific setup and "
+ "arguments. Please consult "
+ "the README for more "
+ "information."
+ ),
+ )
+
+ parser_package = subparsers.add_parser("package")
+ parser_package.add_argument("board", help="Board to target")
+ parser_package.add_argument("remote", help="Remote machine to test on")
+ parser_package.set_defaults(handler=BisectPackage)
+
+ parser_object = subparsers.add_parser("object")
+ parser_object.add_argument("board", help="Board to target")
+ parser_object.add_argument("remote", help="Remote machine to test on")
+ parser_object.add_argument("package", help="Package to emerge and test")
+ parser_object.add_argument(
+ "--use_flags",
+ required=False,
+ default="",
+ help="Use flags passed to emerge",
+ )
+ parser_object.add_argument(
+ "--noreboot",
+ action="store_false",
+ dest="reboot",
+ help="Do not reboot after updating the package (default: False)",
+ )
+ parser_object.add_argument(
+ "--dir",
+ help=(
+ "Bisection directory to use, sets "
+ "$BISECT_DIR if provided. Defaults to "
+ "current value of $BISECT_DIR (or "
+ "/tmp/sysroot_bisect if $BISECT_DIR is "
+ "empty)."
+ ),
+ )
+ parser_object.set_defaults(handler=BisectObject)
+
+ parser_android = subparsers.add_parser("android")
+ parser_android.add_argument(
+ "android_src", help="Path to android source tree"
+ )
+ parser_android.add_argument(
+ "--dir",
+ help=(
+ "Bisection directory to use, sets "
+ "$BISECT_DIR if provided. Defaults to "
+ "current value of $BISECT_DIR (or "
+ "~/ANDROID_BISECT/ if $BISECT_DIR is "
+ "empty)."
+ ),
+ )
+ parser_android.add_argument(
+ "-j",
+ "--num_jobs",
+ type=int,
+ default=1,
+ help=(
+ "Number of jobs that make and various "
+ "scripts for bisector can spawn. Setting "
+ "this value too high can freeze up your "
+ "machine!"
+ ),
+ )
+ parser_android.add_argument(
+ "--device_id",
+ default="",
+ help=(
+ "Device id for device used for testing. "
+ "Use this if you have multiple Android "
+ "devices plugged into your machine."
+ ),
+ )
+ parser_android.set_defaults(handler=BisectAndroid)
+
+ options, remaining = parser.parse_known_args(argv)
+ if remaining:
+ overrides = override_parser.parse_args(remaining)
+ overrides = vars(overrides)
+ else:
+ overrides = {}
+
+ subcmd = options.handler
+ del options.handler
+
+ bisector = subcmd(options, overrides)
+ return Run(bisector)
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(__file__))
+ sys.exit(Main(sys.argv[1:]))