diff options
Diffstat (limited to 'codegen/vulkan/scripts/spec_tools/base_printer.py')
-rw-r--r-- | codegen/vulkan/scripts/spec_tools/base_printer.py | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/codegen/vulkan/scripts/spec_tools/base_printer.py b/codegen/vulkan/scripts/spec_tools/base_printer.py new file mode 100644 index 00000000..f48905ac --- /dev/null +++ b/codegen/vulkan/scripts/spec_tools/base_printer.py @@ -0,0 +1,213 @@ +"""Provides the BasePrinter base class for MacroChecker/Message output techniques.""" + +# Copyright (c) 2018-2019 Collabora, Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Author(s): Ryan Pavlik <ryan.pavlik@collabora.com> + +from abc import ABC, abstractmethod +from pathlib import Path + +from .macro_checker import MacroChecker +from .macro_checker_file import MacroCheckerFile +from .shared import EntityData, Message, MessageContext, MessageType + + +def getColumn(message_context): + """Return the (zero-based) column number of the message context. + + If a group is specified: returns the column of the start of the group. + If no group, but a match is specified: returns the column of the start of + the match. + If no match: returns column 0 (whole line). + """ + if not message_context.match: + # whole line + return 0 + if message_context.group is not None: + return message_context.match.start(message_context.group) + return message_context.match.start() + + +class BasePrinter(ABC): + """Base class for a way of outputting results of a checker execution.""" + + def __init__(self): + """Constructor.""" + self._cwd = None + + def close(self): + """Write the tail end of the output and close it, if applicable. + + Override if you want to print a summary or are writing to a file. + """ + pass + + ### + # Output methods: these should all print/output directly. + def output(self, obj): + """Output any object. + + Delegates to other output* methods, if type known, + otherwise uses self.outputFallback(). + """ + if isinstance(obj, Message): + self.outputMessage(obj) + elif isinstance(obj, MacroCheckerFile): + self.outputCheckerFile(obj) + elif isinstance(obj, MacroChecker): + self.outputChecker(obj) + else: + self.outputFallback(self.formatBrief(obj)) + + @abstractmethod + def outputResults(self, checker, broken_links=True, + missing_includes=False): + """Output the full results of a checker run. + + Must be implemented. + + Typically will call self.output() on the MacroChecker, + as well as calling self.outputBrokenAndMissing() + """ + raise NotImplementedError + + @abstractmethod + def outputBrokenLinks(self, checker, broken): + """Output the collection of broken links. + + `broken` is a dictionary of entity names: usage contexts. + + Must be implemented. + + Called by self.outputBrokenAndMissing() if requested. + """ + raise NotImplementedError + + @abstractmethod + def outputMissingIncludes(self, checker, missing): + """Output a table of missing includes. + + `missing` is a iterable entity names. + + Must be implemented. + + Called by self.outputBrokenAndMissing() if requested. + """ + raise NotImplementedError + + def outputChecker(self, checker): + """Output the contents of a MacroChecker object. + + Default implementation calls self.output() on every MacroCheckerFile. + """ + for f in checker.files: + self.output(f) + + def outputCheckerFile(self, fileChecker): + """Output the contents of a MacroCheckerFile object. + + Default implementation calls self.output() on every Message. + """ + for m in fileChecker.messages: + self.output(m) + + def outputBrokenAndMissing(self, checker, broken_links=True, + missing_includes=False): + """Outputs broken links and missing includes, if desired. + + Delegates to self.outputBrokenLinks() (if broken_links==True) + and self.outputMissingIncludes() (if missing_includes==True). + """ + if broken_links: + broken = checker.getBrokenLinks() + if broken: + self.outputBrokenLinks(checker, broken) + if missing_includes: + missing = checker.getMissingUnreferencedApiIncludes() + if missing: + self.outputMissingIncludes(checker, missing) + + @abstractmethod + def outputMessage(self, msg): + """Output a Message. + + Must be implemented. + """ + raise NotImplementedError + + @abstractmethod + def outputFallback(self, msg): + """Output some text in a general way. + + Must be implemented. + """ + raise NotImplementedError + + ### + # Format methods: these should all return a string. + def formatContext(self, context, _message_type=None): + """Format a message context in a verbose way, if applicable. + + May override, default implementation delegates to + self.formatContextBrief(). + """ + return self.formatContextBrief(context) + + def formatContextBrief(self, context, _with_color=True): + """Format a message context in a brief way. + + May override, default is relativeFilename:line:column + """ + return '{}:{}:{}'.format(self.getRelativeFilename(context.filename), + context.lineNum, getColumn(context)) + + def formatMessageTypeBrief(self, message_type, _with_color=True): + """Format a message type in a brief way. + + May override, default is message_type: + """ + return '{}:'.format(message_type) + + def formatEntityBrief(self, entity_data, _with_color=True): + """Format an entity in a brief way. + + May override, default is macro:entity. + """ + return '{}:{}'.format(entity_data.macro, entity_data.entity) + + def formatBrief(self, obj, with_color=True): + """Format any object in a brief way. + + Delegates to other format*Brief methods, if known, + otherwise uses str(). + """ + if isinstance(obj, MessageContext): + return self.formatContextBrief(obj, with_color) + if isinstance(obj, MessageType): + return self.formatMessageTypeBrief(obj, with_color) + if isinstance(obj, EntityData): + return self.formatEntityBrief(obj, with_color) + return str(obj) + + @property + def cwd(self): + """Get the current working directory, fully resolved. + + Lazy initialized. + """ + if not self._cwd: + self._cwd = Path('.').resolve() + return self._cwd + + ### + # Helper function + def getRelativeFilename(self, fn): + """Return the given filename relative to the current directory, + if possible. + """ + try: + return str(Path(fn).relative_to(self.cwd)) + except ValueError: + return str(Path(fn)) |