#!/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 <gnu.org/licenses>.
#
# 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 pyTooling.Decorators import export
from pyTooling.TerminalUI import LineTerminal, Severity
from pyAttributes import Attribute
from pyAttributes.ArgParseAttributes import (
ArgParseMixin,
CommonSwitchArgumentAttribute,
DefaultAttribute,
CommandAttribute,
ArgumentAttribute,
SwitchArgumentAttribute,
)
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__ = ""
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
[docs]@export
class Application(LineTerminal, ArgParseMixin):
HeadLine = "pyGHDL.dom - Test Application"
# load platform information (Windows, Linux, Darwin, ...)
__PLATFORM = platform_system()
_design: Design
[docs] def __init__(self, debug=False, verbose=False, quiet=False, sphinx=False):
super().__init__(verbose, debug, quiet)
# 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(f"Command {args.Command} is unknown.")
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(f"Copyright: {copyrights[0]}")
for copyright in copyrights[1:]:
self.WriteNormal(f" {copyright}")
self.WriteNormal(f"License: {__license__}")
authors = __author__.split(", ")
self.WriteNormal(f"Authors: {authors[0]}")
for author in authors[1:]:
self.WriteNormal(f" {author}")
self.WriteNormal(f"Version: {__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(f"File '{file!s}' does not exist.")
continue
self.WriteNormal(f"Parsing file '{file!s}'")
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.resolve()
if not d.exists():
self.WriteError(f"Directory '{d!s}' does not exist.")
for file in d.glob("**/*.vhd*"):
self.WriteNormal(f"Parsing file '{file!s}'")
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,
)
)
if not self._design.Documents:
self.WriteFatal("No files processed at all.")
self._design.LoadDefaultLibraries()
self._design.Analyze()
self.WriteInfo(
dedent(
"""\
default library load time: {:5.3f} us
dependency analysis time: {:5.3f} us
"""
).format(
self._design._loadDefaultLibraryTime * 10**6,
self._design._analyzeTime * 10**6,
)
)
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(f"PP: {ex!s}")
LineTerminal.exit()
except DOMException as ex:
print(f"DOM: {ex!s}")
ex2: LibGHDLException = ex.__cause__
if ex2 is not None:
for message in ex2.InternalErrors:
print(f"libghdl: {message}")
LineTerminal.exit(0)
LineTerminal.exit(6)
except LibGHDLException as ex:
print(f"LIB: {ex!s}")
for message in ex.InternalErrors:
print(f" {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()