summaryrefslogtreecommitdiff
path: root/codegen/vulkan/scripts/spec_tools/base_printer.py
diff options
context:
space:
mode:
Diffstat (limited to 'codegen/vulkan/scripts/spec_tools/base_printer.py')
-rw-r--r--codegen/vulkan/scripts/spec_tools/base_printer.py213
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))