bisect.py revision bfe9c5300fca09e8790ed25b41028cba8cb0a78f
1#!/usr/bin/python2
2"""The unified package/object bisecting tool."""
3
4from __future__ import print_function
5
6import abc
7import argparse
8import sys
9from argparse import RawTextHelpFormatter
10
11import common
12
13from utils import command_executer
14from utils import logger
15
16import binary_search_state
17
18
19class Bisector(object):
20  """The abstract base class for Bisectors."""
21
22  # Make Bisector an abstract class
23  __metaclass__ = abc.ABCMeta
24
25  def __init__(self, options, overrides=None):
26    """Constructor for Bisector abstract base class
27
28    Args:
29      options: positional arguments for specific mode (board, remote, etc.)
30      overrides: optional dict of overrides for argument defaults
31    """
32    self.options = options
33    self.overrides = overrides
34    if not overrides:
35      self.overrides = {}
36    self.logger = logger.GetLogger()
37    self.ce = command_executer.GetCommandExecuter()
38
39  @abc.abstractmethod
40  def PreRun(self):
41    pass
42
43  @abc.abstractmethod
44  def Run(self):
45    pass
46
47  @abc.abstractmethod
48  def PostRun(self):
49    pass
50
51
52class BisectPackage(Bisector):
53  """The class for package bisection steps."""
54
55  cros_pkg_setup = 'cros_pkg/setup.sh'
56  cros_pkg_cleanup = 'cros_pkg/%s_cleanup.sh'
57
58  def __init__(self, options, overrides):
59    super(BisectPackage, self).__init__(options, overrides)
60    self.default_kwargs = {
61        'get_initial_items': 'cros_pkg/get_initial_items.sh',
62        'switch_to_good': 'cros_pkg/switch_to_good.sh',
63        'switch_to_bad': 'cros_pkg/switch_to_bad.sh',
64        'install_script': 'cros_pkg/install.sh',
65        'test_script': 'cros_pkg/interactive_test.sh',
66        'noincremental': False,
67        'prune': True,
68        'file_args': True
69    }
70
71  def PreRun(self):
72    cmd = ('%s %s %s' %
73           (self.cros_pkg_setup, self.options.board, self.options.remote))
74    ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
75    if ret:
76      self.logger.LogError('Package bisector setup failed w/ error %d' % ret)
77      return 1
78    return 0
79
80  def Run(self):
81    self.default_kwargs.update(self.overrides)
82    return binary_search_state.Run(**self.default_kwargs)
83
84  def PostRun(self):
85    cmd = self.cros_pkg_cleanup % self.options.board
86    ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
87    if ret:
88      self.logger.LogError('Package bisector cleanup failed w/ error %d' % ret)
89      return 1
90    return 0
91
92
93class BisectObject(Bisector):
94  """The class for object bisection steps."""
95
96  def __init__(self, options, overrides):
97    super(BisectObject, self).__init__(options, overrides)
98
99  def PreRun(self):
100    raise NotImplementedError('Object bisecting still WIP')
101
102  def Run(self):
103    return 1
104
105  def PostRun(self):
106    return 1
107
108
109def Run(bisector):
110  ret = bisector.PreRun()
111  if ret:
112    return ret
113
114  ret = bisector.Run()
115  if ret:
116    return ret
117
118  ret = bisector.PostRun()
119  if ret:
120    return ret
121
122  return 0
123
124
125_HELP_EPILOG = """
126Run ./bisect.py {method} --help for individual method help/args
127
128------------------
129
130See README.bisect for examples on argument overriding
131
132See below for full override argument reference:
133"""
134
135
136def Main(argv):
137  override_parser = argparse.ArgumentParser(add_help=False,
138                                            argument_default=argparse.SUPPRESS,
139                                            usage='bisect.py {mode} [options]')
140  common.BuildArgParser(override_parser, override=True)
141
142  epilog = _HELP_EPILOG + override_parser.format_help()
143  parser = argparse.ArgumentParser(epilog=epilog,
144                                   formatter_class=RawTextHelpFormatter)
145  subparsers = parser.add_subparsers(title='Bisect mode',
146                                     description=('Which bisection method to '
147                                                  'use. Each method has '
148                                                  'specific setup and '
149                                                  'arguments. Please consult '
150                                                  'the README for more '
151                                                  'information.'))
152
153  parser_package = subparsers.add_parser('package')
154  parser_package.add_argument('board', help='Board to target')
155  parser_package.add_argument('remote', help='Remote machine to test on')
156  parser_package.set_defaults(handler=BisectPackage)
157
158  parser_object = subparsers.add_parser('object')
159  parser_object.set_defaults(handler=BisectObject)
160
161  options, remaining = parser.parse_known_args(argv)
162  if remaining:
163    overrides = override_parser.parse_args(remaining)
164    overrides = vars(overrides)
165  else:
166    overrides = {}
167
168  subcmd = options.handler
169  del options.handler
170
171  bisector = subcmd(options, overrides)
172  return Run(bisector)
173
174
175if __name__ == '__main__':
176  os.chdir(os.path.dirname(__file__))
177  sys.exit(Main(sys.argv[1:]))
178