Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 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 | """This is the main entry point. |
| 8 | It implements some functionality common to all subtools such as command line |
| 9 | parsing and running the unit-testing harnesses, before calling the reequested |
| 10 | subtool. |
| 11 | """ |
| 12 | |
| 13 | import imp |
| 14 | import os |
| 15 | import sys |
| 16 | |
| 17 | from dex.utils import PrettyOutput, Timer |
| 18 | from dex.utils import ExtArgParse as argparse |
| 19 | from dex.utils import get_root_directory |
| 20 | from dex.utils.Exceptions import Error, ToolArgumentError |
Stephen Tozer | 73a0195 | 2023-02-28 17:49:59 +0000 | [diff] [blame] | 21 | from dex.utils.Logging import Logger |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 22 | from dex.utils.UnitTests import unit_tests_ok |
| 23 | from dex.utils.Version import version |
| 24 | from dex.utils import WorkingDirectory |
| 25 | from dex.utils.ReturnCode import ReturnCode |
| 26 | |
| 27 | |
| 28 | def _output_bug_report_message(context): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 29 | """In the event of a catastrophic failure, print bug report request to the |
| 30 | user. |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 31 | """ |
| 32 | context.o.red( |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 33 | "\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 Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 55 | |
| 56 | |
| 57 | def get_tools_directory(): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 58 | """Returns directory path where DExTer tool imports can be |
| 59 | found. |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 60 | """ |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 61 | tools_directory = os.path.join(get_root_directory(), "tools") |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 62 | assert os.path.isdir(tools_directory), tools_directory |
| 63 | return tools_directory |
| 64 | |
| 65 | |
| 66 | def get_tool_names(): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 67 | """Returns a list of expected DExTer Tools""" |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 68 | return [ |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 69 | "help", |
| 70 | "list-debuggers", |
| 71 | "no-tool-", |
| 72 | "run-debugger-internal-", |
| 73 | "test", |
| 74 | "view", |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 75 | ] |
| 76 | |
| 77 | |
| 78 | def _set_auto_highlights(context): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 79 | """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 Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 94 | |
| 95 | |
| 96 | def _get_options_and_args(context): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 97 | """get the options and arguments from the commandline""" |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 98 | parser = argparse.ExtArgumentParser(context, add_help=False) |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 99 | parser.add_argument("tool", default=None, nargs="?") |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 100 | options, args = parser.parse_known_args(sys.argv[1:]) |
| 101 | |
| 102 | return options, args |
| 103 | |
| 104 | |
| 105 | def _get_tool_name(options): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 106 | """get the name of the dexter tool (if passed) specified on the command |
| 107 | line, otherwise return 'no_tool_'. |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 108 | """ |
| 109 | tool_name = options.tool |
| 110 | if tool_name is None: |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 111 | tool_name = "no_tool_" |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 112 | else: |
| 113 | _is_valid_tool_name(tool_name) |
| 114 | return tool_name |
| 115 | |
| 116 | |
| 117 | def _is_valid_tool_name(tool_name): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 118 | """check tool name matches a tool directory within the dexter tools |
| 119 | directory. |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 120 | """ |
| 121 | valid_tools = get_tool_names() |
| 122 | if tool_name not in valid_tools: |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 123 | 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 Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 128 | |
| 129 | |
| 130 | def _import_tool_module(tool_name): |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 131 | """Imports the python module at the tool directory specificed by |
| 132 | tool_name. |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 133 | """ |
| 134 | # format tool argument to reflect tool directory form. |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 135 | tool_name = tool_name.replace("-", "_") |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 136 | |
| 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 | |
| 143 | def 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 Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 153 | context.o.green("{}\n".format(context.version)) |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 154 | return ReturnCode.OK |
| 155 | |
Stephen Tozer | 73a0195 | 2023-02-28 17:49:59 +0000 | [diff] [blame] | 156 | if options.verbose: |
| 157 | context.logger.verbosity = 2 |
| 158 | elif options.no_warnings: |
| 159 | context.logger.verbosity = 0 |
| 160 | |
Tobias Hieta | f98ee40 | 2023-05-17 16:59:41 +0200 | [diff] [blame] | 161 | if options.unittest != "off" and not unit_tests_ok(context): |
| 162 | raise Error("<d>unit test failures</>") |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 163 | |
| 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 | |
| 180 | class 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 Tozer | 73a0195 | 2023-02-28 17:49:59 +0000 | [diff] [blame] | 187 | self.logger: Logger = None |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 188 | self.working_directory: str = None |
| 189 | self.options: dict = None |
| 190 | self.version: str = None |
| 191 | self.root_directory: str = None |
| 192 | |
| 193 | |
| 194 | def main() -> ReturnCode: |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 195 | context = Context() |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 196 | with PrettyOutput() as context.o: |
Stephen Tozer | 73a0195 | 2023-02-28 17:49:59 +0000 | [diff] [blame] | 197 | context.logger = Logger(context.o) |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 198 | 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 Tozer | 73a0195 | 2023-02-28 17:49:59 +0000 | [diff] [blame] | 208 | context.logger.error(str(e)) |
Jeremy Morse | 984fad2 | 2019-10-31 16:51:53 +0000 | [diff] [blame] | 209 | 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 |