Skip to content

Commit

Permalink
Improved GHDL.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paebbels committed Jul 8, 2023
1 parent c37c2e8 commit 807336f
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 9 deletions.
131 changes: 127 additions & 4 deletions pyEDAA/CLITool/GHDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
# ==================================================================================================================== #
#
"""This module contains the CLI abstraction layer for `GHDL <https://github.com/ghdl/ghdl>`__."""
from typing import Union, Iterable
from re import search as re_search
from typing import Union, Iterable, Tuple

from pyTooling.Decorators import export
from pyVHDLModel import VHDLVersion
from pyVHDLModel import VHDLVersion

from pyTooling.CLIAbstraction import CLIArgument, Executable
from pyTooling.CLIAbstraction.Argument import PathListArgument, StringArgument
Expand All @@ -43,6 +44,107 @@
from pyTooling.CLIAbstraction.ValuedFlag import ShortValuedFlag, LongValuedFlag
from pyTooling.CLIAbstraction.KeyValueFlag import ShortKeyValueFlag

from pyEDAA.CLITool import CLIToolException


@export
class GHDLVersion:
_major: int
_minor: int
_micro: int
_dev: bool
_commitsSinceLastTag: int
_gitHash: str
_dirty: bool
_edition: str
_gnatCompiler: Tuple[int, int, int]
_backend: str

def __init__(self, versionLine: str, gnatLine: str, backendLine: str):
match = re_search(
r"GHDL"
r"\s(?P<major>\d+)"
r"\.(?P<minor>\d+)"
r"\.(?P<micro>\d+)"
r"(?:-(?P<suffix>dev))?"
r"\s\("
r"(?P<major2>\d+)"
r"\.(?P<minor2>\d+)"
r"\.(?P<micro2>\d+)"
r"\.(?:r(?P<cslt>\d+))"
r"\.(?:g(?P<hash>[0-9a-f]+))"
r"(?:\.(?P<dirty>dirty))?"
r"\)\s"
r"\[(?P<edition>[\w\s]+)\]",
versionLine)

if match is None:
raise CLIToolException(f"Unknown first GHDL version string '{versionLine}'.")

self._major = int(match["major"])
self._minor = int(match["minor"])
self._micro = int(match["micro"])
self._dev = "dev" in match.groups()
self._commitsSinceLastTag = int(match["cslt"])
self._gitHash = match["hash"]
self._dirty = "dirty" in match.groups()
self._edition = match["edition"]

match = re_search(
r"\s*[\w\s]+:\s(?P<major>\d+)\.(?P<minor>\d+)\.(?P<micro>\d+)", gnatLine)

if match is None:
raise CLIToolException(f"Unknown second GHDL version string '{gnatLine}'.")

self._gnatCompiler = (match["major"], match["minor"], match["micro"])

match = re_search(
r"\s*(?P<backend>\w+)\scode\sgenerator", backendLine)

if match is None:
raise CLIToolException(f"Unknown third GHDL version string '{backendLine}'.")

self._backend = match["backend"]

@property
def Major(self) -> int:
return self._major

@property
def Minor(self) -> int:
return self._minor

@property
def Micro(self) -> int:
return self._micro

@property
def Dev(self) -> bool:
return self._dev

@property
def CommitsSinceLastTag(self) -> int:
return self._commitsSinceLastTag

@property
def GitHash(self) -> str:
return self._gitHash

@property
def Dirty(self) -> bool:
return self._dirty

@property
def Edition(self) -> str:
return self._edition

def __str__(self) -> str:
dev = f"-dev" if self._dev else ""
return f"{self._major}.{self._minor}.{self._micro}{dev}"

def __repr__(self) -> str:
return f"{self.__str__()} (Backend: {self._backend}; Git: {self._gitHash})"


@export
class GHDL(Executable):
Expand Down Expand Up @@ -227,8 +329,8 @@ class FlagSyntesisBindingRule(ShortFlag, name="error", pattern="-W{0}"):
"""Turns warnings into errors."""

@CLIArgument()
class OptionPath(PathListArgument):
"""Add VHDL file to analyze."""
class OptionPaths(PathListArgument):
"""Add list of VHDL files to analyze."""

@CLIArgument()
class OptionTopLevel(StringArgument):
Expand Down Expand Up @@ -335,3 +437,24 @@ def GetGHDLAsSimulator(self, std: VHDLVersion = None, ieee: str = None):
self._SetParameters(tool, std, ieee)

return tool

def Help(self):
tool = GHDL(executablePath=self._executablePath)

tool[tool.CommandHelp] = True

tool.StartProcess()
return "\n".join(tool.GetLineReader())

def Version(self):
tool = GHDL(executablePath=self._executablePath)

tool[tool.CommandVersion] = True

tool.StartProcess()
iterator = iter(tool.GetLineReader())
firstLine = next(iterator)
secondLine = next(iterator)
thirdLine = next(iterator)

return GHDLVersion(firstLine, secondLine, thirdLine)
8 changes: 8 additions & 0 deletions pyEDAA/CLITool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,11 @@
__license__ = "Apache License, Version 2.0"
__version__ = "0.3.0"
__keywords__ = ["cli", "abstraction layer", "eda"]

from pyTooling.Decorators import export
from pyTooling.Exceptions import ExceptionBase


@export
class CLIToolException(ExceptionBase):
pass
58 changes: 53 additions & 5 deletions tests/unit/GHDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,45 +39,58 @@


class CommonOptions(TestCase, Helper):
_binaryDirectoryPath = Path(os_getenv("GHDL_PREFIX", default="/usr/local")) / "bin"
_binaryDirectoryPath = Path(os_getenv("GHDL_PREFIX", default="/usr/local/lib/ghdl")) / "../../bin"

@classmethod
def setUpClass(cls) -> None:
print(f"\nPlatform: {sys_platform}")
# print(f"\nPlatform: {sys_platform}")
if sys_platform in ("linux", "darwin"):
ghdlBinaryPath: Path = cls._binaryDirectoryPath / "ghdl"
print(f"Creating dummy file '{ghdlBinaryPath}': ", end="")
ghdlBinaryPath.touch()
print(f"DONE" if ghdlBinaryPath.exists() else f"FAILED")

def test_Help(self):
print()

tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
tool[tool.CommandHelp] = True

executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
self.assertEqual(f"[\"{executable}\", \"help\"]", repr(tool))

helpText = tool.Help()
print(helpText)

def test_Version(self):
print()

tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
tool[tool.CommandVersion] = True

executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
self.assertEqual(f"[\"{executable}\", \"version\"]", repr(tool))

version = tool.Version()
print(str(version))
print(repr(version))


class Analyze(TestCase, Helper):
_binaryDirectoryPath = Path(os_getenv("GHDL_PREFIX", default="/usr/local")) / "bin"
_binaryDirectoryPath = Path(os_getenv("GHDL_PREFIX", default="/usr/local/lib/ghdl")) / "../../bin"

@classmethod
def setUpClass(cls) -> None:
print(f"\nPlatform: {sys_platform}")
# print(f"\nPlatform: {sys_platform}")
if sys_platform in ("linux", "darwin"):
ghdlBinaryPath: Path = cls._binaryDirectoryPath / "ghdl"
print(f"Creating dummy file '{ghdlBinaryPath}': ", end="")
ghdlBinaryPath.touch()
print(f"DONE" if ghdlBinaryPath.exists() else f"FAILED")

def test_AnalyzeFile(self):
def test_Analyze(self):
print()

tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
tool[tool.CommandAnalyze] = True
tool[tool.FlagVHDLStandard] = "08"
Expand All @@ -90,7 +103,37 @@ def test_AnalyzeFile(self):
executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
self.assertEqual(f"[\"{executable}\", \"analyze\", \"--std=08\", \"-fsynopsys\", \"-frelaxed\", \"-fexplicit\", \"--work=lib_Test\", \"--mb-comments\"]", repr(tool))

tool.StartProcess()
for line in tool.GetLineReader():
print(line)
tool.Terminate()
print(tool.ExitCode)

def test_AnalyzeFile(self):
print()

tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
tool[tool.CommandAnalyze] = True
tool[tool.FlagVHDLStandard] = "08"
tool[tool.FlagSynopsys] = True
tool[tool.FlagRelaxed] = True
tool[tool.FlagExplicit] = True
tool[tool.FlagMultiByteComments] = True
tool[tool.FlagLibrary] = "lib_Test"
tool[tool.OptionPaths] = (Path("example/file_A1.vhdl"), )

executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
self.assertEqual(f"[\"{executable}\", \"analyze\", \"--std=08\", \"-fsynopsys\", \"-frelaxed\", \"-fexplicit\", \"--work=lib_Test\", \"--mb-comments\", \"example\\file_A1.vhdl\"]", repr(tool))

tool.StartProcess()
for line in tool.GetLineReader():
print(line)
tool.Terminate()
print(tool.ExitCode)

def test_DeriveAnalyzer(self):
print()

tool = GHDL(binaryDirectoryPath=self._binaryDirectoryPath)
tool[tool.FlagVHDLStandard] = "08"
tool[tool.FlagSynopsys] = True
Expand All @@ -103,3 +146,8 @@ def test_DeriveAnalyzer(self):

executable = self.getExecutablePath("ghdl", self._binaryDirectoryPath)
self.assertEqual(f"[\"{executable}\", \"analyze\", \"--std=08\", \"-fsynopsys\", \"-frelaxed\", \"-fexplicit\", \"--work=lib_Test\", \"--mb-comments\"]", repr(derived))

derived.StartProcess()
for line in derived.GetLineReader():
print(line)
print(derived.ExitCode)

0 comments on commit 807336f

Please sign in to comment.