blob: b6c146ad784062c922b62ef2e4d39e6d2574d9a3 [file] [log] [blame]
Jeremy Morse984fad22019-10-31 16:51:53 +00001# 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"""This is the main entry point.
8It implements some functionality common to all subtools such as command line
9parsing and running the unit-testing harnesses, before calling the reequested
10subtool.
11"""
12
13import imp
14import os
15import sys
16
17from dex.utils import PrettyOutput, Timer
18from dex.utils import ExtArgParse as argparse
19from dex.utils import get_root_directory
20from dex.utils.Exceptions import Error, ToolArgumentError
Stephen Tozer73a01952023-02-28 17:49:59 +000021from dex.utils.Logging import Logger
Jeremy Morse984fad22019-10-31 16:51:53 +000022from dex.utils.UnitTests import unit_tests_ok
23from dex.utils.Version import version
24from dex.utils import WorkingDirectory
25from dex.utils.ReturnCode import ReturnCode
26
27
28def _output_bug_report_message(context):
Tobias Hietaf98ee402023-05-17 16:59:41 +020029 """In the event of a catastrophic failure, print bug report request to the
30 user.
Jeremy Morse984fad22019-10-31 16:51:53 +000031 """
32 context.o.red(
Tobias Hietaf98ee402023-05-17 16:59:41 +020033 "\n\n"
34 "<g>****************************************</>\n"
35 "<b>****************************************</>\n"
36 "****************************************\n"
37 "** **\n"
38 "** <y>This is a bug in <a>DExTer</>.</> **\n"
39 "** **\n"
40 "** <y>Please report it.</> **\n"
41 "** **\n"
42 "****************************************\n"
43 "<b>****************************************</>\n"
44 "<g>****************************************</>\n"
45 "\n"
46 "<b>system:</>\n"
47 "<d>{}</>\n\n"
48 "<b>version:</>\n"
49 "<d>{}</>\n\n"
50 "<b>args:</>\n"
51 "<d>{}</>\n"
52 "\n".format(sys.platform, version("DExTer"), [sys.executable] + sys.argv),
53 stream=PrettyOutput.stderr,
54 )
Jeremy Morse984fad22019-10-31 16:51:53 +000055
56
57def get_tools_directory():
Tobias Hietaf98ee402023-05-17 16:59:41 +020058 """Returns directory path where DExTer tool imports can be
59 found.
Jeremy Morse984fad22019-10-31 16:51:53 +000060 """
Tobias Hietaf98ee402023-05-17 16:59:41 +020061 tools_directory = os.path.join(get_root_directory(), "tools")
Jeremy Morse984fad22019-10-31 16:51:53 +000062 assert os.path.isdir(tools_directory), tools_directory
63 return tools_directory
64
65
66def get_tool_names():
Tobias Hietaf98ee402023-05-17 16:59:41 +020067 """Returns a list of expected DExTer Tools"""
Jeremy Morse984fad22019-10-31 16:51:53 +000068 return [
Tobias Hietaf98ee402023-05-17 16:59:41 +020069 "help",
70 "list-debuggers",
71 "no-tool-",
72 "run-debugger-internal-",
73 "test",
74 "view",
Jeremy Morse984fad22019-10-31 16:51:53 +000075 ]
76
77
78def _set_auto_highlights(context):
Tobias Hietaf98ee402023-05-17 16:59:41 +020079 """Flag some strings for auto-highlighting."""
80 context.o.auto_reds.extend(
81 [
82 r"[Ee]rror\:",
83 r"[Ee]xception\:",
84 r"un(expected|recognized) argument",
85 ]
86 )
87 context.o.auto_yellows.extend(
88 [
89 r"[Ww]arning\:",
90 r"\(did you mean ",
91 r"During handling of the above exception, another exception",
92 ]
93 )
Jeremy Morse984fad22019-10-31 16:51:53 +000094
95
96def _get_options_and_args(context):
Tobias Hietaf98ee402023-05-17 16:59:41 +020097 """get the options and arguments from the commandline"""
Jeremy Morse984fad22019-10-31 16:51:53 +000098 parser = argparse.ExtArgumentParser(context, add_help=False)
Tobias Hietaf98ee402023-05-17 16:59:41 +020099 parser.add_argument("tool", default=None, nargs="?")
Jeremy Morse984fad22019-10-31 16:51:53 +0000100 options, args = parser.parse_known_args(sys.argv[1:])
101
102 return options, args
103
104
105def _get_tool_name(options):
Tobias Hietaf98ee402023-05-17 16:59:41 +0200106 """get the name of the dexter tool (if passed) specified on the command
107 line, otherwise return 'no_tool_'.
Jeremy Morse984fad22019-10-31 16:51:53 +0000108 """
109 tool_name = options.tool
110 if tool_name is None:
Tobias Hietaf98ee402023-05-17 16:59:41 +0200111 tool_name = "no_tool_"
Jeremy Morse984fad22019-10-31 16:51:53 +0000112 else:
113 _is_valid_tool_name(tool_name)
114 return tool_name
115
116
117def _is_valid_tool_name(tool_name):
Tobias Hietaf98ee402023-05-17 16:59:41 +0200118 """check tool name matches a tool directory within the dexter tools
119 directory.
Jeremy Morse984fad22019-10-31 16:51:53 +0000120 """
121 valid_tools = get_tool_names()
122 if tool_name not in valid_tools:
Tobias Hietaf98ee402023-05-17 16:59:41 +0200123 raise Error(
124 'invalid tool "{}" (choose from {})'.format(
125 tool_name, ", ".join([t for t in valid_tools if not t.endswith("-")])
126 )
127 )
Jeremy Morse984fad22019-10-31 16:51:53 +0000128
129
130def _import_tool_module(tool_name):
Tobias Hietaf98ee402023-05-17 16:59:41 +0200131 """Imports the python module at the tool directory specificed by
132 tool_name.
Jeremy Morse984fad22019-10-31 16:51:53 +0000133 """
134 # format tool argument to reflect tool directory form.
Tobias Hietaf98ee402023-05-17 16:59:41 +0200135 tool_name = tool_name.replace("-", "_")
Jeremy Morse984fad22019-10-31 16:51:53 +0000136
137 tools_directory = get_tools_directory()
138 module_info = imp.find_module(tool_name, [tools_directory])
139
140 return imp.load_module(tool_name, *module_info)
141
142
143def tool_main(context, tool, args):
144 with Timer(tool.name):
145 options, defaults = tool.parse_command_line(args)
146 Timer.display = options.time_report
147 Timer.indent = options.indent_timer_level
148 Timer.fn = context.o.blue
149 context.options = options
150 context.version = version(tool.name)
151
152 if options.version:
Tobias Hietaf98ee402023-05-17 16:59:41 +0200153 context.o.green("{}\n".format(context.version))
Jeremy Morse984fad22019-10-31 16:51:53 +0000154 return ReturnCode.OK
155
Stephen Tozer73a01952023-02-28 17:49:59 +0000156 if options.verbose:
157 context.logger.verbosity = 2
158 elif options.no_warnings:
159 context.logger.verbosity = 0
160
Tobias Hietaf98ee402023-05-17 16:59:41 +0200161 if options.unittest != "off" and not unit_tests_ok(context):
162 raise Error("<d>unit test failures</>")
Jeremy Morse984fad22019-10-31 16:51:53 +0000163
164 if options.colortest:
165 context.o.colortest()
166 return ReturnCode.OK
167
168 try:
169 tool.handle_base_options(defaults)
170 except ToolArgumentError as e:
171 raise Error(e)
172
173 dir_ = context.options.working_directory
174 with WorkingDirectory(context, dir=dir_) as context.working_directory:
175 return_code = tool.go()
176
177 return return_code
178
179
180class Context(object):
181 """Context encapsulates globally useful objects and data; passed to many
182 Dexter functions.
183 """
184
185 def __init__(self):
186 self.o: PrettyOutput = None
Stephen Tozer73a01952023-02-28 17:49:59 +0000187 self.logger: Logger = None
Jeremy Morse984fad22019-10-31 16:51:53 +0000188 self.working_directory: str = None
189 self.options: dict = None
190 self.version: str = None
191 self.root_directory: str = None
192
193
194def main() -> ReturnCode:
Jeremy Morse984fad22019-10-31 16:51:53 +0000195 context = Context()
Jeremy Morse984fad22019-10-31 16:51:53 +0000196 with PrettyOutput() as context.o:
Stephen Tozer73a01952023-02-28 17:49:59 +0000197 context.logger = Logger(context.o)
Jeremy Morse984fad22019-10-31 16:51:53 +0000198 try:
199 context.root_directory = get_root_directory()
200 # Flag some strings for auto-highlighting.
201 _set_auto_highlights(context)
202 options, args = _get_options_and_args(context)
203 # raises 'Error' if command line tool is invalid.
204 tool_name = _get_tool_name(options)
205 module = _import_tool_module(tool_name)
206 return tool_main(context, module.Tool(context), args)
207 except Error as e:
Stephen Tozer73a01952023-02-28 17:49:59 +0000208 context.logger.error(str(e))
Jeremy Morse984fad22019-10-31 16:51:53 +0000209 try:
210 if context.options.error_debug:
211 raise
212 except AttributeError:
213 pass
214 return ReturnCode._ERROR
215 except (KeyboardInterrupt, SystemExit):
216 raise
217 except: # noqa
218 _output_bug_report_message(context)
219 raise