1# DExTer : Debugging Experience Tester
2# ~~~~~~   ~         ~~         ~   ~~
3#
4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5# See https://llvm.org/LICENSE.txt for license information.
6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7"""Base class for all subtools."""
8
9import abc
10import os
11import tempfile
12
13from dex import __version__
14from dex.utils import ExtArgParse
15from dex.utils import PrettyOutput
16from dex.utils.ReturnCode import ReturnCode
17
18
19class ToolBase(object, metaclass=abc.ABCMeta):
20    def __init__(self, context):
21        self.context = context
22        self.parser = None
23
24    @abc.abstractproperty
25    def name(self):
26        pass
27
28    @abc.abstractmethod
29    def add_tool_arguments(self, parser, defaults):
30        pass
31
32    def parse_command_line(self, args):
33        """Define two parsers: pparser and self.parser.
34        pparser deals with args that need to be parsed prior to any of those of
35        self.parser.  For example, any args which may affect the state of
36        argparse error output.
37        """
38
39        class defaults(object):
40            pass
41
42        pparser = ExtArgParse.ExtArgumentParser(
43            self.context, add_help=False, prog=self.name)
44
45        pparser.add_argument(
46            '--no-color-output',
47            action='store_true',
48            default=False,
49            help='do not use colored output on stdout/stderr')
50        pparser.add_argument(
51            '--time-report',
52            action='store_true',
53            default=False,
54            help='display timing statistics')
55
56        self.parser = ExtArgParse.ExtArgumentParser(
57            self.context, parents=[pparser], prog=self.name)
58        self.parser.add_argument(
59            '-v',
60            '--verbose',
61            action='store_true',
62            default=False,
63            help='enable verbose output')
64        self.parser.add_argument(
65            '-V',
66            '--version',
67            action='store_true',
68            default=False,
69            help='display the DExTer version and exit')
70        self.parser.add_argument(
71            '-w',
72            '--no-warnings',
73            action='store_true',
74            default=False,
75            help='suppress warning output')
76        self.parser.add_argument(
77            '--unittest',
78            type=str,
79            choices=['off', 'show-failures', 'show-all'],
80            default='off',
81            help='run the DExTer codebase unit tests')
82
83        suppress = ExtArgParse.SUPPRESS  # pylint: disable=no-member
84        self.parser.add_argument(
85            '--colortest', action='store_true', default=False, help=suppress)
86        self.parser.add_argument(
87            '--error-debug', action='store_true', default=False, help=suppress)
88        defaults.working_directory = os.path.join(tempfile.gettempdir(),
89                                                  'dexter')
90        self.parser.add_argument(
91            '--indent-timer-level', type=int, default=1, help=suppress)
92        self.parser.add_argument(
93            '--working-directory',
94            type=str,
95            metavar='<file>',
96            default=None,
97            display_default=defaults.working_directory,
98            help='location of working directory')
99        self.parser.add_argument(
100            '--save-temps',
101            action='store_true',
102            default=False,
103            help='save temporary files')
104
105        self.add_tool_arguments(self.parser, defaults)
106
107        # If an error is encountered during pparser, show the full usage text
108        # including self.parser options. Strip the preceding 'usage: ' to avoid
109        # having it appear twice.
110        pparser.usage = self.parser.format_usage().lstrip('usage: ')
111
112        options, args = pparser.parse_known_args(args)
113
114        if options.no_color_output:
115            PrettyOutput.stdout.color_enabled = False
116            PrettyOutput.stderr.color_enabled = False
117
118        options = self.parser.parse_args(args, namespace=options)
119        return options, defaults
120
121    def handle_base_options(self, defaults):
122        self.handle_options(defaults)
123
124        options = self.context.options
125
126        if options.working_directory is None:
127            options.working_directory = defaults.working_directory
128
129    @abc.abstractmethod
130    def handle_options(self, defaults):
131        pass
132
133    @abc.abstractmethod
134    def go(self) -> ReturnCode:
135        pass
136