#!/usr/bin/env python3 # ============================================================================= # ____ _ _ ____ _ _ # _ __ _ _ / ___| | | | _ \| | __| | ___ _ __ ___ # | '_ \| | | | | _| |_| | | | | | / _` |/ _ \| '_ ` _ \ # | |_) | |_| | |_| | _ | |_| | |___ | (_| | (_) | | | | | | # | .__/ \__, |\____|_| |_|____/|_____(_)__,_|\___/|_| |_| |_| # |_| |___/ # ============================================================================= # Authors: # Patrick Lehmann # Unai Martinez-Corral # # Package module: DOM: Interface items (e.g. generic or port) # # License: # ============================================================================ # Copyright (C) 2019-2021 Tristan Gingold # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # SPDX-License-Identifier: GPL-2.0-or-later # ============================================================================ from argparse import RawDescriptionHelpFormatter from pathlib import Path from platform import system as platform_system from textwrap import wrap, dedent from pyGHDL.dom import DOMException from pyGHDL.libghdl import LibGHDLException from pydecor import export from pyMetaClasses import Singleton from pyAttributes import Attribute from pyAttributes.ArgParseAttributes import ( ArgParseMixin, CommonSwitchArgumentAttribute, DefaultAttribute, CommandAttribute, ArgumentAttribute, SwitchArgumentAttribute, ) from pyTerminalUI import LineTerminal, Severity from pyGHDL import GHDLBaseException from pyGHDL.dom.NonStandard import Design, Document from pyGHDL.dom.formatting.prettyprint import PrettyPrint, PrettyPrintException __author__ = "Tristan Gingold" __copyright__ = "Copyright (C) 2019-2021 Tristan Gingold" __maintainer__ = "Tristan Gingold" __email__ = "" __version__ = "0.0.0" __status__ = "Alpha" __license__ = "" __all__ = [] __api__ = __all__ class SourceAttribute(Attribute): def __call__(self, func): self._AppendAttribute( func, ArgumentAttribute( "-f", "--file", action="append", metavar="file", dest="Files", type=Path, help="The filename to parse (can be used multiple times).", ), ) self._AppendAttribute( func, ArgumentAttribute( "-F", "--files", metavar="files", dest="Files", type=Path, nargs="+", help="List of filenames to parse.", ), ) self._AppendAttribute( func, ArgumentAttribute( "-D", "--directory", metavar="dir", dest="Directory", type=Path, help="The directory to parse.", ), ) return func @export class Application(LineTerminal, ArgParseMixin): HeadLine = "pyGHDL.dom - Test Application" # load platform information (Windows, Linux, Darwin, ...) __PLATFORM = platform_system() _design: Design def __init__(self, debug=False, verbose=False, quiet=False, sphinx=False): super().__init__(verbose, debug, quiet) # Initialize the Terminal class # -------------------------------------------------------------------------- Singleton.Register(LineTerminal, self) # Initialize DOM with an empty design # -------------------------------------------------------------------------- self._design = Design() # Call the constructor of the ArgParseMixin # -------------------------------------------------------------------------- textWidth = min(max(self.Width, 80), 160) description = dedent( """\ Application to test pyGHDL's DOM API. """ ) epilog = "\n".join( wrap( dedent( """\ pyGHDL is a Python binding for libghdl. """ ), textWidth, replace_whitespace=False, ) ) class HelpFormatter(RawDescriptionHelpFormatter): def __init__(self, *args, **kwargs): kwargs["max_help_position"] = 30 kwargs["width"] = textWidth super().__init__(*args, **kwargs) ArgParseMixin.__init__( self, description=description, epilog=epilog, formatter_class=HelpFormatter, add_help=False, ) # If executed in Sphinx to auto-document CLI arguments, exit now # -------------------------------------------------------------------------- if sphinx: return # Change error and warning reporting # -------------------------------------------------------------------------- self._LOG_MESSAGE_FORMAT__[Severity.Fatal] = "{DARK_RED}[FATAL] {message}{NOCOLOR}" self._LOG_MESSAGE_FORMAT__[Severity.Error] = "{RED}[ERROR] {message}{NOCOLOR}" self._LOG_MESSAGE_FORMAT__[Severity.Warning] = "{YELLOW}[WARNING] {message}{NOCOLOR}" self._LOG_MESSAGE_FORMAT__[Severity.Normal] = "{GRAY}{message}{NOCOLOR}" # class properties # ============================================================================ @property def Platform(self): return self.__PLATFORM def PrintHeadline(self): self.WriteNormal( dedent( """\ {HEADLINE}{line} {headline: ^80s} {line}""" ).format(line="=" * 80, headline=self.HeadLine, **LineTerminal.Foreground) ) # ============================================================================ # Common commands # ============================================================================ # common arguments valid for all commands # ---------------------------------------------------------------------------- @CommonSwitchArgumentAttribute("-d", "--debug", dest="debug", help="Enable debug mode.") @CommonSwitchArgumentAttribute("-v", "--verbose", dest="verbose", help="Print out detailed messages.") @CommonSwitchArgumentAttribute("-q", "--quiet", dest="quiet", help="Reduce messages to a minimum.") def Run(self): ArgParseMixin.Run(self) @DefaultAttribute() def HandleDefault(self, _): self.PrintHeadline() self.MainParser.print_help() self.WriteNormal("") self.exit() # ---------------------------------------------------------------------------- # create the sub-parser for the "help" command # ---------------------------------------------------------------------------- @CommandAttribute("help", help="Display help page(s) for the given command name.") @ArgumentAttribute( metavar="Command", dest="Command", type=str, nargs="?", help="Print help page(s) for a command.", ) def HandleHelp(self, args): self.PrintHeadline() if args.Command is None: self.MainParser.print_help() elif args.Command == "help": self.WriteError("This is a recursion ...") else: try: self.SubParsers[args.Command].print_help() except KeyError: self.WriteError("Command {0} is unknown.".format(args.Command)) self.WriteNormal("") self.exit() # ---------------------------------------------------------------------------- # create the sub-parser for the "version" command # ---------------------------------------------------------------------------- @CommandAttribute("version", help="Display tool and version information.") def HandleInfo(self, args): self.PrintHeadline() copyrights = __copyright__.split("\n", 1) self.WriteNormal("Copyright: {0}".format(copyrights[0])) for copyright in copyrights[1:]: self.WriteNormal(" {0}".format(copyright)) self.WriteNormal("License: {0}".format(__license__)) authors = __author__.split(", ") self.WriteNormal("Authors: {0}".format(authors[0])) for author in authors[1:]: self.WriteNormal(" {0}".format(author)) self.WriteNormal("Version: {0}".format(__version__)) self.exit() # ---------------------------------------------------------------------------- # Create the sub-parser for the "pretty" command # ---------------------------------------------------------------------------- @CommandAttribute( "pretty", help="Pretty-print the DOM to console.", description="Translate a source file into a DOM and pretty-print the DOM.", ) @SourceAttribute() def HandlePretty(self, args): self.PrintHeadline() if args.Files is not None: for file in args.Files: if not file.exists(): self.WriteError("File '{0!s}' does not exist.".format(file)) continue self.WriteNormal("Parsing file '{!s}'".format(file)) document = self.addFile(file, "pretty") self.WriteInfo( dedent( """\ libghdl processing time: {: 5.3f} us DOM translation time: {:5.3f} us """ ).format( document.LibGHDLProcessingTime * 10 ** 6, document.DOMTranslationTime * 10 ** 6, ) ) elif args.Directory is not None: d: Path = args.Directory if not d.exists(): self.WriteError("Directory '{0!s}' does not exist.".format(d)) for file in d.glob("**/*.vhd?"): self.WriteNormal("Parsing file '{!s}'".format(file)) document = self.addFile(file, "pretty") self.WriteInfo( dedent( """\ libghdl processing time: {: 5.3f} us DOM translation time: {:5.3f} us """ ).format( document.LibGHDLProcessingTime * 10 ** 6, document.DOMTranslationTime * 10 ** 6, ) ) for library in self._design.Libraries.values(): for entityName, architectures in library.Architectures.items(): for entity in library.Entities: if entity.Identifier == str(entityName): for architecture in architectures: entity.Architectures.append(architecture) PP = PrettyPrint() buffer = [] buffer.append("Design:") for line in PP.formatDesign(self._design, 1): buffer.append(line) print("\n".join(buffer)) self.exit() def addFile(self, filename: Path, library: str) -> Document: lib = self._design.GetLibrary(library) document = Document(filename) self._design.AddDocument(document, lib) return document # main program def main(): # mccabe:disable=MC0001 """This is the entry point for pyghdl.cli.dom written as a function. 1. It extracts common flags from the script's arguments list, before :py:class:`~argparse.ArgumentParser` is fully loaded. 2. It creates an instance of DOM test application and hands over to a class based execution. All is wrapped in a big ``try..except`` block to catch every unhandled exception. 3. Shutdown the script and return its exit code. """ from sys import argv as sys_argv debug = "-d" in sys_argv verbose = "-v" in sys_argv quiet = "-q" in sys_argv try: # handover to a class instance app = Application(debug, verbose, quiet) app.Run() app.exit() except PrettyPrintException as ex: print("PP: {!s}".format(ex)) LineTerminal.exit() except DOMException as ex: print("DOM: {!s}".format(ex)) ex2: LibGHDLException = ex.__cause__ if ex2 is not None: for message in ex2.InternalErrors: print("libghdl: {message}".format(message=message)) LineTerminal.exit(0) LineTerminal.exit(6) except LibGHDLException as ex: print("LIB: {!s}".format(ex)) for message in ex.InternalErrors: print(" {message}".format(message=message)) LineTerminal.exit(5) except GHDLBaseException as ex: LineTerminal.printExceptionBase(ex) except NotImplementedError as ex: LineTerminal.printNotImplementedError(ex) # except ImportError as ex: # printImportError(ex) except Exception as ex: LineTerminal.printException(ex) # entry point if __name__ == "__main__": LineTerminal.versionCheck((3, 6, 0)) main()