You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
9.2 KiB
324 lines
9.2 KiB
# -*- coding: utf-8 -*-
|
|
# 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.
|
|
|
|
"""Common config and logic for binary search tool
|
|
|
|
This module serves two main purposes:
|
|
1. Programatically include the utils module in PYTHONPATH
|
|
2. Create the argument parsing shared between binary_search_state.py and
|
|
run_bisect.py
|
|
|
|
The argument parsing is handled by populating _ArgsDict with all arguments.
|
|
_ArgsDict is required so that binary_search_state.py and run_bisect.py can
|
|
share the argument parsing, but treat them slightly differently. For example,
|
|
run_bisect.py requires that all argument defaults are suppressed so that
|
|
overriding can occur properly (i.e. only options that are explicitly entered
|
|
by the user end up in the resultant options dictionary).
|
|
|
|
ArgumentDict inherits OrderedDict in order to preserve the order the args are
|
|
created so the help text is made properly.
|
|
"""
|
|
|
|
|
|
import collections
|
|
import os
|
|
import sys
|
|
|
|
|
|
# Programatically adding utils python path to PYTHONPATH
|
|
if os.path.isabs(sys.argv[0]):
|
|
utils_pythonpath = os.path.abspath(
|
|
"{0}/..".format(os.path.dirname(sys.argv[0]))
|
|
)
|
|
else:
|
|
wdir = os.getcwd()
|
|
utils_pythonpath = os.path.abspath(
|
|
"{0}/{1}/..".format(wdir, os.path.dirname(sys.argv[0]))
|
|
)
|
|
sys.path.append(utils_pythonpath)
|
|
|
|
|
|
class ArgumentDict(collections.OrderedDict):
|
|
"""Wrapper around OrderedDict, represents CLI arguments for program.
|
|
|
|
AddArgument enforces the following layout:
|
|
{
|
|
['-n', '--iterations'] : {
|
|
'dest': 'iterations',
|
|
'type': int,
|
|
'help': 'Number of iterations to try in the search.',
|
|
'default': 50
|
|
}
|
|
[arg_name1, arg_name2, ...] : {
|
|
arg_option1 : arg_option_val1,
|
|
...
|
|
},
|
|
...
|
|
}
|
|
"""
|
|
|
|
_POSSIBLE_OPTIONS = [
|
|
"action",
|
|
"nargs",
|
|
"const",
|
|
"default",
|
|
"type",
|
|
"choices",
|
|
"required",
|
|
"help",
|
|
"metavar",
|
|
"dest",
|
|
]
|
|
|
|
def AddArgument(self, *args, **kwargs):
|
|
"""Add argument to ArgsDict, has same signature as argparse.add_argument
|
|
|
|
Emulates the the argparse.add_argument method so the internal OrderedDict
|
|
can be safely and easily populated. Each call to this method will have a 1-1
|
|
corresponding call to argparse.add_argument once BuildArgParser is called.
|
|
|
|
Args:
|
|
*args: The names for the argument (-V, --verbose, etc.)
|
|
**kwargs: The options for the argument, corresponds to the args of
|
|
argparse.add_argument
|
|
|
|
Returns:
|
|
None
|
|
|
|
Raises:
|
|
TypeError: if args is empty or if option in kwargs is not a valid
|
|
option for argparse.add_argument.
|
|
"""
|
|
if not args:
|
|
raise TypeError("Argument needs at least one name")
|
|
|
|
for key in kwargs:
|
|
if key not in self._POSSIBLE_OPTIONS:
|
|
raise TypeError(
|
|
'Invalid option "%s" for argument %s' % (key, args[0])
|
|
)
|
|
|
|
self[args] = kwargs
|
|
|
|
|
|
_ArgsDict = ArgumentDict()
|
|
|
|
|
|
def GetArgsDict():
|
|
"""_ArgsDict singleton method"""
|
|
if not _ArgsDict:
|
|
_BuildArgsDict(_ArgsDict)
|
|
return _ArgsDict
|
|
|
|
|
|
def BuildArgParser(parser, override=False):
|
|
"""Add all arguments from singleton ArgsDict to parser.
|
|
|
|
Will take argparse parser and add all arguments in ArgsDict. Will ignore
|
|
the default and required options if override is set to True.
|
|
|
|
Args:
|
|
parser: type argparse.ArgumentParser, will call add_argument for every item
|
|
in _ArgsDict
|
|
override: True if being called from run_bisect.py. Used to say that default
|
|
and required options are to be ignored
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
ArgsDict = GetArgsDict()
|
|
|
|
# Have no defaults when overriding
|
|
for arg_names, arg_options in ArgsDict.items():
|
|
if override:
|
|
arg_options = arg_options.copy()
|
|
arg_options.pop("default", None)
|
|
arg_options.pop("required", None)
|
|
|
|
parser.add_argument(*arg_names, **arg_options)
|
|
|
|
|
|
def StrToBool(str_in):
|
|
if str_in.lower() in ["true", "t", "1"]:
|
|
return True
|
|
if str_in.lower() in ["false", "f", "0"]:
|
|
return False
|
|
|
|
raise AttributeError("%s is not a valid boolean string" % str_in)
|
|
|
|
|
|
def _BuildArgsDict(args):
|
|
"""Populate ArgumentDict with all arguments"""
|
|
args.AddArgument(
|
|
"-n",
|
|
"--iterations",
|
|
dest="iterations",
|
|
type=int,
|
|
help="Number of iterations to try in the search.",
|
|
default=50,
|
|
)
|
|
args.AddArgument(
|
|
"-i",
|
|
"--get_initial_items",
|
|
dest="get_initial_items",
|
|
help="Script to run to get the initial objects. "
|
|
"If your script requires user input "
|
|
"the --verbose option must be used",
|
|
)
|
|
args.AddArgument(
|
|
"-g",
|
|
"--switch_to_good",
|
|
dest="switch_to_good",
|
|
help="Script to run to switch to good. "
|
|
"If your switch script requires user input "
|
|
"the --verbose option must be used",
|
|
)
|
|
args.AddArgument(
|
|
"-b",
|
|
"--switch_to_bad",
|
|
dest="switch_to_bad",
|
|
help="Script to run to switch to bad. "
|
|
"If your switch script requires user input "
|
|
"the --verbose option must be used",
|
|
)
|
|
args.AddArgument(
|
|
"-I",
|
|
"--test_setup_script",
|
|
dest="test_setup_script",
|
|
help="Optional script to perform building, flashing, "
|
|
"and other setup before the test script runs.",
|
|
)
|
|
args.AddArgument(
|
|
"-t",
|
|
"--test_script",
|
|
dest="test_script",
|
|
help="Script to run to test the " "output after packages are built.",
|
|
)
|
|
# No input (evals to False),
|
|
# --prune (evals to True),
|
|
# --prune=False,
|
|
# --prune=True
|
|
args.AddArgument(
|
|
"-p",
|
|
"--prune",
|
|
dest="prune",
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
type=StrToBool,
|
|
metavar="bool",
|
|
help="If True, continue until all bad items are found. "
|
|
"Defaults to False.",
|
|
)
|
|
args.AddArgument(
|
|
"-P",
|
|
"--pass_bisect",
|
|
dest="pass_bisect",
|
|
default=None,
|
|
help="Script to generate another script for pass level bisect, "
|
|
"which contains command line options to build bad item. "
|
|
"This will also turn on pass/transformation level bisection. "
|
|
"Needs support of `-opt-bisect-limit`(pass) and "
|
|
"`-print-debug-counter`(transformation) from LLVM. "
|
|
"For now it only supports one single bad item, so to use it, "
|
|
"prune must be set to False.",
|
|
)
|
|
# No input (evals to False),
|
|
# --ir_diff (evals to True),
|
|
# --ir_diff=False,
|
|
# --ir_diff=True
|
|
args.AddArgument(
|
|
"-d",
|
|
"--ir_diff",
|
|
dest="ir_diff",
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
type=StrToBool,
|
|
metavar="bool",
|
|
help="Whether to print IR differences before and after bad "
|
|
"pass/transformation to verbose output. Defaults to False, "
|
|
"only works when pass_bisect is enabled.",
|
|
)
|
|
# No input (evals to False),
|
|
# --noincremental (evals to True),
|
|
# --noincremental=False,
|
|
# --noincremental=True
|
|
args.AddArgument(
|
|
"-c",
|
|
"--noincremental",
|
|
dest="noincremental",
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
type=StrToBool,
|
|
metavar="bool",
|
|
help="If True, don't propagate good/bad changes "
|
|
"incrementally. Defaults to False.",
|
|
)
|
|
# No input (evals to False),
|
|
# --file_args (evals to True),
|
|
# --file_args=False,
|
|
# --file_args=True
|
|
args.AddArgument(
|
|
"-f",
|
|
"--file_args",
|
|
dest="file_args",
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
type=StrToBool,
|
|
metavar="bool",
|
|
help="Whether to use a file to pass arguments to scripts. "
|
|
"Defaults to False.",
|
|
)
|
|
# No input (evals to True),
|
|
# --verify (evals to True),
|
|
# --verify=False,
|
|
# --verify=True
|
|
args.AddArgument(
|
|
"--verify",
|
|
dest="verify",
|
|
nargs="?",
|
|
const=True,
|
|
default=True,
|
|
type=StrToBool,
|
|
metavar="bool",
|
|
help="Whether to run verify iterations before searching. "
|
|
"Defaults to True.",
|
|
)
|
|
args.AddArgument(
|
|
"-N",
|
|
"--prune_iterations",
|
|
dest="prune_iterations",
|
|
type=int,
|
|
help="Number of prune iterations to try in the search.",
|
|
default=100,
|
|
)
|
|
# No input (evals to False),
|
|
# --verbose (evals to True),
|
|
# --verbose=False,
|
|
# --verbose=True
|
|
args.AddArgument(
|
|
"-V",
|
|
"--verbose",
|
|
dest="verbose",
|
|
nargs="?",
|
|
const=True,
|
|
default=False,
|
|
type=StrToBool,
|
|
metavar="bool",
|
|
help="If True, print full output to console.",
|
|
)
|
|
args.AddArgument(
|
|
"-r",
|
|
"--resume",
|
|
dest="resume",
|
|
action="store_true",
|
|
help="Resume bisection tool execution from state file."
|
|
"Useful if the last bisection was terminated "
|
|
"before it could properly finish.",
|
|
)
|