218 lines · python
1# DExTer : Debugging Experience Tester2# ~~~~~~ ~ ~~ ~ ~~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-exception7"""This is the main entry point.8It implements some functionality common to all subtools such as command line9parsing and running the unit-testing harnesses, before calling the reequested10subtool.11"""12 13import os14import sys15 16from dex.utils import PrettyOutput, Timer17from dex.utils import ExtArgParse as argparse18from dex.utils import get_root_directory19from dex.utils.Exceptions import Error, ToolArgumentError20from dex.utils.Imports import load_module21from dex.utils.Logging import Logger22from dex.utils.UnitTests import unit_tests_ok23from dex.utils.Version import version24from dex.utils import WorkingDirectory25from dex.utils.ReturnCode import ReturnCode26 27 28def _output_bug_report_message(context):29 """In the event of a catastrophic failure, print bug report request to the30 user.31 """32 context.o.red(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 )55 56 57def get_tools_directory():58 """Returns directory path where DExTer tool imports can be59 found.60 """61 tools_directory = os.path.join(get_root_directory(), "tools")62 assert os.path.isdir(tools_directory), tools_directory63 return tools_directory64 65 66def get_tool_names():67 """Returns a list of expected DExTer Tools"""68 return [69 "help",70 "list-debuggers",71 "no-tool-",72 "run-debugger-internal-",73 "test",74 "view",75 ]76 77 78def _set_auto_highlights(context):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 )94 95 96def _get_options_and_args(context):97 """get the options and arguments from the commandline"""98 parser = argparse.ExtArgumentParser(context, add_help=False)99 parser.add_argument("tool", default=None, nargs="?")100 options, args = parser.parse_known_args(sys.argv[1:])101 102 return options, args103 104 105def _get_tool_name(options):106 """get the name of the dexter tool (if passed) specified on the command107 line, otherwise return 'no_tool_'.108 """109 tool_name = options.tool110 if tool_name is None:111 tool_name = "no_tool_"112 else:113 _is_valid_tool_name(tool_name)114 return tool_name115 116 117def _is_valid_tool_name(tool_name):118 """check tool name matches a tool directory within the dexter tools119 directory.120 """121 valid_tools = get_tool_names()122 if tool_name not in valid_tools: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 )128 129 130def _import_tool_module(tool_name):131 """Imports the python module at the tool directory specificed by132 tool_name.133 """134 # format tool argument to reflect tool directory form.135 tool_name = tool_name.replace("-", "_")136 137 tools_directory = get_tools_directory()138 return load_module(tool_name, tools_directory)139 140 141def tool_main(context, tool, args):142 with Timer(tool.name):143 options, defaults = tool.parse_command_line(args)144 Timer.display = options.time_report145 Timer.indent = options.indent_timer_level146 Timer.fn = context.o.blue147 context.options = options148 context.version = version(tool.name)149 150 if options.version:151 context.o.green("{}\n".format(context.version))152 return ReturnCode.OK153 154 if options.verbose:155 context.logger.verbosity = 2156 elif options.no_warnings:157 context.logger.verbosity = 0158 159 if options.unittest != "off" and not unit_tests_ok(context):160 raise Error("<d>unit test failures</>")161 162 if options.colortest:163 context.o.colortest()164 return ReturnCode.OK165 166 try:167 tool.handle_base_options(defaults)168 except ToolArgumentError as e:169 raise Error(e)170 171 dir_ = context.options.working_directory172 with WorkingDirectory(context, dir=dir_) as context.working_directory:173 return_code = tool.go()174 175 return return_code176 177 178class Context(object):179 """Context encapsulates globally useful objects and data; passed to many180 Dexter functions.181 """182 183 def __init__(self):184 self.o: PrettyOutput = None185 self.logger: Logger = None186 self.working_directory: str = None187 self.options: dict = None188 self.version: str = None189 self.root_directory: str = None190 191 192def main() -> ReturnCode:193 context = Context()194 with PrettyOutput() as context.o:195 context.logger = Logger(context.o)196 try:197 context.root_directory = get_root_directory()198 # Flag some strings for auto-highlighting.199 _set_auto_highlights(context)200 options, args = _get_options_and_args(context)201 # raises 'Error' if command line tool is invalid.202 tool_name = _get_tool_name(options)203 module = _import_tool_module(tool_name)204 return tool_main(context, module.Tool(context), args)205 except Error as e:206 context.logger.error(str(e))207 try:208 if context.options.error_debug:209 raise210 except AttributeError:211 pass212 return ReturnCode._ERROR213 except (KeyboardInterrupt, SystemExit):214 raise215 except: # noqa216 _output_bug_report_message(context)217 raise218