xref: /aosp_15_r20/external/toolchain-utils/binary_search_tool/common.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li# -*- coding: utf-8 -*-
2*760c253cSXin Li# Copyright 2020 The ChromiumOS Authors
3*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be
4*760c253cSXin Li# found in the LICENSE file.
5*760c253cSXin Li
6*760c253cSXin Li"""Common config and logic for binary search tool
7*760c253cSXin Li
8*760c253cSXin LiThis module serves two main purposes:
9*760c253cSXin Li  1. Programatically include the utils module in PYTHONPATH
10*760c253cSXin Li  2. Create the argument parsing shared between binary_search_state.py and
11*760c253cSXin Li     run_bisect.py
12*760c253cSXin Li
13*760c253cSXin LiThe argument parsing is handled by populating _ArgsDict with all arguments.
14*760c253cSXin Li_ArgsDict is required so that binary_search_state.py and run_bisect.py can
15*760c253cSXin Lishare the argument parsing, but treat them slightly differently. For example,
16*760c253cSXin Lirun_bisect.py requires that all argument defaults are suppressed so that
17*760c253cSXin Lioverriding can occur properly (i.e. only options that are explicitly entered
18*760c253cSXin Liby the user end up in the resultant options dictionary).
19*760c253cSXin Li
20*760c253cSXin LiArgumentDict inherits OrderedDict in order to preserve the order the args are
21*760c253cSXin Licreated so the help text is made properly.
22*760c253cSXin Li"""
23*760c253cSXin Li
24*760c253cSXin Li
25*760c253cSXin Liimport collections
26*760c253cSXin Liimport os
27*760c253cSXin Liimport sys
28*760c253cSXin Li
29*760c253cSXin Li
30*760c253cSXin Li# Programatically adding utils python path to PYTHONPATH
31*760c253cSXin Liif os.path.isabs(sys.argv[0]):
32*760c253cSXin Li    utils_pythonpath = os.path.abspath(
33*760c253cSXin Li        "{0}/..".format(os.path.dirname(sys.argv[0]))
34*760c253cSXin Li    )
35*760c253cSXin Lielse:
36*760c253cSXin Li    wdir = os.getcwd()
37*760c253cSXin Li    utils_pythonpath = os.path.abspath(
38*760c253cSXin Li        "{0}/{1}/..".format(wdir, os.path.dirname(sys.argv[0]))
39*760c253cSXin Li    )
40*760c253cSXin Lisys.path.append(utils_pythonpath)
41*760c253cSXin Li
42*760c253cSXin Li
43*760c253cSXin Liclass ArgumentDict(collections.OrderedDict):
44*760c253cSXin Li    """Wrapper around OrderedDict, represents CLI arguments for program.
45*760c253cSXin Li
46*760c253cSXin Li    AddArgument enforces the following layout:
47*760c253cSXin Li    {
48*760c253cSXin Li        ['-n', '--iterations'] : {
49*760c253cSXin Li            'dest': 'iterations',
50*760c253cSXin Li            'type': int,
51*760c253cSXin Li            'help': 'Number of iterations to try in the search.',
52*760c253cSXin Li            'default': 50
53*760c253cSXin Li        }
54*760c253cSXin Li        [arg_name1, arg_name2, ...] : {
55*760c253cSXin Li            arg_option1 : arg_option_val1,
56*760c253cSXin Li            ...
57*760c253cSXin Li        },
58*760c253cSXin Li        ...
59*760c253cSXin Li    }
60*760c253cSXin Li    """
61*760c253cSXin Li
62*760c253cSXin Li    _POSSIBLE_OPTIONS = [
63*760c253cSXin Li        "action",
64*760c253cSXin Li        "nargs",
65*760c253cSXin Li        "const",
66*760c253cSXin Li        "default",
67*760c253cSXin Li        "type",
68*760c253cSXin Li        "choices",
69*760c253cSXin Li        "required",
70*760c253cSXin Li        "help",
71*760c253cSXin Li        "metavar",
72*760c253cSXin Li        "dest",
73*760c253cSXin Li    ]
74*760c253cSXin Li
75*760c253cSXin Li    def AddArgument(self, *args, **kwargs):
76*760c253cSXin Li        """Add argument to ArgsDict, has same signature as argparse.add_argument
77*760c253cSXin Li
78*760c253cSXin Li        Emulates the the argparse.add_argument method so the internal OrderedDict
79*760c253cSXin Li        can be safely and easily populated. Each call to this method will have a 1-1
80*760c253cSXin Li        corresponding call to argparse.add_argument once BuildArgParser is called.
81*760c253cSXin Li
82*760c253cSXin Li        Args:
83*760c253cSXin Li          *args: The names for the argument (-V, --verbose, etc.)
84*760c253cSXin Li          **kwargs: The options for the argument, corresponds to the args of
85*760c253cSXin Li                    argparse.add_argument
86*760c253cSXin Li
87*760c253cSXin Li        Returns:
88*760c253cSXin Li          None
89*760c253cSXin Li
90*760c253cSXin Li        Raises:
91*760c253cSXin Li          TypeError: if args is empty or if option in kwargs is not a valid
92*760c253cSXin Li                     option for argparse.add_argument.
93*760c253cSXin Li        """
94*760c253cSXin Li        if not args:
95*760c253cSXin Li            raise TypeError("Argument needs at least one name")
96*760c253cSXin Li
97*760c253cSXin Li        for key in kwargs:
98*760c253cSXin Li            if key not in self._POSSIBLE_OPTIONS:
99*760c253cSXin Li                raise TypeError(
100*760c253cSXin Li                    'Invalid option "%s" for argument %s' % (key, args[0])
101*760c253cSXin Li                )
102*760c253cSXin Li
103*760c253cSXin Li        self[args] = kwargs
104*760c253cSXin Li
105*760c253cSXin Li
106*760c253cSXin Li_ArgsDict = ArgumentDict()
107*760c253cSXin Li
108*760c253cSXin Li
109*760c253cSXin Lidef GetArgsDict():
110*760c253cSXin Li    """_ArgsDict singleton method"""
111*760c253cSXin Li    if not _ArgsDict:
112*760c253cSXin Li        _BuildArgsDict(_ArgsDict)
113*760c253cSXin Li    return _ArgsDict
114*760c253cSXin Li
115*760c253cSXin Li
116*760c253cSXin Lidef BuildArgParser(parser, override=False):
117*760c253cSXin Li    """Add all arguments from singleton ArgsDict to parser.
118*760c253cSXin Li
119*760c253cSXin Li    Will take argparse parser and add all arguments in ArgsDict. Will ignore
120*760c253cSXin Li    the default and required options if override is set to True.
121*760c253cSXin Li
122*760c253cSXin Li    Args:
123*760c253cSXin Li      parser: type argparse.ArgumentParser, will call add_argument for every item
124*760c253cSXin Li              in _ArgsDict
125*760c253cSXin Li      override: True if being called from run_bisect.py. Used to say that default
126*760c253cSXin Li                and required options are to be ignored
127*760c253cSXin Li
128*760c253cSXin Li    Returns:
129*760c253cSXin Li      None
130*760c253cSXin Li    """
131*760c253cSXin Li    ArgsDict = GetArgsDict()
132*760c253cSXin Li
133*760c253cSXin Li    # Have no defaults when overriding
134*760c253cSXin Li    for arg_names, arg_options in ArgsDict.items():
135*760c253cSXin Li        if override:
136*760c253cSXin Li            arg_options = arg_options.copy()
137*760c253cSXin Li            arg_options.pop("default", None)
138*760c253cSXin Li            arg_options.pop("required", None)
139*760c253cSXin Li
140*760c253cSXin Li        parser.add_argument(*arg_names, **arg_options)
141*760c253cSXin Li
142*760c253cSXin Li
143*760c253cSXin Lidef StrToBool(str_in):
144*760c253cSXin Li    if str_in.lower() in ["true", "t", "1"]:
145*760c253cSXin Li        return True
146*760c253cSXin Li    if str_in.lower() in ["false", "f", "0"]:
147*760c253cSXin Li        return False
148*760c253cSXin Li
149*760c253cSXin Li    raise AttributeError("%s is not a valid boolean string" % str_in)
150*760c253cSXin Li
151*760c253cSXin Li
152*760c253cSXin Lidef _BuildArgsDict(args):
153*760c253cSXin Li    """Populate ArgumentDict with all arguments"""
154*760c253cSXin Li    args.AddArgument(
155*760c253cSXin Li        "-n",
156*760c253cSXin Li        "--iterations",
157*760c253cSXin Li        dest="iterations",
158*760c253cSXin Li        type=int,
159*760c253cSXin Li        help="Number of iterations to try in the search.",
160*760c253cSXin Li        default=50,
161*760c253cSXin Li    )
162*760c253cSXin Li    args.AddArgument(
163*760c253cSXin Li        "-i",
164*760c253cSXin Li        "--get_initial_items",
165*760c253cSXin Li        dest="get_initial_items",
166*760c253cSXin Li        help="Script to run to get the initial objects. "
167*760c253cSXin Li        "If your script requires user input "
168*760c253cSXin Li        "the --verbose option must be used",
169*760c253cSXin Li    )
170*760c253cSXin Li    args.AddArgument(
171*760c253cSXin Li        "-g",
172*760c253cSXin Li        "--switch_to_good",
173*760c253cSXin Li        dest="switch_to_good",
174*760c253cSXin Li        help="Script to run to switch to good. "
175*760c253cSXin Li        "If your switch script requires user input "
176*760c253cSXin Li        "the --verbose option must be used",
177*760c253cSXin Li    )
178*760c253cSXin Li    args.AddArgument(
179*760c253cSXin Li        "-b",
180*760c253cSXin Li        "--switch_to_bad",
181*760c253cSXin Li        dest="switch_to_bad",
182*760c253cSXin Li        help="Script to run to switch to bad. "
183*760c253cSXin Li        "If your switch script requires user input "
184*760c253cSXin Li        "the --verbose option must be used",
185*760c253cSXin Li    )
186*760c253cSXin Li    args.AddArgument(
187*760c253cSXin Li        "-I",
188*760c253cSXin Li        "--test_setup_script",
189*760c253cSXin Li        dest="test_setup_script",
190*760c253cSXin Li        help="Optional script to perform building, flashing, "
191*760c253cSXin Li        "and other setup before the test script runs.",
192*760c253cSXin Li    )
193*760c253cSXin Li    args.AddArgument(
194*760c253cSXin Li        "-t",
195*760c253cSXin Li        "--test_script",
196*760c253cSXin Li        dest="test_script",
197*760c253cSXin Li        help="Script to run to test the " "output after packages are built.",
198*760c253cSXin Li    )
199*760c253cSXin Li    # No input (evals to False),
200*760c253cSXin Li    # --prune (evals to True),
201*760c253cSXin Li    # --prune=False,
202*760c253cSXin Li    # --prune=True
203*760c253cSXin Li    args.AddArgument(
204*760c253cSXin Li        "-p",
205*760c253cSXin Li        "--prune",
206*760c253cSXin Li        dest="prune",
207*760c253cSXin Li        nargs="?",
208*760c253cSXin Li        const=True,
209*760c253cSXin Li        default=False,
210*760c253cSXin Li        type=StrToBool,
211*760c253cSXin Li        metavar="bool",
212*760c253cSXin Li        help="If True, continue until all bad items are found. "
213*760c253cSXin Li        "Defaults to False.",
214*760c253cSXin Li    )
215*760c253cSXin Li    args.AddArgument(
216*760c253cSXin Li        "-P",
217*760c253cSXin Li        "--pass_bisect",
218*760c253cSXin Li        dest="pass_bisect",
219*760c253cSXin Li        default=None,
220*760c253cSXin Li        help="Script to generate another script for pass level bisect, "
221*760c253cSXin Li        "which contains command line options to build bad item. "
222*760c253cSXin Li        "This will also turn on pass/transformation level bisection. "
223*760c253cSXin Li        "Needs support of `-opt-bisect-limit`(pass) and "
224*760c253cSXin Li        "`-print-debug-counter`(transformation) from LLVM. "
225*760c253cSXin Li        "For now it only supports one single bad item, so to use it, "
226*760c253cSXin Li        "prune must be set to False.",
227*760c253cSXin Li    )
228*760c253cSXin Li    # No input (evals to False),
229*760c253cSXin Li    # --ir_diff (evals to True),
230*760c253cSXin Li    # --ir_diff=False,
231*760c253cSXin Li    # --ir_diff=True
232*760c253cSXin Li    args.AddArgument(
233*760c253cSXin Li        "-d",
234*760c253cSXin Li        "--ir_diff",
235*760c253cSXin Li        dest="ir_diff",
236*760c253cSXin Li        nargs="?",
237*760c253cSXin Li        const=True,
238*760c253cSXin Li        default=False,
239*760c253cSXin Li        type=StrToBool,
240*760c253cSXin Li        metavar="bool",
241*760c253cSXin Li        help="Whether to print IR differences before and after bad "
242*760c253cSXin Li        "pass/transformation to verbose output. Defaults to False, "
243*760c253cSXin Li        "only works when pass_bisect is enabled.",
244*760c253cSXin Li    )
245*760c253cSXin Li    # No input (evals to False),
246*760c253cSXin Li    # --noincremental (evals to True),
247*760c253cSXin Li    # --noincremental=False,
248*760c253cSXin Li    # --noincremental=True
249*760c253cSXin Li    args.AddArgument(
250*760c253cSXin Li        "-c",
251*760c253cSXin Li        "--noincremental",
252*760c253cSXin Li        dest="noincremental",
253*760c253cSXin Li        nargs="?",
254*760c253cSXin Li        const=True,
255*760c253cSXin Li        default=False,
256*760c253cSXin Li        type=StrToBool,
257*760c253cSXin Li        metavar="bool",
258*760c253cSXin Li        help="If True, don't propagate good/bad changes "
259*760c253cSXin Li        "incrementally. Defaults to False.",
260*760c253cSXin Li    )
261*760c253cSXin Li    # No input (evals to False),
262*760c253cSXin Li    # --file_args (evals to True),
263*760c253cSXin Li    # --file_args=False,
264*760c253cSXin Li    # --file_args=True
265*760c253cSXin Li    args.AddArgument(
266*760c253cSXin Li        "-f",
267*760c253cSXin Li        "--file_args",
268*760c253cSXin Li        dest="file_args",
269*760c253cSXin Li        nargs="?",
270*760c253cSXin Li        const=True,
271*760c253cSXin Li        default=False,
272*760c253cSXin Li        type=StrToBool,
273*760c253cSXin Li        metavar="bool",
274*760c253cSXin Li        help="Whether to use a file to pass arguments to scripts. "
275*760c253cSXin Li        "Defaults to False.",
276*760c253cSXin Li    )
277*760c253cSXin Li    # No input (evals to True),
278*760c253cSXin Li    # --verify (evals to True),
279*760c253cSXin Li    # --verify=False,
280*760c253cSXin Li    # --verify=True
281*760c253cSXin Li    args.AddArgument(
282*760c253cSXin Li        "--verify",
283*760c253cSXin Li        dest="verify",
284*760c253cSXin Li        nargs="?",
285*760c253cSXin Li        const=True,
286*760c253cSXin Li        default=True,
287*760c253cSXin Li        type=StrToBool,
288*760c253cSXin Li        metavar="bool",
289*760c253cSXin Li        help="Whether to run verify iterations before searching. "
290*760c253cSXin Li        "Defaults to True.",
291*760c253cSXin Li    )
292*760c253cSXin Li    args.AddArgument(
293*760c253cSXin Li        "-N",
294*760c253cSXin Li        "--prune_iterations",
295*760c253cSXin Li        dest="prune_iterations",
296*760c253cSXin Li        type=int,
297*760c253cSXin Li        help="Number of prune iterations to try in the search.",
298*760c253cSXin Li        default=100,
299*760c253cSXin Li    )
300*760c253cSXin Li    # No input (evals to False),
301*760c253cSXin Li    # --verbose (evals to True),
302*760c253cSXin Li    # --verbose=False,
303*760c253cSXin Li    # --verbose=True
304*760c253cSXin Li    args.AddArgument(
305*760c253cSXin Li        "-V",
306*760c253cSXin Li        "--verbose",
307*760c253cSXin Li        dest="verbose",
308*760c253cSXin Li        nargs="?",
309*760c253cSXin Li        const=True,
310*760c253cSXin Li        default=False,
311*760c253cSXin Li        type=StrToBool,
312*760c253cSXin Li        metavar="bool",
313*760c253cSXin Li        help="If True, print full output to console.",
314*760c253cSXin Li    )
315*760c253cSXin Li    args.AddArgument(
316*760c253cSXin Li        "-r",
317*760c253cSXin Li        "--resume",
318*760c253cSXin Li        dest="resume",
319*760c253cSXin Li        action="store_true",
320*760c253cSXin Li        help="Resume bisection tool execution from state file."
321*760c253cSXin Li        "Useful if the last bisection was terminated "
322*760c253cSXin Li        "before it could properly finish.",
323*760c253cSXin Li    )
324