xref: /aosp_15_r20/external/crosvm/tools/impl/cli.py (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2023 The ChromiumOS Authors
3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file.
5*bb4ee6a4SAndroid Build Coastguard Worker
6*bb4ee6a4SAndroid Build Coastguard Worker"""
7*bb4ee6a4SAndroid Build Coastguard WorkerProvides a framework for command line interfaces based on argh.
8*bb4ee6a4SAndroid Build Coastguard Worker
9*bb4ee6a4SAndroid Build Coastguard WorkerIt automatically adds common arguments, such as -v, -vv and --color to provide consistent
10*bb4ee6a4SAndroid Build Coastguard Workerbehavior.
11*bb4ee6a4SAndroid Build Coastguard Worker"""
12*bb4ee6a4SAndroid Build Coastguard Worker
13*bb4ee6a4SAndroid Build Coastguard Workerimport argparse
14*bb4ee6a4SAndroid Build Coastguard Workerimport sys
15*bb4ee6a4SAndroid Build Coastguard Workerimport traceback
16*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import (
17*bb4ee6a4SAndroid Build Coastguard Worker    Any,
18*bb4ee6a4SAndroid Build Coastguard Worker    Callable,
19*bb4ee6a4SAndroid Build Coastguard Worker    Optional,
20*bb4ee6a4SAndroid Build Coastguard Worker)
21*bb4ee6a4SAndroid Build Coastguard Worker
22*bb4ee6a4SAndroid Build Coastguard Workerfrom .util import (
23*bb4ee6a4SAndroid Build Coastguard Worker    add_common_args,
24*bb4ee6a4SAndroid Build Coastguard Worker    parse_common_args,
25*bb4ee6a4SAndroid Build Coastguard Worker    print_timing_info,
26*bb4ee6a4SAndroid Build Coastguard Worker    record_time,
27*bb4ee6a4SAndroid Build Coastguard Worker    verbose,
28*bb4ee6a4SAndroid Build Coastguard Worker    ensure_packages_exist,
29*bb4ee6a4SAndroid Build Coastguard Worker)
30*bb4ee6a4SAndroid Build Coastguard Worker
31*bb4ee6a4SAndroid Build Coastguard Workerensure_packages_exist("argh")
32*bb4ee6a4SAndroid Build Coastguard Workerimport argh  # type: ignore
33*bb4ee6a4SAndroid Build Coastguard Worker
34*bb4ee6a4SAndroid Build Coastguard Worker# Hack: argh does not support type annotations. This prevents type errors.
35*bb4ee6a4SAndroid Build Coastguard Workerargh: Any  # type: ignore
36*bb4ee6a4SAndroid Build Coastguard Worker
37*bb4ee6a4SAndroid Build Coastguard Worker
38*bb4ee6a4SAndroid Build Coastguard Workerdef run_main(main_fn: Callable[..., Any], usage: Optional[str] = None):
39*bb4ee6a4SAndroid Build Coastguard Worker    run_commands(default_fn=main_fn, usage=usage)
40*bb4ee6a4SAndroid Build Coastguard Worker
41*bb4ee6a4SAndroid Build Coastguard Worker
42*bb4ee6a4SAndroid Build Coastguard Workerdef run_commands(
43*bb4ee6a4SAndroid Build Coastguard Worker    *functions: Callable[..., Any],
44*bb4ee6a4SAndroid Build Coastguard Worker    default_fn: Optional[Callable[..., Any]] = None,
45*bb4ee6a4SAndroid Build Coastguard Worker    usage: Optional[str] = None,
46*bb4ee6a4SAndroid Build Coastguard Worker):
47*bb4ee6a4SAndroid Build Coastguard Worker    """
48*bb4ee6a4SAndroid Build Coastguard Worker    Allow the user to call the provided functions with command line arguments translated to
49*bb4ee6a4SAndroid Build Coastguard Worker    function arguments via argh: https://pythonhosted.org/argh
50*bb4ee6a4SAndroid Build Coastguard Worker    """
51*bb4ee6a4SAndroid Build Coastguard Worker    exit_code = 0
52*bb4ee6a4SAndroid Build Coastguard Worker    try:
53*bb4ee6a4SAndroid Build Coastguard Worker        parser = argparse.ArgumentParser(
54*bb4ee6a4SAndroid Build Coastguard Worker            description=usage,
55*bb4ee6a4SAndroid Build Coastguard Worker            # Docstrings are used as the description in argparse, preserve their formatting.
56*bb4ee6a4SAndroid Build Coastguard Worker            formatter_class=argparse.RawDescriptionHelpFormatter,
57*bb4ee6a4SAndroid Build Coastguard Worker            # Do not allow implied abbreviations. Abbreviations should be manually specified.
58*bb4ee6a4SAndroid Build Coastguard Worker            allow_abbrev=False,
59*bb4ee6a4SAndroid Build Coastguard Worker        )
60*bb4ee6a4SAndroid Build Coastguard Worker        add_common_args(parser)
61*bb4ee6a4SAndroid Build Coastguard Worker
62*bb4ee6a4SAndroid Build Coastguard Worker        # Add provided commands to parser. Do not use sub-commands if we just got one function.
63*bb4ee6a4SAndroid Build Coastguard Worker        if functions:
64*bb4ee6a4SAndroid Build Coastguard Worker            argh.add_commands(parser, functions)  # type: ignore
65*bb4ee6a4SAndroid Build Coastguard Worker        if default_fn:
66*bb4ee6a4SAndroid Build Coastguard Worker            argh.set_default_command(parser, default_fn)  # type: ignore
67*bb4ee6a4SAndroid Build Coastguard Worker
68*bb4ee6a4SAndroid Build Coastguard Worker        with record_time("Total Time"):
69*bb4ee6a4SAndroid Build Coastguard Worker            # Call main method
70*bb4ee6a4SAndroid Build Coastguard Worker            argh.dispatch(parser)  # type: ignore
71*bb4ee6a4SAndroid Build Coastguard Worker
72*bb4ee6a4SAndroid Build Coastguard Worker    except Exception as e:
73*bb4ee6a4SAndroid Build Coastguard Worker        if verbose():
74*bb4ee6a4SAndroid Build Coastguard Worker            traceback.print_exc()
75*bb4ee6a4SAndroid Build Coastguard Worker        else:
76*bb4ee6a4SAndroid Build Coastguard Worker            print(e)
77*bb4ee6a4SAndroid Build Coastguard Worker        exit_code = 1
78*bb4ee6a4SAndroid Build Coastguard Worker
79*bb4ee6a4SAndroid Build Coastguard Worker    if parse_common_args().timing_info:
80*bb4ee6a4SAndroid Build Coastguard Worker        print_timing_info()
81*bb4ee6a4SAndroid Build Coastguard Worker
82*bb4ee6a4SAndroid Build Coastguard Worker    sys.exit(exit_code)
83*bb4ee6a4SAndroid Build Coastguard Worker
84*bb4ee6a4SAndroid Build Coastguard Worker
85*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__":
86*bb4ee6a4SAndroid Build Coastguard Worker    import doctest
87*bb4ee6a4SAndroid Build Coastguard Worker
88*bb4ee6a4SAndroid Build Coastguard Worker    (failures, num_tests) = doctest.testmod(optionflags=doctest.ELLIPSIS)
89*bb4ee6a4SAndroid Build Coastguard Worker    sys.exit(1 if failures > 0 else 0)
90