You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
6.4 KiB
208 lines
6.4 KiB
4 months ago
|
# DExTer : Debugging Experience Tester
|
||
|
# ~~~~~~ ~ ~~ ~ ~~
|
||
|
#
|
||
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
# See https://llvm.org/LICENSE.txt for license information.
|
||
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
"""This is the main entry point.
|
||
|
It implements some functionality common to all subtools such as command line
|
||
|
parsing and running the unit-testing harnesses, before calling the reequested
|
||
|
subtool.
|
||
|
"""
|
||
|
|
||
|
import imp
|
||
|
import os
|
||
|
import sys
|
||
|
|
||
|
from dex.utils import PrettyOutput, Timer
|
||
|
from dex.utils import ExtArgParse as argparse
|
||
|
from dex.utils import get_root_directory
|
||
|
from dex.utils.Exceptions import Error, ToolArgumentError
|
||
|
from dex.utils.UnitTests import unit_tests_ok
|
||
|
from dex.utils.Version import version
|
||
|
from dex.utils import WorkingDirectory
|
||
|
from dex.utils.ReturnCode import ReturnCode
|
||
|
|
||
|
|
||
|
def _output_bug_report_message(context):
|
||
|
""" In the event of a catastrophic failure, print bug report request to the
|
||
|
user.
|
||
|
"""
|
||
|
context.o.red(
|
||
|
'\n\n'
|
||
|
'<g>****************************************</>\n'
|
||
|
'<b>****************************************</>\n'
|
||
|
'****************************************\n'
|
||
|
'** **\n'
|
||
|
'** <y>This is a bug in <a>DExTer</>.</> **\n'
|
||
|
'** **\n'
|
||
|
'** <y>Please report it.</> **\n'
|
||
|
'** **\n'
|
||
|
'****************************************\n'
|
||
|
'<b>****************************************</>\n'
|
||
|
'<g>****************************************</>\n'
|
||
|
'\n'
|
||
|
'<b>system:</>\n'
|
||
|
'<d>{}</>\n\n'
|
||
|
'<b>version:</>\n'
|
||
|
'<d>{}</>\n\n'
|
||
|
'<b>args:</>\n'
|
||
|
'<d>{}</>\n'
|
||
|
'\n'.format(sys.platform, version('DExTer'),
|
||
|
[sys.executable] + sys.argv),
|
||
|
stream=PrettyOutput.stderr)
|
||
|
|
||
|
|
||
|
def get_tools_directory():
|
||
|
""" Returns directory path where DExTer tool imports can be
|
||
|
found.
|
||
|
"""
|
||
|
tools_directory = os.path.join(get_root_directory(), 'tools')
|
||
|
assert os.path.isdir(tools_directory), tools_directory
|
||
|
return tools_directory
|
||
|
|
||
|
|
||
|
def get_tool_names():
|
||
|
""" Returns a list of expected DExTer Tools
|
||
|
"""
|
||
|
return [
|
||
|
'clang-opt-bisect', 'help', 'list-debuggers', 'no-tool-',
|
||
|
'run-debugger-internal-', 'test', 'view'
|
||
|
]
|
||
|
|
||
|
|
||
|
def _set_auto_highlights(context):
|
||
|
"""Flag some strings for auto-highlighting.
|
||
|
"""
|
||
|
context.o.auto_reds.extend([
|
||
|
r'[Ee]rror\:',
|
||
|
r'[Ee]xception\:',
|
||
|
r'un(expected|recognized) argument',
|
||
|
])
|
||
|
context.o.auto_yellows.extend([
|
||
|
r'[Ww]arning\:',
|
||
|
r'\(did you mean ',
|
||
|
r'During handling of the above exception, another exception',
|
||
|
])
|
||
|
|
||
|
|
||
|
def _get_options_and_args(context):
|
||
|
""" get the options and arguments from the commandline
|
||
|
"""
|
||
|
parser = argparse.ExtArgumentParser(context, add_help=False)
|
||
|
parser.add_argument('tool', default=None, nargs='?')
|
||
|
options, args = parser.parse_known_args(sys.argv[1:])
|
||
|
|
||
|
return options, args
|
||
|
|
||
|
|
||
|
def _get_tool_name(options):
|
||
|
""" get the name of the dexter tool (if passed) specified on the command
|
||
|
line, otherwise return 'no_tool_'.
|
||
|
"""
|
||
|
tool_name = options.tool
|
||
|
if tool_name is None:
|
||
|
tool_name = 'no_tool_'
|
||
|
else:
|
||
|
_is_valid_tool_name(tool_name)
|
||
|
return tool_name
|
||
|
|
||
|
|
||
|
def _is_valid_tool_name(tool_name):
|
||
|
""" check tool name matches a tool directory within the dexter tools
|
||
|
directory.
|
||
|
"""
|
||
|
valid_tools = get_tool_names()
|
||
|
if tool_name not in valid_tools:
|
||
|
raise Error('invalid tool "{}" (choose from {})'.format(
|
||
|
tool_name,
|
||
|
', '.join([t for t in valid_tools if not t.endswith('-')])))
|
||
|
|
||
|
|
||
|
def _import_tool_module(tool_name):
|
||
|
""" Imports the python module at the tool directory specificed by
|
||
|
tool_name.
|
||
|
"""
|
||
|
# format tool argument to reflect tool directory form.
|
||
|
tool_name = tool_name.replace('-', '_')
|
||
|
|
||
|
tools_directory = get_tools_directory()
|
||
|
module_info = imp.find_module(tool_name, [tools_directory])
|
||
|
|
||
|
return imp.load_module(tool_name, *module_info)
|
||
|
|
||
|
|
||
|
def tool_main(context, tool, args):
|
||
|
with Timer(tool.name):
|
||
|
options, defaults = tool.parse_command_line(args)
|
||
|
Timer.display = options.time_report
|
||
|
Timer.indent = options.indent_timer_level
|
||
|
Timer.fn = context.o.blue
|
||
|
context.options = options
|
||
|
context.version = version(tool.name)
|
||
|
|
||
|
if options.version:
|
||
|
context.o.green('{}\n'.format(context.version))
|
||
|
return ReturnCode.OK
|
||
|
|
||
|
if (options.unittest != 'off' and not unit_tests_ok(context)):
|
||
|
raise Error('<d>unit test failures</>')
|
||
|
|
||
|
if options.colortest:
|
||
|
context.o.colortest()
|
||
|
return ReturnCode.OK
|
||
|
|
||
|
try:
|
||
|
tool.handle_base_options(defaults)
|
||
|
except ToolArgumentError as e:
|
||
|
raise Error(e)
|
||
|
|
||
|
dir_ = context.options.working_directory
|
||
|
with WorkingDirectory(context, dir=dir_) as context.working_directory:
|
||
|
return_code = tool.go()
|
||
|
|
||
|
return return_code
|
||
|
|
||
|
|
||
|
class Context(object):
|
||
|
"""Context encapsulates globally useful objects and data; passed to many
|
||
|
Dexter functions.
|
||
|
"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.o: PrettyOutput = None
|
||
|
self.working_directory: str = None
|
||
|
self.options: dict = None
|
||
|
self.version: str = None
|
||
|
self.root_directory: str = None
|
||
|
|
||
|
|
||
|
def main() -> ReturnCode:
|
||
|
|
||
|
context = Context()
|
||
|
|
||
|
with PrettyOutput() as context.o:
|
||
|
try:
|
||
|
context.root_directory = get_root_directory()
|
||
|
# Flag some strings for auto-highlighting.
|
||
|
_set_auto_highlights(context)
|
||
|
options, args = _get_options_and_args(context)
|
||
|
# raises 'Error' if command line tool is invalid.
|
||
|
tool_name = _get_tool_name(options)
|
||
|
module = _import_tool_module(tool_name)
|
||
|
return tool_main(context, module.Tool(context), args)
|
||
|
except Error as e:
|
||
|
context.o.auto(
|
||
|
'\nerror: {}\n'.format(str(e)), stream=PrettyOutput.stderr)
|
||
|
try:
|
||
|
if context.options.error_debug:
|
||
|
raise
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
return ReturnCode._ERROR
|
||
|
except (KeyboardInterrupt, SystemExit):
|
||
|
raise
|
||
|
except: # noqa
|
||
|
_output_bug_report_message(context)
|
||
|
raise
|