Source code for pyEDAA.ProjectModel

# ==================================================================================================================== #
#               _____ ____    _        _      ____            _           _   __  __           _      _                #
#   _ __  _   _| ____|  _ \  / \      / \    |  _ \ _ __ ___ (_) ___  ___| |_|  \/  | ___   __| | ___| |               #
#  | '_ \| | | |  _| | | | |/ _ \    / _ \   | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ |               #
#  | |_) | |_| | |___| |_| / ___ \  / ___ \ _|  __/| | | (_) | |  __/ (__| |_| |  | | (_) | (_| |  __/ |               #
#  | .__/ \__, |_____|____/_/   \_\/_/   \_(_)_|   |_|  \___// |\___|\___|\__|_|  |_|\___/ \__,_|\___|_|               #
#  |_|    |___/                                            |__/                                                        #
# ==================================================================================================================== #
# Authors:                                                                                                             #
#   Patrick Lehmann                                                                                                    #
#                                                                                                                      #
# License:                                                                                                             #
# ==================================================================================================================== #
# Copyright 2017-2026 Patrick Lehmann - Boetzingen, Germany                                                            #
# Copyright 2014-2016 Technische Universität Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture     #
#                                                                                                                      #
# Licensed under the Apache License, Version 2.0 (the "License");                                                      #
# you may not use this file except in compliance with the License.                                                     #
# You may obtain a copy of the License at                                                                              #
#                                                                                                                      #
#   http://www.apache.org/licenses/LICENSE-2.0                                                                         #
#                                                                                                                      #
# Unless required by applicable law or agreed to in writing, software                                                  #
# distributed under the License is distributed on an "AS IS" BASIS,                                                    #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                             #
# See the License for the specific language governing permissions and                                                  #
# limitations under the License.                                                                                       #
#                                                                                                                      #
# SPDX-License-Identifier: Apache-2.0                                                                                  #
# ==================================================================================================================== #
#
"""An abstract model of EDA tool projects."""
__author__ =            "Patrick Lehmann"
__email__ =             "[email protected]"
__copyright__ =         "2014-2026, Patrick Lehmann, Unai Martinez-Corral"
__license__ =           "Apache License, Version 2.0"
__version__ =           "0.6.4"
__keywords__ =          ["eda project", "model", "abstract", "xilinx", "vivado", "osvvm", "file set", "file group", "test bench", "test harness"]
__project_url__ =       "https://github.com/edaa-org/pyEDAA.ProjectModel"
__documentation_url__ = "https://edaa-org.github.io/pyEDAA.ProjectModel"
__issue_tracker_url__ = "https://GitHub.com/edaa-org/pyEDAA.ProjectModel/issues"

from os.path import relpath as path_relpath
from pathlib import Path as pathlib_Path
from sys     import version_info
from typing  import Dict, Union, Optional as Nullable, List, Iterable, Generator, Tuple, Any as typing_Any, Type, Set, Self

from pyTooling.Common      import getFullyQualifiedName
from pyTooling.Decorators  import export
from pyTooling.MetaClasses import ExtendedType
from pyTooling.Graph       import Graph, Vertex
from pySVModel             import SystemVerilogVersion
from pyVHDLModel           import VHDLVersion
from pySystemRDLModel      import SystemRDLVersion


[docs] @export class Attribute(metaclass=ExtendedType): KEY: str VALUE_TYPE: typing_Any @staticmethod def resolve(obj: typing_Any, key: Type['Attribute']): if isinstance(obj, File): return obj._fileSet[key] elif isinstance(obj, FileSet): return obj._design[key] elif isinstance(obj, Design): return obj._project[key] else: raise Exception("Resolution error")
[docs] @export class FileType(ExtendedType): """ A :term:`meta-class` to construct *FileType* classes. Modifications done by this meta-class: * Register all classes of type :class:`FileType` or derived variants in a class field :attr:`FileType.FileTypes` in this meta-class. """ FileTypes: Dict[str, 'FileType'] = {} #: Dictionary of all classes of type :class:`FileType` or derived variants Any: 'FileType'
[docs] def __init__(cls, name: str, bases: Tuple[type, ...], dictionary: Dict[str, typing_Any], **kwargs) -> None: super().__init__(name, bases, dictionary, **kwargs) cls.Any = cls
[docs] def __new__(cls, className, baseClasses, classMembers: Dict, *args, **kwargs) -> Self: fileType = super().__new__(cls, className, baseClasses, classMembers, *args, **kwargs) cls.FileTypes[className] = fileType return fileType
def __getattr__(cls, item) -> 'FileType': if item[:2] != "__" and item[-2:] != "__": return cls.FileTypes[item] else: return super().__getattribute__(item) def __contains__(cls, item) -> bool: return issubclass(item, cls)
[docs] @export class File(metaclass=FileType, slots=True): """ A :term:`File` represents a file in a design. This :term:`base-class` is used for all derived file classes. A file can be created standalone and later associated to a fileset, design and project. Or a fileset, design and/or project can be associated immediately while creating a file. :arg path: Relative or absolute path to the file. :arg project: Project the file is associated with. :arg design: Design the file is associated with. :arg fileSet: Fileset the file is associated with. """ _path: pathlib_Path _fileType: 'FileType' _project: Nullable['Project'] _design: Nullable['Design'] _fileSet: Nullable['FileSet'] _attributes: Dict[Type[Attribute], typing_Any]
[docs] def __init__( self, path: pathlib_Path, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None ) -> None: self._fileType = getattr(FileTypes, self.__class__.__name__) self._path = path if project is not None: self._project = project self._design = design if fileSet is not None: self.FileSet = fileSet elif design is not None: self._project = design._project self._design = design self.FileSet = design.DefaultFileSet if fileSet is None else fileSet elif fileSet is not None: design = fileSet._design if design is not None: self._project = design._project else: self._project = None self._design = design self.FileSet = fileSet else: self._project = None self._design = None self._fileSet = None self._attributes = {} self._registerAttributes()
def _registerAttributes(self) -> None: pass @property def FileType(self) -> 'FileType': """Read-only property to return the file type of this file.""" return self._fileType @property def Path(self) -> pathlib_Path: """ Read-only property to access the path to the file. :returns: The file's path. """ return self._path # TODO: setter? @property def ResolvedPath(self) -> pathlib_Path: """Read-only property returning the resolved path of this file.""" if self._path.is_absolute(): return self._path.resolve() elif self._fileSet is not None: path = (self._fileSet.ResolvedPath / self._path).resolve() if path.is_absolute(): return path else: # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath return pathlib_Path(path_relpath(path, pathlib_Path.cwd())) else: # TODO: message and exception type raise Exception("") @property def Project(self) -> Nullable['Project']: """Property setting or returning the project this file is used in.""" return self._project @Project.setter def Project(self, value: 'Project') -> None: self._project = value if self._fileSet is None: self._project.DefaultDesign.DefaultFileSet.AddFile(self) @property def Design(self) -> Nullable['Design']: """Property setting or returning the design this file is used in.""" return self._design @Design.setter def Design(self, value: 'Design') -> None: self._design = value if self._fileSet is None: self._design.DefaultFileSet.AddFile(self) if self._project is None: self._project = value._project elif self._project is not value._project: raise Exception("The design's project is not identical to the already assigned project.") @property def FileSet(self) -> Nullable['FileSet']: """Property setting or returning the fileset this file is used in.""" return self._fileSet @FileSet.setter def FileSet(self, value: 'FileSet') -> None: self._fileSet = value value._files.append(self)
[docs] def Validate(self) -> None: """Validate this file.""" if self._path is None: raise Exception("Validation: File has no path.") try: path = self.ResolvedPath except Exception as ex: raise Exception(f"Validation: File '{self._path}' could not compute resolved path.") from ex if not path.exists(): raise Exception(f"Validation: File '{self._path}' (={path}) does not exist.") if not path.is_file(): raise Exception(f"Validation: File '{self._path}' (={path}) is not a file.") if self._fileSet is None: raise Exception(f"Validation: File '{self._path}' has no fileset.") if self._design is None: raise Exception(f"Validation: File '{self._path}' has no design.") if self._project is None: raise Exception(f"Validation: File '{self._path}' has no project.")
[docs] def __len__(self) -> int: """ Returns number of attributes set on this file. :returns: The number of attributes set on this file. """ return len(self._attributes)
[docs] def __getitem__(self, key: Type[Attribute]) -> typing_Any: """Index access for returning attributes on this file. :param key: The attribute type. :returns: The attribute's value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") try: return self._attributes[key] except KeyError: try: return key.resolve(self, key) except KeyError: attribute = key() self._attributes[key] = attribute return attribute
[docs] def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: """ Index access for adding or setting attributes on this file. :param key: The attribute type. :param value: The attributes value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") self._attributes[key] = value
[docs] def __delitem__(self, key: Type[Attribute]) -> None: """ Index access for deleting attributes on this file. :param key: The attribute type. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") del self._attributes[key]
[docs] def __str__(self) -> str: return f"{self._path}"
FileTypes = File
[docs] @export class HumanReadableContent(metaclass=ExtendedType, mixin=True): """A file type representing human-readable contents."""
[docs] @export class XMLContent(HumanReadableContent, mixin=True): """A file type representing XML contents."""
[docs] @export class YAMLContent(HumanReadableContent, mixin=True): """A file type representing YAML contents."""
[docs] @export class JSONContent(HumanReadableContent, mixin=True): """A file type representing JSON contents."""
[docs] @export class INIContent(HumanReadableContent, mixin=True): """A file type representing INI contents."""
[docs] @export class TOMLContent(HumanReadableContent, mixin=True): """A file type representing TOML contents."""
[docs] @export class TCLContent(HumanReadableContent, mixin=True): """A file type representing content in TCL code."""
[docs] @export class SDCContent(TCLContent, mixin=True): """A file type representing contents as Synopsys Design Constraints (SDC)."""
[docs] @export class PythonContent(HumanReadableContent, mixin=True): """A file type representing contents as Python source code."""
[docs] @export class TextFile(File, HumanReadableContent): """A text file (``*.txt``)."""
[docs] @export class LogFile(File, HumanReadableContent): """A log file (``*.log``)."""
[docs] @export class XMLFile(File, XMLContent): """An XML file (``*.xml``)."""
[docs] @export class SourceFile(File): """Base-class of all source files."""
[docs] @export class HDLSourceFile(SourceFile): """Base-class of all HDL source files."""
[docs] @export class RDLSourceFile(SourceFile): """Base-class of all RDL source files."""
[docs] @export class NetlistFile(SourceFile): """Base-class of all netlist source files."""
[docs] @export class EDIFNetlistFile(NetlistFile): """Netlist file in EDIF (Electronic Design Interchange Format)."""
[docs] @export class TCLSourceFile(SourceFile, TCLContent): """A TCL source file."""
[docs] @export class VHDLSourceFile(HDLSourceFile, HumanReadableContent): """ A VHDL source file (of any language version). :arg path: Relative or absolute path to the file. :arg vhdlLibrary: VHDLLibrary this VHDL source file is associated wih. :arg vhdlVersion: VHDLVersion this VHDL source file is associated wih. :arg project: Project the file is associated with. :arg design: Design the file is associated with. :arg fileSet: Fileset the file is associated with. """ _vhdlLibrary: Nullable['VHDLLibrary'] _vhdlVersion: VHDLVersion
[docs] def __init__(self, path: pathlib_Path, vhdlLibrary: Union[str, 'VHDLLibrary'] = None, vhdlVersion: Nullable[VHDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None: super().__init__(path, project, design, fileSet) if isinstance(vhdlLibrary, str): if design is not None: try: vhdlLibrary = design.VHDLLibraries[vhdlLibrary] except KeyError as ex: raise Exception(f"VHDL library '{vhdlLibrary}' not found in design '{design.Name}'.") from ex elif project is not None: try: vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary] except KeyError as ex: raise Exception(f"VHDL library '{vhdlLibrary}' not found in default design '{project.DefaultDesign.Name}'.") from ex else: raise Exception(f"Can't lookup VHDL library because neither 'project' nor 'design' is given as a parameter.") elif isinstance(vhdlLibrary, VHDLLibrary): self._vhdlLibrary = vhdlLibrary vhdlLibrary.AddFile(self) elif vhdlLibrary is None: self._vhdlLibrary = None else: ex = TypeError(f"Parameter 'vhdlLibrary' is neither a 'str' nor 'VHDLibrary'.") if version_info >= (3, 11): # pragma: no cover ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.") raise ex self._vhdlVersion = vhdlVersion
[docs] def Validate(self) -> None: """Validate this VHDL source file.""" super().Validate() try: _ = self.VHDLLibrary except Exception as ex: raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLLibrary assigned.") from ex try: _ = self.VHDLVersion except Exception as ex: raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLVersion assigned.") from ex
@property def VHDLLibrary(self) -> 'VHDLLibrary': """Property setting or returning the VHDL library this VHDL source file is used in.""" if self._vhdlLibrary is not None: return self._vhdlLibrary elif self._fileSet is not None: return self._fileSet.VHDLLibrary else: raise Exception("VHDLLibrary was neither set locally nor globally.") @VHDLLibrary.setter def VHDLLibrary(self, value: 'VHDLLibrary') -> None: self._vhdlLibrary = value value._files.append(self) @property def VHDLVersion(self) -> VHDLVersion: """Property setting or returning the VHDL version this VHDL source file is used in.""" if self._vhdlVersion is not None: return self._vhdlVersion elif self._fileSet is not None: return self._fileSet.VHDLVersion else: raise Exception("VHDLVersion was neither set locally nor globally.") @VHDLVersion.setter def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value
[docs] def __repr__(self) -> str: return f"<VHDL file: '{self.ResolvedPath}'; lib: '{self.VHDLLibrary}'; version: {self.VHDLVersion}>"
class VerilogMixIn(metaclass=ExtendedType, mixin=True): @property def VerilogVersion(self) -> SystemVerilogVersion: """Property setting or returning the Verilog version this Verilog source file is used in.""" if self._version is not None: return self._version elif self._fileSet is not None: return self._fileSet.VerilogVersion else: raise Exception("VerilogVersion was neither set locally nor globally.") @VerilogVersion.setter def VerilogVersion(self, value: SystemVerilogVersion) -> None: self._version = value class SystemVerilogMixIn(metaclass=ExtendedType, mixin=True): @property def SVVersion(self) -> SystemVerilogVersion: """Property setting or returning the SystemVerilog version this SystemVerilog source file is used in.""" if self._version is not None: return self._version elif self._fileSet is not None: return self._fileSet.SVVersion else: raise Exception("SVVersion was neither set locally nor globally.") @SVVersion.setter def SVVersion(self, value: SystemVerilogVersion) -> None: self._version = value
[docs] @export class VerilogBaseFile(HDLSourceFile, HumanReadableContent): _version: SystemVerilogVersion
[docs] def __init__(self, path: pathlib_Path, version: Nullable[SystemVerilogVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None: super().__init__(path, project, design, fileSet) self._version = version
[docs] @export class VerilogSourceFile(VerilogBaseFile, VerilogMixIn): """A Verilog source file (of any language version)."""
[docs] @export class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn): """A Verilog header file (of any language version)."""
[docs] @export class SystemVerilogBaseFile(VerilogBaseFile): ...
[docs] @export class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn): """A SystemVerilog source file (of any language version)."""
[docs] @export class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn): """A SystemVerilog header file (of any language version)."""
[docs] @export class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent): """A SystemRDL source file (of any language version).""" _srdlVersion: SystemRDLVersion
[docs] def __init__(self, path: pathlib_Path, srdlVersion: Nullable[SystemRDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None: super().__init__(path, project, design, fileSet) self._srdlVersion = srdlVersion
@property def SystemRDLVersion(self) -> SystemRDLVersion: """Property setting or returning the SystemRDL version this SystemRDL source file is used in.""" if self._srdlVersion is not None: return self._srdlVersion elif self._fileSet is not None: return self._fileSet.SRDLVersion else: raise Exception("SRDLVersion was neither set locally nor globally.") @SystemRDLVersion.setter def SystemRDLVersion(self, value: SystemRDLVersion) -> None: self._srdlVersion = value
[docs] @export class PythonSourceFile(SourceFile, PythonContent): """A Python source file."""
# TODO: move to a Cocotb module
[docs] @export class CocotbPythonFile(PythonSourceFile): """A Python source file used by Cocotb."""
[docs] @export class ConstraintFile(File, HumanReadableContent): """Base-class of all constraint files."""
[docs] @export class ProjectFile(File): """Base-class of all tool-specific project files."""
[docs] @export class CSourceFile(SourceFile): """Base-class of all ANSI-C source files."""
[docs] @export class CppSourceFile(SourceFile): """Base-class of all ANSI-C++ source files."""
[docs] @export class SettingFile(File): """Base-class of all tool-specific setting files."""
[docs] @export class SimulationAnalysisFile(File): """Base-class of all tool-specific analysis files."""
[docs] @export class SimulationElaborationFile(File): """Base-class of all tool-specific elaboration files."""
[docs] @export class SimulationStartFile(File): """Base-class of all tool-specific simulation start-up files."""
[docs] @export class SimulationRunFile(File): """Base-class of all tool-specific simulation run (execution) files."""
[docs] @export class WaveformConfigFile(File): """Base-class of all tool-specific waveform configuration files."""
[docs] @export class WaveformDatabaseFile(File): """Base-class of all tool-specific waveform database files."""
[docs] @export class WaveformExchangeFile(File): """Base-class of all tool-independent waveform exchange files."""
[docs] @export class FileSet(metaclass=ExtendedType, slots=True): """ A :term:`FileSet` represents a group of files. Filesets can have sub-filesets. The order of insertion is preserved. A fileset can be created standalone and later associated to another fileset, design and/or project. Or a fileset, design and/or project can be associated immediately while creating the fileset. :arg name: Name of this fileset. :arg topLevel: Name of the fileset's toplevel. :arg directory: Path of this fileset (absolute or relative to a parent fileset or design). :arg project: Project the file is associated with. :arg design: Design the file is associated with. :arg parent: Parent fileset if this fileset is nested. :arg vhdlLibrary: Default VHDL library for files in this fileset, if not specified for the file itself. :arg vhdlVersion: Default VHDL version for files in this fileset, if not specified for the file itself. :arg verilogVersion: Default Verilog version for files in this fileset, if not specified for the file itself. :arg svVersion: Default SystemVerilog version for files in this fileset, if not specified for the file itself. :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself. """ _name: str _topLevel: Nullable[str] _project: Nullable['Project'] _design: Nullable['Design'] _directory: pathlib_Path _parent: Nullable['FileSet'] _fileSets: Dict[str, 'FileSet'] _files: List[File] _set: Set _attributes: Dict[Type[Attribute], typing_Any] _vhdlLibraries: Dict[str, 'VHDLLibrary'] _vhdlLibrary: 'VHDLLibrary' _vhdlVersion: VHDLVersion _verilogVersion: SystemVerilogVersion _svVersion: SystemVerilogVersion _srdlVersion: SystemRDLVersion
[docs] def __init__( self, name: str, topLevel: Nullable[str] = None, directory: pathlib_Path = pathlib_Path("."), project: Nullable["Project"] = None, design: Nullable["Design"] = None, parent: Nullable['FileSet'] = None, vhdlLibrary: Union[str, 'VHDLLibrary'] = None, vhdlVersion: Nullable[VHDLVersion] = None, verilogVersion: Nullable[SystemVerilogVersion] = None, svVersion: Nullable[SystemVerilogVersion] = None, srdlVersion: Nullable[SystemRDLVersion] = None ) -> None: self._name = name self._topLevel = topLevel if project is not None: self._project = project self._design = design if design is not None else project.DefaultDesign elif design is not None: self._project = design._project self._design = design else: self._project = None self._design = None self._directory = directory self._parent = parent self._fileSets = {} self._files = [] self._set = set() if design is not None: design._fileSets[name] = self self._attributes = {} self._vhdlLibraries = {} # TODO: handle if vhdlLibrary is a string self._vhdlLibrary = vhdlLibrary self._vhdlVersion = vhdlVersion self._verilogVersion = verilogVersion self._svVersion = svVersion self._srdlVersion = srdlVersion
@property def Name(self) -> str: """Property setting or returning the fileset's name.""" return self._name @Name.setter def Name(self, value: str) -> None: self._name = value @property def TopLevel(self) -> str: """Property setting or returning the fileset's toplevel.""" return self._topLevel @TopLevel.setter def TopLevel(self, value: str) -> None: self._topLevel = value @property def Project(self) -> Nullable['Project']: """Property setting or returning the project this fileset is used in.""" return self._project @Project.setter def Project(self, value: 'Project') -> None: self._project = value @property def Design(self) -> Nullable['Design']: """Property setting or returning the design this fileset is used in.""" if self._design is not None: return self._design elif self._parent is not None: return self._parent.Design else: return None # TODO: raise exception instead # QUESTION: how to handle if design and parent is set? @Design.setter def Design(self, value: 'Design') -> None: self._design = value if self._project is None: self._project = value._project elif self._project is not value._project: raise Exception("The design's project is not identical to the already assigned project.") @property def Directory(self) -> pathlib_Path: """Property setting or returning the directory this fileset is located in.""" return self._directory @Directory.setter def Directory(self, value: pathlib_Path) -> None: self._directory = value @property def ResolvedPath(self) -> pathlib_Path: """Read-only property returning the resolved path of this fileset.""" if self._directory.is_absolute(): return self._directory.resolve() else: if self._parent is not None: directory = self._parent.ResolvedPath elif self._design is not None: directory = self._design.ResolvedPath elif self._project is not None: directory = self._project.ResolvedPath else: # TODO: message and exception type raise Exception("") directory = (directory / self._directory).resolve() if directory.is_absolute(): return directory else: # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath return pathlib_Path(path_relpath(directory, pathlib_Path.cwd())) @property def Parent(self) -> Nullable['FileSet']: """Property setting or returning the parent fileset this fileset is used in.""" return self._parent @Parent.setter def Parent(self, value: 'FileSet') -> None: self._parent = value value._fileSets[self._name] = self # TODO: check it it already exists # QUESTION: make an Add fileset method? @property def FileSets(self) -> Dict[str, 'FileSet']: """Read-only property returning the dictionary of sub-filesets.""" return self._fileSets
[docs] def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[bool, str, 'FileSet'] = None) -> Generator[File, None, None]: """ Method returning the files of this fileset. :arg fileType: A filter for file types. Default: ``Any``. :arg fileSet: Specifies how to handle sub-filesets. """ if fileSet is False: for file in self._files: if file.FileType in fileType: yield file elif fileSet is None: for fileSet in self._fileSets.values(): for file in fileSet.Files(fileType): yield file for file in self._files: if file.FileType in fileType: yield file else: if isinstance(fileSet, str): fileSetName = fileSet try: fileSet = self._fileSets[fileSetName] except KeyError as ex: raise Exception(f"Fileset {fileSetName} not bound to fileset {self.Name}.") from ex elif not isinstance(fileSet, FileSet): raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.") for file in fileSet.Files(fileType): yield file
[docs] def AddFileSet(self, fileSet: "FileSet") -> None: """ Method to add a single sub-fileset to this fileset. :arg fileSet: A fileset to add to this fileset as sub-fileset. """ if not isinstance(fileSet, FileSet): raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.") elif fileSet in self._fileSets: raise Exception("Sub-fileset already contains this fileset.") elif fileSet.Name in self._fileSets.keys(): raise Exception(f"Fileset already contains a sub-fileset named '{fileSet.Name}'.") self._fileSets[fileSet.Name] = fileSet fileSet._parent = self
[docs] def AddFileSets(self, fileSets: Iterable["FileSet"]) -> None: """ Method to add a multiple sub-filesets to this fileset. :arg fileSets: An iterable of filesets to add each to the fileset. """ for fileSet in fileSets: self.AddFileSet(fileSet)
@property def FileSetCount(self) -> int: """Returns number of file sets excl. sub-filesets.""" return len(self._fileSets) @property def TotalFileSetCount(self) -> int: """Returns number of file sets incl. sub-filesets.""" fileSetCount = len(self._fileSets) for fileSet in self._fileSets.values(): fileSetCount += fileSet.TotalFileSetCount return fileSetCount
[docs] def AddFile(self, file: File) -> None: """ Method to add a single file to this fileset. :arg file: A file to add to this fileset. """ if not isinstance(file, File): raise TypeError("Parameter 'file' is not of type ProjectModel.File.") elif file._fileSet is not None: ex = ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}'.") if version_info >= (3, 11): # pragma: no cover ex.add_note(f"A file can't be assigned to another fileset.") raise ex elif file in self._set: ex = ValueError(f"File '{file.Path!s}' is already part of this fileset.") if version_info >= (3, 11): # pragma: no cover ex.add_note(f"A file can't be added twice to a fileset.") raise ex self._files.append(file) self._set.add(file) file._fileSet = self
[docs] def AddFiles(self, files: Iterable[File]) -> None: """ Method to add a multiple files to this fileset. :arg files: An iterable of files to add each to the fileset. """ for file in files: self.AddFile(file)
@property def FileCount(self) -> int: """Returns number of files excl. sub-filesets.""" return len(self._files) @property def TotalFileCount(self) -> int: """Returns number of files incl. the files in sub-filesets.""" fileCount = len(self._files) for fileSet in self._fileSets.values(): fileCount += fileSet.FileCount return fileCount
[docs] def Validate(self) -> None: """Validate this fileset.""" if self._name is None or self._name == "": raise Exception("Validation: FileSet has no name.") if self._directory is None: raise Exception(f"Validation: FileSet '{self._name}' has no directory.") try: path = self.ResolvedPath except Exception as ex: raise Exception(f"Validation: FileSet '{self._name}' could not compute resolved path.") from ex if not path.exists(): raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' does not exist.") if not path.is_dir(): raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' is not a directory.") if self._design is None: raise Exception(f"Validation: FileSet '{self._directory}' has no design.") if self._project is None: raise Exception(f"Validation: FileSet '{self._directory}' has no project.") for fileSet in self._fileSets.values(): fileSet.Validate() for file in self._files: file.Validate()
def GetOrCreateVHDLLibrary(self, name) -> 'VHDLLibrary': if name in self._vhdlLibraries: return self._vhdlLibraries[name] elif name in self._design._vhdlLibraries: library = self._design._vhdlLibraries[name] self._vhdlLibraries[name] = library return library else: library = VHDLLibrary(name, design=self._design, vhdlVersion=self._vhdlVersion) self._vhdlLibraries[name] = library return library @property def VHDLLibrary(self) -> 'VHDLLibrary': """Property setting or returning the VHDL library of this fileset.""" if self._vhdlLibrary is not None: return self._vhdlLibrary elif self._parent is not None: return self._parent.VHDLLibrary elif self._design is not None: return self._design.VHDLLibrary # FIXME: no library property. add a DefaultVHDLLibrary property else: raise Exception("VHDLLibrary was neither set locally nor globally.") @VHDLLibrary.setter def VHDLLibrary(self, value: 'VHDLLibrary') -> None: self._vhdlLibrary = value @property def VHDLVersion(self) -> VHDLVersion: """Property setting or returning the VHDL version of this fileset.""" if self._vhdlVersion is not None: return self._vhdlVersion elif self._parent is not None: return self._parent.VHDLVersion elif self._design is not None: return self._design.VHDLVersion else: raise Exception("VHDLVersion was neither set locally nor globally.") @VHDLVersion.setter def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value @property def VerilogVersion(self) -> SystemVerilogVersion: """Property setting or returning the Verilog version of this fileset.""" if self._verilogVersion is not None: return self._verilogVersion elif self._parent is not None: return self._parent.VerilogVersion elif self._design is not None: return self._design.VerilogVersion else: raise Exception("VerilogVersion was neither set locally nor globally.") @VerilogVersion.setter def VerilogVersion(self, value: SystemVerilogVersion) -> None: self._verilogVersion = value @property def SVVersion(self) -> SystemVerilogVersion: """Property setting or returning the SystemVerilog version of this fileset.""" if self._svVersion is not None: return self._svVersion elif self._parent is not None: return self._parent.SVVersion elif self._design is not None: return self._design.SVVersion else: raise Exception("SVVersion was neither set locally nor globally.") @SVVersion.setter def SVVersion(self, value: SystemVerilogVersion) -> None: self._svVersion = value @property def SRDLVersion(self) -> SystemRDLVersion: if self._srdlVersion is not None: return self._srdlVersion elif self._parent is not None: return self._parent.SRDLVersion elif self._design is not None: return self._design.SRDLVersion else: raise Exception("SRDLVersion was neither set locally nor globally.") @SRDLVersion.setter def SRDLVersion(self, value: SystemRDLVersion) -> None: self._srdlVersion = value
[docs] def __len__(self) -> int: """ Returns number of attributes set on this fileset. :returns: The number of attributes set on this fileset. """ return len(self._attributes)
[docs] def __getitem__(self, key: Type[Attribute]) -> typing_Any: """Index access for returning attributes on this fileset. :param key: The attribute type. :returns: The attribute's value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") try: return self._attributes[key] except KeyError: return key.resolve(self, key)
[docs] def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: """ Index access for adding or setting attributes on this fileset. :param key: The attribute type. :param value: The attributes value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") self._attributes[key] = value
[docs] def __delitem__(self, key: Type[Attribute]) -> None: """ Index access for deleting attributes on this fileset. :param key: The attribute type. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") del self._attributes[key]
[docs] def __str__(self) -> str: """Returns the fileset's name.""" return self._name
[docs] @export class VHDLLibrary(metaclass=ExtendedType, slots=True): """ A :term:`VHDLLibrary` represents a group of VHDL source files compiled into the same VHDL library. :arg name: The VHDL libraries' name. :arg project: Project the VHDL library is associated with. :arg design: Design the VHDL library is associated with. :arg vhdlVersion: Default VHDL version for files in this VHDL library, if not specified for the file itself. """ _name: str _project: Nullable['Project'] _design: Nullable['Design'] _files: List[File] _vhdlVersion: VHDLVersion _attributes: Dict[Attribute, typing_Any] _dependencyNode: Vertex
[docs] def __init__( self, name: str, project: Nullable["Project"] = None, design: Nullable["Design"] = None, vhdlVersion: Nullable[VHDLVersion] = None ) -> None: self._name = name self._attributes = {} if project is not None: self._project = project self._design = project._defaultDesign if design is None else design self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph) if name in self._design._vhdlLibraries: raise Exception(f"Library '{name}' already in design '{self._design.Name}'.") else: self._design._vhdlLibraries[name] = self elif design is not None: self._project = design._project self._design = design self._dependencyNode = Vertex(value=self, graph=design._vhdlLibraryDependencyGraph) if name in design._vhdlLibraries: raise Exception(f"Library '{name}' already in design '{design.Name}'.") else: design._vhdlLibraries[name] = self else: self._project = None self._design = None self._dependencyNode = None self._files = [] self._vhdlVersion = vhdlVersion
@property def Name(self) -> str: return self._name @property def Project(self) -> Nullable['Project']: """Property setting or returning the project this VHDL library is used in.""" return self._project @Project.setter def Project(self, value: 'Project') -> None: if not isinstance(value, Project): raise TypeError("Parameter 'value' is not of type 'Project'.") if value is None: # TODO: unlink VHDLLibrary from project self._project = None else: self._project = value if self._design is None: self._design = value._defaultDesign @property def Design(self) -> Nullable['Design']: """Property setting or returning the design this VHDL library is used in.""" return self._design @Design.setter def Design(self, value: 'Design') -> None: if not isinstance(value, Design): raise TypeError("Parameter 'value' is not of type 'Design'.") if value is None: # TODO: unlink VHDLLibrary from design self._design = None else: if self._design is None: self._design = value self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph) elif self._design is not value: # TODO: move VHDLLibrary to other design # TODO: create new vertex in dependency graph and remove vertex from old graph self._design = value else: pass if self._project is None: self._project = value._project elif self._project is not value._project: raise Exception("The design's project is not identical to the already assigned project.") @property def Files(self) -> Generator[File, None, None]: """Read-only property to return all files in this VHDL library.""" for file in self._files: yield file @property def VHDLVersion(self) -> VHDLVersion: """Property setting or returning the VHDL version of this VHDL library.""" if self._vhdlVersion is not None: return self._vhdlVersion elif self._design is not None: return self._design.VHDLVersion else: raise Exception("VHDLVersion is not set on VHDLLibrary nor parent object.") @VHDLVersion.setter def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value def AddDependency(self, library: 'VHDLLibrary') -> None: library.parent = self def AddFile(self, vhdlFile: VHDLSourceFile) -> None: if not isinstance(vhdlFile, VHDLSourceFile): ex = TypeError(f"Parameter 'vhdlFile' is not a 'VHDLSourceFile'.") if version_info >= (3, 11): # pragma: no cover ex.add_note(f"Got type '{getFullyQualifiedName(vhdlFile)}'.") raise ex self._files.append(vhdlFile) def AddFiles(self, vhdlFiles: Iterable[VHDLSourceFile]) -> None: for vhdlFile in vhdlFiles: if not isinstance(vhdlFile, VHDLSourceFile): raise TypeError(f"Item '{vhdlFile}' in parameter 'vhdlFiles' is not a 'VHDLSourceFile'.") self._files.append(vhdlFile) @property def FileCount(self) -> int: """Returns number of files.""" return len(self._files)
[docs] def __len__(self) -> int: """ Returns number of attributes set on this VHDL library. :returns: The number of attributes set on this VHDL library. """ return len(self._attributes)
[docs] def __getitem__(self, key: Type[Attribute]) -> typing_Any: """Index access for returning attributes on this VHDL library. :param key: The attribute type. :returns: The attribute's value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") try: return self._attributes[key] except KeyError: return key.resolve(self, key)
[docs] def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: """ Index access for adding or setting attributes on this VHDL library. :param key: The attribute type. :param value: The attributes value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") self._attributes[key] = value
[docs] def __delitem__(self, key: Type[Attribute]) -> None: """ Index access for deleting attributes on this VHDL library. :param key: The attribute type. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") del self._attributes[key]
[docs] def __str__(self) -> str: """Returns the VHDL library's name.""" return self._name
[docs] @export class Design(metaclass=ExtendedType, slots=True): """ A :term:`Design` represents a group of filesets and the source files therein. Each design contains at least one fileset - the :term:`default fileset`. For designs with VHDL source files, a independent `VHDLLibraries` overlay structure exists. :arg name: The design's name. :arg topLevel: Name of the design's toplevel. :arg directory: Path of this design (absolute or relative to the project). :arg project: Project the design is associated with. :arg vhdlVersion: Default VHDL version for files in this design, if not specified for the file itself. :arg verilogVersion: Default Verilog version for files in this design, if not specified for the file itself. :arg svVersion: Default SystemVerilog version for files in this design, if not specified for the file itself. :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself. """ _name: str _topLevel: Nullable[str] _project: Nullable['Project'] _directory: pathlib_Path _fileSets: Dict[str, FileSet] _defaultFileSet: Nullable[FileSet] _attributes: Dict[Type[Attribute], typing_Any] _vhdlLibraries: Dict[str, VHDLLibrary] _vhdlVersion: VHDLVersion _verilogVersion: SystemVerilogVersion _svVersion: SystemVerilogVersion _srdlVersion: SystemRDLVersion _externalVHDLLibraries: List _vhdlLibraryDependencyGraph: Graph _fileDependencyGraph: Graph
[docs] def __init__( self, name: str, topLevel: Nullable[str] = None, directory: pathlib_Path = pathlib_Path("."), project: Nullable["Project"] = None, vhdlVersion: Nullable[VHDLVersion] = None, verilogVersion: Nullable[SystemVerilogVersion] = None, svVersion: Nullable[SystemVerilogVersion] = None, srdlVersion: Nullable[SystemRDLVersion] = None ) -> None: self._name = name self._topLevel = topLevel self._project = project if project is not None: project._designs[name] = self self._directory = directory self._fileSets = {} self._defaultFileSet = FileSet("default", project=project, design=self) self._attributes = {} self._vhdlLibraries = {} self._vhdlVersion = vhdlVersion self._verilogVersion = verilogVersion self._svVersion = svVersion self._srdlVersion = srdlVersion self._externalVHDLLibraries = [] self._vhdlLibraryDependencyGraph = Graph() self._fileDependencyGraph = Graph()
@property def Name(self) -> str: """Property setting or returning the design's name.""" return self._name @Name.setter def Name(self, value: str) -> None: self._name = value @property def TopLevel(self) -> str: """Property setting or returning the fileset's toplevel.""" return self._topLevel @TopLevel.setter def TopLevel(self, value: str) -> None: self._topLevel = value @property def Project(self) -> Nullable['Project']: """Property setting or returning the project this design is used in.""" return self._project @Project.setter def Project(self, value: 'Project') -> None: self._project = value @property def Directory(self) -> pathlib_Path: """Property setting or returning the directory this design is located in.""" return self._directory @Directory.setter def Directory(self, value: pathlib_Path) -> None: self._directory = value @property def ResolvedPath(self) -> pathlib_Path: """Read-only property returning the resolved path of this fileset.""" if self._directory.is_absolute(): return self._directory.resolve() elif self._project is not None: path = (self._project.ResolvedPath / self._directory).resolve() if path.is_absolute(): return path else: # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath return pathlib_Path(path_relpath(path, pathlib_Path.cwd())) else: # TODO: message and exception type raise Exception("") @property def DefaultFileSet(self) -> FileSet: """Property setting or returning the default fileset of this design.""" return self._defaultFileSet @DefaultFileSet.setter def DefaultFileSet(self, value: Union[str, FileSet]) -> None: if isinstance(value, str): if value not in self._fileSets.keys(): raise Exception(f"Fileset '{value}' is not in this design.") self._defaultFileSet = self._fileSets[value] elif isinstance(value, FileSet): if value not in self.FileSets: raise Exception(f"Fileset '{value}' is not associated to this design.") self._defaultFileSet = value else: raise ValueError("Unsupported parameter type for 'value'.") # TODO: return generator with another method @property def FileSets(self) -> Dict[str, FileSet]: """Read-only property returning the dictionary of filesets.""" return self._fileSets
[docs] def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[str, FileSet] = None) -> Generator[File, None, None]: """ Method returning the files of this design. :arg fileType: A filter for file types. Default: ``Any``. :arg fileSet: Specifies if all files from all filesets (``fileSet=None``) are files from a single fileset are returned. """ if fileSet is None: for fileSet in self._fileSets.values(): for file in fileSet.Files(fileType): yield file else: if isinstance(fileSet, str): try: fileSet = self._fileSets[fileSet] except KeyError as ex: raise Exception(f"Fileset {fileSet.Name} not bound to design {self.Name}.") from ex elif not isinstance(fileSet, FileSet): raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.") for file in fileSet.Files(fileType): yield file
[docs] def Validate(self) -> None: """Validate this design.""" if self._name is None or self._name == "": raise Exception("Validation: Design has no name.") if self._directory is None: raise Exception(f"Validation: Design '{self._name}' has no directory.") try: path = self.ResolvedPath except Exception as ex: raise Exception(f"Validation: Design '{self._name}' could not compute resolved path.") from ex if not path.exists(): raise Exception(f"Validation: Design '{self._name}'s directory '{path}' does not exist.") if not path.is_dir(): raise Exception(f"Validation: Design '{self._name}'s directory '{path}' is not a directory.") if len(self._fileSets) == 0: raise Exception(f"Validation: Design '{self._name}' has no fileset.") try: if self._defaultFileSet is not self._fileSets[self._defaultFileSet.Name]: raise Exception(f"Validation: Design '{self._name}'s default fileset is the same as listed in filesets.") except KeyError as ex: raise Exception(f"Validation: Design '{self._name}'s default fileset is not in list of filesets.") from ex if self._project is None: raise Exception(f"Validation: Design '{self._path}' has no project.") for fileSet in self._fileSets.values(): fileSet.Validate()
@property def VHDLLibraries(self) -> Dict[str, VHDLLibrary]: return self._vhdlLibraries @property def VHDLVersion(self) -> VHDLVersion: if self._vhdlVersion is not None: return self._vhdlVersion elif self._project is not None: return self._project.VHDLVersion else: raise Exception("VHDLVersion was neither set locally nor globally.") @VHDLVersion.setter def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value @property def VerilogVersion(self) -> SystemVerilogVersion: if self._verilogVersion is not None: return self._verilogVersion elif self._project is not None: return self._project.VerilogVersion else: raise Exception("VerilogVersion was neither set locally nor globally.") @VerilogVersion.setter def VerilogVersion(self, value: SystemVerilogVersion) -> None: self._verilogVersion = value @property def SVVersion(self) -> SystemVerilogVersion: if self._svVersion is not None: return self._svVersion elif self._project is not None: return self._project.SVVersion else: raise Exception("SVVersion was neither set locally nor globally.") @SVVersion.setter def SVVersion(self, value: SystemVerilogVersion) -> None: self._svVersion = value @property def SRDLVersion(self) -> SystemRDLVersion: if self._srdlVersion is not None: return self._srdlVersion elif self._project is not None: return self._project.SRDLVersion else: raise Exception("SRDLVersion was neither set locally nor globally.") @SRDLVersion.setter def SRDLVersion(self, value: SystemRDLVersion) -> None: self._srdlVersion = value @property def ExternalVHDLLibraries(self) -> List: return self._externalVHDLLibraries def AddFileSet(self, fileSet: FileSet) -> None: if not isinstance(fileSet, FileSet): raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.") elif fileSet in self._fileSets: raise Exception("Design already contains this fileset.") elif fileSet.Name in self._fileSets.keys(): raise Exception(f"Design already contains a fileset named '{fileSet.Name}'.") self._fileSets[fileSet.Name] = fileSet fileSet.Design = self fileSet._parent = self def AddFileSets(self, fileSets: Iterable[FileSet]) -> None: for fileSet in fileSets: self.AddFileSet(fileSet) @property def FileSetCount(self) -> int: """Returns number of file sets excl. sub-filesets.""" return len(self._fileSets) @property def TotalFileSetCount(self) -> int: """Returns number of file sets incl. sub-filesets.""" fileSetCount = len(self._fileSets) for fileSet in self._fileSets.values(): fileSetCount += fileSet.TotalFileSetCount return fileSetCount def AddFile(self, file: File) -> None: if file.FileSet is None: self._defaultFileSet.AddFile(file) else: raise ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}' and can't be assigned via Design to a default fileset.") def AddFiles(self, files: Iterable[File]) -> None: for file in files: self.AddFile(file) def AddVHDLLibrary(self, vhdlLibrary: VHDLLibrary) -> None: if vhdlLibrary.Name in self._vhdlLibraries: if self._vhdlLibraries[vhdlLibrary.Name] is vhdlLibrary: raise Exception(f"The VHDLLibrary '{vhdlLibrary.Name}' was already added to the design.") else: raise Exception(f"A VHDLLibrary with same name ('{vhdlLibrary.Name}') already exists for this design.")
[docs] def __len__(self) -> int: """ Returns number of attributes set on this design. :returns: The number of attributes set on this design. """ return len(self._attributes)
[docs] def __getitem__(self, key: Type[Attribute]) -> typing_Any: """Index access for returning attributes on this design. :param key: The attribute type. :returns: The attribute's value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") try: return self._attributes[key] except KeyError: return key.resolve(self, key)
[docs] def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: """ Index access for adding or setting attributes on this design. :param key: The attribute type. :param value: The attributes value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") self._attributes[key] = value
[docs] def __delitem__(self, key: Type[Attribute]) -> None: """ Index access for deleting attributes on this design. :param key: The attribute type. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") del self._attributes[key]
[docs] def __str__(self) -> str: return self._name
[docs] @export class Project(metaclass=ExtendedType, slots=True): """ A :term:`Project` represents a group of designs and the source files therein. :arg name: The project's name. :arg rootDirectory: Base-path to the project. :arg vhdlVersion: Default VHDL version for files in this project, if not specified for the file itself. :arg verilogVersion: Default Verilog version for files in this project, if not specified for the file itself. :arg svVersion: Default SystemVerilog version for files in this project, if not specified for the file itself. """ _name: str _rootDirectory: pathlib_Path _designs: Dict[str, Design] _defaultDesign: Design _attributes: Dict[Type[Attribute], typing_Any] _vhdlVersion: VHDLVersion _verilogVersion: SystemVerilogVersion _svVersion: SystemVerilogVersion _srdlVersion: SystemRDLVersion
[docs] def __init__( self, name: str, rootDirectory: pathlib_Path = pathlib_Path("."), vhdlVersion: Nullable[VHDLVersion] = None, verilogVersion: Nullable[SystemVerilogVersion] = None, svVersion: Nullable[SystemVerilogVersion] = None ) -> None: self._name = name self._rootDirectory = rootDirectory self._designs = {} self._defaultDesign = Design("default", project=self) self._attributes = {} self._vhdlVersion = vhdlVersion self._verilogVersion = verilogVersion self._svVersion = svVersion
@property def Name(self) -> str: """Property setting or returning the project's name.""" return self._name @property def RootDirectory(self) -> pathlib_Path: """Property setting or returning the root directory this project is located in.""" return self._rootDirectory @RootDirectory.setter def RootDirectory(self, value: pathlib_Path) -> None: self._rootDirectory = value @property def ResolvedPath(self) -> pathlib_Path: """Read-only property returning the resolved path of this fileset.""" path = self._rootDirectory.resolve() if self._rootDirectory.is_absolute(): return path else: # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath return pathlib_Path(path_relpath(path, pathlib_Path.cwd())) # TODO: return generator with another method @property def Designs(self) -> Dict[str, Design]: return self._designs @property def DefaultDesign(self) -> Design: return self._defaultDesign
[docs] def Validate(self) -> None: """Validate this project.""" if self._name is None or self._name == "": raise Exception("Validation: Project has no name.") if self._rootDirectory is None: raise Exception(f"Validation: Project '{self._name}' has no root directory.") try: path = self.ResolvedPath except Exception as ex: raise Exception(f"Validation: Project '{self._name}' could not compute resolved path.") from ex if not path.exists(): raise Exception(f"Validation: Project '{self._name}'s directory '{path}' does not exist.") if not path.is_dir(): raise Exception(f"Validation: Project '{self._name}'s directory '{path}' is not a directory.") if len(self._designs) == 0: raise Exception(f"Validation: Project '{self._name}' has no design.") try: if self._defaultDesign is not self._designs[self._defaultDesign.Name]: raise Exception(f"Validation: Project '{self._name}'s default design is the same as listed in designs.") except KeyError as ex: raise Exception(f"Validation: Project '{self._name}'s default design is not in list of designs.") from ex for design in self._designs.values(): design.Validate()
@property def DesignCount(self) -> int: """Returns number of designs.""" return len(self._designs) @property def VHDLVersion(self) -> VHDLVersion: # TODO: check for None and return exception return self._vhdlVersion @VHDLVersion.setter def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value @property def VerilogVersion(self) -> SystemVerilogVersion: # TODO: check for None and return exception return self._verilogVersion @VerilogVersion.setter def VerilogVersion(self, value: SystemVerilogVersion) -> None: self._verilogVersion = value @property def SVVersion(self) -> SystemVerilogVersion: # TODO: check for None and return exception return self._svVersion @SVVersion.setter def SVVersion(self, value: SystemVerilogVersion) -> None: self._svVersion = value @property def SRDLVersion(self) -> SystemRDLVersion: # TODO: check for None and return exception return self._srdlVersion @SRDLVersion.setter def SRDLVersion(self, value: SystemRDLVersion) -> None: self._srdlVersion = value
[docs] def __len__(self) -> int: """ Returns number of attributes set on this project. :returns: The number of attributes set on this project. """ return len(self._attributes)
[docs] def __getitem__(self, key: Type[Attribute]) -> typing_Any: """Index access for returning attributes on this project. :param key: The attribute type. :returns: The attribute's value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") try: return self._attributes[key] except KeyError: return key.resolve(self, key)
[docs] def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: """ Index access for adding or setting attributes on this project. :param key: The attribute type. :param value: The attributes value. :raises TypeError: When parameter 'key' is not a subclass of Attribute. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") self._attributes[key] = value
[docs] def __delitem__(self, key: Type[Attribute]) -> None: """ Index access for deleting attributes on this project. :param key: The attribute type. """ if not issubclass(key, Attribute): raise TypeError("Parameter 'key' is not an 'Attribute'.") del self._attributes[key]
[docs] def __str__(self) -> str: return self._name