Coverage for pyEDAA/ProjectModel/__init__.py: 69%
1070 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-08 22:18 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-08 22:18 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ _ __ __ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ |__/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2024 Patrick Lehmann - Boetzingen, Germany #
15# Copyright 2014-2016 Technische Universität Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture #
16# #
17# Licensed under the Apache License, Version 2.0 (the "License"); #
18# you may not use this file except in compliance with the License. #
19# You may obtain a copy of the License at #
20# #
21# http://www.apache.org/licenses/LICENSE-2.0 #
22# #
23# Unless required by applicable law or agreed to in writing, software #
24# distributed under the License is distributed on an "AS IS" BASIS, #
25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
26# See the License for the specific language governing permissions and #
27# limitations under the License. #
28# #
29# SPDX-License-Identifier: Apache-2.0 #
30# ==================================================================================================================== #
31#
32"""An abstract model of EDA tool projects."""
33__author__ = "Patrick Lehmann"
34__email__ = "Paebbels@gmail.com"
35__copyright__ = "2014-2024, Patrick Lehmann, Unai Martinez-Corral"
36__license__ = "Apache License, Version 2.0"
37__version__ = "0.5.0"
38__keywords__ = ["eda project", "model", "abstract", "xilinx", "vivado", "osvvm", "file set", "file group", "test bench", "test harness"]
40from os.path import relpath as path_relpath
41from pathlib import Path as pathlib_Path
42from sys import version_info
43from typing import Dict, Union, Optional as Nullable, List, Iterable, Generator, Tuple, Any as typing_Any, Type, Set, Any
45from pyTooling.Common import getFullyQualifiedName
46from pyTooling.Decorators import export
47from pyTooling.MetaClasses import ExtendedType
48from pyTooling.Graph import Graph, Vertex
49from pySVModel import SystemVerilogVersion
50from pyVHDLModel import VHDLVersion
51from pySystemRDLModel import SystemRDLVersion
54@export
55class Attribute(metaclass=ExtendedType):
56 KEY: str
57 VALUE_TYPE: typing_Any
59 @staticmethod
60 def resolve(obj: typing_Any, key: Type['Attribute']):
61 if isinstance(obj, File): 61 ↛ 63line 61 didn't jump to line 63 because the condition on line 61 was always true
62 return obj._fileSet[key]
63 elif isinstance(obj, FileSet):
64 return obj._design[key]
65 elif isinstance(obj, Design):
66 return obj._project[key]
67 else:
68 raise Exception("Resolution error")
71@export
72class FileType(ExtendedType):
73 """
74 A :term:`meta-class` to construct *FileType* classes.
76 Modifications done by this meta-class:
77 * Register all classes of type :class:`FileType` or derived variants in a class field :attr:`FileType.FileTypes` in this meta-class.
78 """
80 FileTypes: Dict[str, 'FileType'] = {} #: Dictionary of all classes of type :class:`FileType` or derived variants
81 Any: 'FileType'
83 def __init__(cls, name: str, bases: Tuple[type, ...], dictionary: Dict[str, typing_Any], **kwargs):
84 super().__init__(name, bases, dictionary, **kwargs)
85 cls.Any = cls
87 def __new__(cls, className, baseClasses, classMembers: Dict, *args, **kwargs):
88 fileType = super().__new__(cls, className, baseClasses, classMembers, *args, **kwargs)
89 cls.FileTypes[className] = fileType
90 return fileType
92 def __getattr__(cls, item) -> 'FileType':
93 if item[:2] != "__" and item[-2:] != "__":
94 return cls.FileTypes[item]
95 else:
96 return super().__getattribute__(item)
98 def __contains__(cls, item) -> bool:
99 return issubclass(item, cls)
102@export
103class File(metaclass=FileType, slots=True):
104 """
105 A :term:`File` represents a file in a design. This :term:`base-class` is used
106 for all derived file classes.
108 A file can be created standalone and later associated to a fileset, design and
109 project. Or a fileset, design and/or project can be associated immediately
110 while creating a file.
112 :arg path: Relative or absolute path to the file.
113 :arg project: Project the file is associated with.
114 :arg design: Design the file is associated with.
115 :arg fileSet: Fileset the file is associated with.
116 """
118 _path: pathlib_Path
119 _fileType: 'FileType'
120 _project: Nullable['Project']
121 _design: Nullable['Design']
122 _fileSet: Nullable['FileSet']
123 _attributes: Dict[Type[Attribute], typing_Any]
125 def __init__(
126 self,
127 path: pathlib_Path,
128 project: Nullable["Project"] = None,
129 design: Nullable["Design"] = None,
130 fileSet: Nullable["FileSet"] = None
131 ):
132 self._fileType = getattr(FileTypes, self.__class__.__name__)
133 self._path = path
134 if project is not None:
135 self._project = project
136 self._design = design
137 if fileSet is not None: 137 ↛ 138line 137 didn't jump to line 138 because the condition on line 137 was never true
138 self.FileSet = fileSet
139 elif design is not None:
140 self._project = design._project
141 self._design = design
142 self.FileSet = design.DefaultFileSet if fileSet is None else fileSet
143 elif fileSet is not None:
144 design = fileSet._design
145 if design is not None:
146 self._project = design._project
147 else:
148 self._project = None
149 self._design = design
150 self.FileSet = fileSet
151 else:
152 self._project = None
153 self._design = None
154 self._fileSet = None
156 self._attributes = {}
157 self._registerAttributes()
159 def _registerAttributes(self) -> None:
160 pass
162 @property
163 def FileType(self) -> 'FileType':
164 """Read-only property to return the file type of this file."""
165 return self._fileType
167 @property
168 def Path(self) -> pathlib_Path:
169 """Read-only property returning the path of this file."""
170 return self._path
172 # TODO: setter?
174 @property
175 def ResolvedPath(self) -> pathlib_Path:
176 """Read-only property returning the resolved path of this file."""
177 if self._path.is_absolute(): 177 ↛ 178line 177 didn't jump to line 178 because the condition on line 177 was never true
178 return self._path.resolve()
179 elif self._fileSet is not None: 179 ↛ 189line 179 didn't jump to line 189 because the condition on line 179 was always true
180 path = (self._fileSet.ResolvedPath / self._path).resolve()
182 if path.is_absolute(): 182 ↛ 186line 182 didn't jump to line 186 because the condition on line 182 was always true
183 return path
184 else:
185 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
186 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
187 else:
188 # TODO: message and exception type
189 raise Exception("")
191 @property
192 def Project(self) -> Nullable['Project']:
193 """Property setting or returning the project this file is used in."""
194 return self._project
196 @Project.setter
197 def Project(self, value: 'Project') -> None:
198 self._project = value
200 if self._fileSet is None: 200 ↛ exitline 200 didn't return from function 'Project' because the condition on line 200 was always true
201 self._project.DefaultDesign.DefaultFileSet.AddFile(self)
203 @property
204 def Design(self) -> Nullable['Design']:
205 """Property setting or returning the design this file is used in."""
206 return self._design
208 @Design.setter
209 def Design(self, value: 'Design') -> None:
210 self._design = value
212 if self._fileSet is None: 212 ↛ 215line 212 didn't jump to line 215 because the condition on line 212 was always true
213 self._design.DefaultFileSet.AddFile(self)
215 if self._project is None: 215 ↛ 217line 215 didn't jump to line 217 because the condition on line 215 was always true
216 self._project = value._project
217 elif self._project is not value._project:
218 raise Exception("The design's project is not identical to the already assigned project.")
220 @property
221 def FileSet(self) -> Nullable['FileSet']:
222 """Property setting or returning the fileset this file is used in."""
223 return self._fileSet
225 @FileSet.setter
226 def FileSet(self, value: 'FileSet') -> None:
227 self._fileSet = value
228 value._files.append(self)
230 def Validate(self) -> None:
231 """Validate this file."""
232 if self._path is None: 232 ↛ 233line 232 didn't jump to line 233 because the condition on line 232 was never true
233 raise Exception("Validation: File has no path.")
234 try:
235 path = self.ResolvedPath
236 except Exception as ex:
237 raise Exception(f"Validation: File '{self._path}' could not compute resolved path.") from ex
238 if not path.exists(): 238 ↛ 239line 238 didn't jump to line 239 because the condition on line 238 was never true
239 raise Exception(f"Validation: File '{self._path}' (={path}) does not exist.")
240 if not path.is_file(): 240 ↛ 241line 240 didn't jump to line 241 because the condition on line 240 was never true
241 raise Exception(f"Validation: File '{self._path}' (={path}) is not a file.")
243 if self._fileSet is None: 243 ↛ 244line 243 didn't jump to line 244 because the condition on line 243 was never true
244 raise Exception(f"Validation: File '{self._path}' has no fileset.")
245 if self._design is None: 245 ↛ 246line 245 didn't jump to line 246 because the condition on line 245 was never true
246 raise Exception(f"Validation: File '{self._path}' has no design.")
247 if self._project is None: 247 ↛ 248line 247 didn't jump to line 248 because the condition on line 247 was never true
248 raise Exception(f"Validation: File '{self._path}' has no project.")
250 def __len__(self) -> int:
251 """
252 Returns number of attributes set on this file.
254 :returns: The number if attributes set on this file.
255 """
256 return len(self._attributes)
258 def __getitem__(self, key: Type[Attribute]) -> Any:
259 """Index access for returning attributes on this file.
261 :param key: The attribute type.
262 :returns: The attribute's value.
263 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
264 """
265 if not issubclass(key, Attribute): 265 ↛ 266line 265 didn't jump to line 266 because the condition on line 265 was never true
266 raise TypeError("Parameter 'key' is not an 'Attribute'.")
268 try:
269 return self._attributes[key]
270 except KeyError:
271 try:
272 return key.resolve(self, key)
273 except KeyError:
274 attribute = key()
275 self._attributes[key] = attribute
276 return attribute
278 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
279 """
280 Index access for adding or setting attributes on this file.
282 :param key: The attribute type.
283 :param value: The attributes value.
284 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
285 """
286 if not issubclass(key, Attribute): 286 ↛ 287line 286 didn't jump to line 287 because the condition on line 286 was never true
287 raise TypeError("Parameter 'key' is not an 'Attribute'.")
289 self._attributes[key] = value
291 def __delitem__(self, key: Type[Attribute]) -> None:
292 """
293 Index access for deleting attributes on this file.
295 :param key: The attribute type.
296 """
297 if not issubclass(key, Attribute): 297 ↛ 298line 297 didn't jump to line 298 because the condition on line 297 was never true
298 raise TypeError("Parameter 'key' is not an 'Attribute'.")
300 del self._attributes[key]
302 def __str__(self) -> str:
303 return f"{self._path}"
306FileTypes = File
309@export
310class HumanReadableContent(metaclass=ExtendedType, mixin=True):
311 """A file type representing human-readable contents."""
314@export
315class XMLContent(HumanReadableContent, mixin=True):
316 """A file type representing XML contents."""
319@export
320class YAMLContent(HumanReadableContent, mixin=True):
321 """A file type representing YAML contents."""
324@export
325class JSONContent(HumanReadableContent, mixin=True):
326 """A file type representing JSON contents."""
329@export
330class INIContent(HumanReadableContent, mixin=True):
331 """A file type representing INI contents."""
334@export
335class TOMLContent(HumanReadableContent, mixin=True):
336 """A file type representing TOML contents."""
339@export
340class TCLContent(HumanReadableContent, mixin=True):
341 """A file type representing content in TCL code."""
344@export
345class SDCContent(TCLContent, mixin=True):
346 """A file type representing contents as Synopsys Design Constraints (SDC)."""
349@export
350class PythonContent(HumanReadableContent, mixin=True):
351 """A file type representing contents as Python source code."""
354@export
355class TextFile(File, HumanReadableContent):
356 """A text file (``*.txt``)."""
359@export
360class LogFile(File, HumanReadableContent):
361 """A log file (``*.log``)."""
364@export
365class XMLFile(File, XMLContent):
366 """An XML file (``*.xml``)."""
369@export
370class SourceFile(File):
371 """Base-class of all source files."""
374@export
375class HDLSourceFile(SourceFile):
376 """Base-class of all HDL source files."""
379@export
380class RDLSourceFile(SourceFile):
381 """Base-class of all RDL source files."""
384@export
385class NetlistFile(SourceFile):
386 """Base-class of all netlist source files."""
389@export
390class EDIFNetlistFile(NetlistFile):
391 """Netlist file in EDIF (Electronic Design Interchange Format)."""
394@export
395class TCLSourceFile(SourceFile, TCLContent):
396 """A TCL source file."""
399@export
400class VHDLSourceFile(HDLSourceFile, HumanReadableContent):
401 """
402 A VHDL source file (of any language version).
404 :arg path: Relative or absolute path to the file.
405 :arg vhdlLibrary: VHDLLibrary this VHDL source file is associated wih.
406 :arg vhdlVersion: VHDLVersion this VHDL source file is associated wih.
407 :arg project: Project the file is associated with.
408 :arg design: Design the file is associated with.
409 :arg fileSet: Fileset the file is associated with.
410 """
412 _vhdlLibrary: Nullable['VHDLLibrary']
413 _vhdlVersion: VHDLVersion
415 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):
416 super().__init__(path, project, design, fileSet)
418 if isinstance(vhdlLibrary, str): 418 ↛ 419line 418 didn't jump to line 419 because the condition on line 418 was never true
419 if design is not None:
420 try:
421 vhdlLibrary = design.VHDLLibraries[vhdlLibrary]
422 except KeyError as ex:
423 raise Exception(f"VHDL library '{vhdlLibrary}' not found in design '{design.Name}'.") from ex
424 elif project is not None:
425 try:
426 vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary]
427 except KeyError as ex:
428 raise Exception(f"VHDL library '{vhdlLibrary}' not found in default design '{project.DefaultDesign.Name}'.") from ex
429 else:
430 raise Exception(f"Can't lookup VHDL library because neither 'project' nor 'design' is given as a parameter.")
431 elif isinstance(vhdlLibrary, VHDLLibrary):
432 self._vhdlLibrary = vhdlLibrary
433 vhdlLibrary.AddFile(self)
434 elif vhdlLibrary is None: 434 ↛ 437line 434 didn't jump to line 437 because the condition on line 434 was always true
435 self._vhdlLibrary = None
436 else:
437 ex = TypeError(f"Parameter 'vhdlLibrary' is neither a 'str' nor 'VHDLibrary'.")
438 if version_info >= (3, 11): # pragma: no cover
439 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.")
440 raise ex
442 self._vhdlVersion = vhdlVersion
444 def Validate(self) -> None:
445 """Validate this VHDL source file."""
446 super().Validate()
448 try:
449 _ = self.VHDLLibrary
450 except Exception as ex:
451 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLLibrary assigned.") from ex
452 try:
453 _ = self.VHDLVersion
454 except Exception as ex:
455 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLVersion assigned.") from ex
457 @property
458 def VHDLLibrary(self) -> 'VHDLLibrary':
459 """Property setting or returning the VHDL library this VHDL source file is used in."""
460 if self._vhdlLibrary is not None:
461 return self._vhdlLibrary
462 elif self._fileSet is not None:
463 return self._fileSet.VHDLLibrary
464 else:
465 raise Exception("VHDLLibrary was neither set locally nor globally.")
467 @VHDLLibrary.setter
468 def VHDLLibrary(self, value: 'VHDLLibrary') -> None:
469 self._vhdlLibrary = value
470 value._files.append(self)
472 @property
473 def VHDLVersion(self) -> VHDLVersion:
474 """Property setting or returning the VHDL version this VHDL source file is used in."""
475 if self._vhdlVersion is not None:
476 return self._vhdlVersion
477 elif self._fileSet is not None:
478 return self._fileSet.VHDLVersion
479 else:
480 raise Exception("VHDLVersion was neither set locally nor globally.")
482 @VHDLVersion.setter
483 def VHDLVersion(self, value: VHDLVersion) -> None:
484 self._vhdlVersion = value
486 def __repr__(self) -> str:
487 return f"<VHDL file: '{self.ResolvedPath}'; lib: '{self.VHDLLibrary}'; version: {self.VHDLVersion}>"
490class VerilogMixIn(metaclass=ExtendedType, mixin=True):
491 @property
492 def VerilogVersion(self) -> SystemVerilogVersion:
493 """Property setting or returning the Verilog version this Verilog source file is used in."""
494 if self._version is not None:
495 return self._version
496 elif self._fileSet is not None:
497 return self._fileSet.VerilogVersion
498 else:
499 raise Exception("VerilogVersion was neither set locally nor globally.")
501 @VerilogVersion.setter
502 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
503 self._version = value
506class SystemVerilogMixIn(metaclass=ExtendedType, mixin=True):
507 @property
508 def SVVersion(self) -> SystemVerilogVersion:
509 """Property setting or returning the SystemVerilog version this SystemVerilog source file is used in."""
510 if self._version is not None:
511 return self._version
512 elif self._fileSet is not None:
513 return self._fileSet.SVVersion
514 else:
515 raise Exception("SVVersion was neither set locally nor globally.")
517 @SVVersion.setter
518 def SVVersion(self, value: SystemVerilogVersion) -> None:
519 self._version = value
522@export
523class VerilogBaseFile(HDLSourceFile, HumanReadableContent):
524 _version: SystemVerilogVersion
526 def __init__(self, path: pathlib_Path, version: Nullable[SystemVerilogVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None):
527 super().__init__(path, project, design, fileSet)
529 self._version = version
532@export
533class VerilogSourceFile(VerilogBaseFile, VerilogMixIn):
534 """A Verilog source file (of any language version)."""
537@export
538class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn):
539 """A Verilog header file (of any language version)."""
542@export
543class SystemVerilogBaseFile(VerilogBaseFile):
544 ...
547@export
548class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn):
549 """A SystemVerilog source file (of any language version)."""
552@export
553class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn):
554 """A SystemVerilog header file (of any language version)."""
557@export
558class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent):
559 """A SystemRDL source file (of any language version)."""
561 _srdlVersion: SystemRDLVersion
563 def __init__(self, path: pathlib_Path, srdlVersion: Nullable[SystemRDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None):
564 super().__init__(path, project, design, fileSet)
566 self._srdlVersion = srdlVersion
568 @property
569 def SystemRDLVersion(self) -> SystemRDLVersion:
570 """Property setting or returning the SystemRDL version this SystemRDL source file is used in."""
571 if self._srdlVersion is not None:
572 return self._srdlVersion
573 elif self._fileSet is not None:
574 return self._fileSet.SRDLVersion
575 else:
576 raise Exception("SRDLVersion was neither set locally nor globally.")
578 @SystemRDLVersion.setter
579 def SystemRDLVersion(self, value: SystemRDLVersion) -> None:
580 self._srdlVersion = value
583@export
584class PythonSourceFile(SourceFile, PythonContent):
585 """A Python source file."""
588# TODO: move to a Cocotb module
589@export
590class CocotbPythonFile(PythonSourceFile):
591 """A Python source file used by Cocotb."""
594@export
595class ConstraintFile(File, HumanReadableContent):
596 """Base-class of all constraint files."""
599@export
600class ProjectFile(File):
601 """Base-class of all tool-specific project files."""
604@export
605class CSourceFile(SourceFile):
606 """Base-class of all ANSI-C source files."""
609@export
610class CppSourceFile(SourceFile):
611 """Base-class of all ANSI-C++ source files."""
614@export
615class SettingFile(File):
616 """Base-class of all tool-specific setting files."""
619@export
620class SimulationAnalysisFile(File):
621 """Base-class of all tool-specific analysis files."""
624@export
625class SimulationElaborationFile(File):
626 """Base-class of all tool-specific elaboration files."""
629@export
630class SimulationStartFile(File):
631 """Base-class of all tool-specific simulation start-up files."""
634@export
635class SimulationRunFile(File):
636 """Base-class of all tool-specific simulation run (execution) files."""
639@export
640class WaveformConfigFile(File):
641 """Base-class of all tool-specific waveform configuration files."""
644@export
645class WaveformDatabaseFile(File):
646 """Base-class of all tool-specific waveform database files."""
649@export
650class WaveformExchangeFile(File):
651 """Base-class of all tool-independent waveform exchange files."""
654@export
655class FileSet(metaclass=ExtendedType, slots=True):
656 """
657 A :term:`FileSet` represents a group of files. Filesets can have sub-filesets.
659 The order of insertion is preserved. A fileset can be created standalone and
660 later associated to another fileset, design and/or project. Or a fileset,
661 design and/or project can be associated immediately while creating the
662 fileset.
664 :arg name: Name of this fileset.
665 :arg topLevel: Name of the fileset's toplevel.
666 :arg directory: Path of this fileset (absolute or relative to a parent fileset or design).
667 :arg project: Project the file is associated with.
668 :arg design: Design the file is associated with.
669 :arg parent: Parent fileset if this fileset is nested.
670 :arg vhdlLibrary: Default VHDL library for files in this fileset, if not specified for the file itself.
671 :arg vhdlVersion: Default VHDL version for files in this fileset, if not specified for the file itself.
672 :arg verilogVersion: Default Verilog version for files in this fileset, if not specified for the file itself.
673 :arg svVersion: Default SystemVerilog version for files in this fileset, if not specified for the file itself.
674 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself.
675 """
677 _name: str
678 _topLevel: Nullable[str]
679 _project: Nullable['Project']
680 _design: Nullable['Design']
681 _directory: pathlib_Path
682 _parent: Nullable['FileSet']
683 _fileSets: Dict[str, 'FileSet']
684 _files: List[File]
685 _set: Set
686 _attributes: Dict[Type[Attribute], typing_Any]
687 _vhdlLibraries: Dict[str, 'VHDLLibrary']
688 _vhdlLibrary: 'VHDLLibrary'
689 _vhdlVersion: VHDLVersion
690 _verilogVersion: SystemVerilogVersion
691 _svVersion: SystemVerilogVersion
692 _srdlVersion: SystemRDLVersion
694 def __init__(
695 self,
696 name: str,
697 topLevel: Nullable[str] = None,
698 directory: pathlib_Path = pathlib_Path("."),
699 project: Nullable["Project"] = None,
700 design: Nullable["Design"] = None,
701 parent: Nullable['FileSet'] = None,
702 vhdlLibrary: Union[str, 'VHDLLibrary'] = None,
703 vhdlVersion: Nullable[VHDLVersion] = None,
704 verilogVersion: Nullable[SystemVerilogVersion] = None,
705 svVersion: Nullable[SystemVerilogVersion] = None,
706 srdlVersion: Nullable[SystemRDLVersion] = None
707 ):
708 self._name = name
709 self._topLevel = topLevel
710 if project is not None:
711 self._project = project
712 self._design = design if design is not None else project.DefaultDesign
714 elif design is not None:
715 self._project = design._project
716 self._design = design
717 else:
718 self._project = None
719 self._design = None
720 self._directory = directory
721 self._parent = parent
722 self._fileSets = {}
723 self._files = []
724 self._set = set()
726 if design is not None:
727 design._fileSets[name] = self
729 self._attributes = {}
730 self._vhdlLibraries = {}
732 # TODO: handle if vhdlLibrary is a string
733 self._vhdlLibrary = vhdlLibrary
734 self._vhdlVersion = vhdlVersion
735 self._verilogVersion = verilogVersion
736 self._svVersion = svVersion
737 self._srdlVersion = srdlVersion
739 @property
740 def Name(self) -> str:
741 """Property setting or returning the fileset's name."""
742 return self._name
744 @Name.setter
745 def Name(self, value: str) -> None:
746 self._name = value
748 @property
749 def TopLevel(self) -> str:
750 """Property setting or returning the fileset's toplevel."""
751 return self._topLevel
753 @TopLevel.setter
754 def TopLevel(self, value: str) -> None:
755 self._topLevel = value
757 @property
758 def Project(self) -> Nullable['Project']:
759 """Property setting or returning the project this fileset is used in."""
760 return self._project
762 @Project.setter
763 def Project(self, value: 'Project') -> None:
764 self._project = value
766 @property
767 def Design(self) -> Nullable['Design']:
768 """Property setting or returning the design this fileset is used in."""
769 if self._design is not None:
770 return self._design
771 elif self._parent is not None: 771 ↛ 772line 771 didn't jump to line 772 because the condition on line 771 was never true
772 return self._parent.Design
773 else:
774 return None
775 # TODO: raise exception instead
776 # QUESTION: how to handle if design and parent is set?
778 @Design.setter
779 def Design(self, value: 'Design') -> None:
780 self._design = value
781 if self._project is None: 781 ↛ 783line 781 didn't jump to line 783 because the condition on line 781 was always true
782 self._project = value._project
783 elif self._project is not value._project:
784 raise Exception("The design's project is not identical to the already assigned project.")
786 @property
787 def Directory(self) -> pathlib_Path:
788 """Property setting or returning the directory this fileset is located in."""
789 return self._directory
791 @Directory.setter
792 def Directory(self, value: pathlib_Path) -> None:
793 self._directory = value
795 @property
796 def ResolvedPath(self) -> pathlib_Path:
797 """Read-only property returning the resolved path of this fileset."""
798 if self._directory.is_absolute(): 798 ↛ 799line 798 didn't jump to line 799 because the condition on line 798 was never true
799 return self._directory.resolve()
800 else:
801 if self._parent is not None: 801 ↛ 802line 801 didn't jump to line 802 because the condition on line 801 was never true
802 directory = self._parent.ResolvedPath
803 elif self._design is not None: 803 ↛ 805line 803 didn't jump to line 805 because the condition on line 803 was always true
804 directory = self._design.ResolvedPath
805 elif self._project is not None:
806 directory = self._project.ResolvedPath
807 else:
808 # TODO: message and exception type
809 raise Exception("")
811 directory = (directory / self._directory).resolve()
812 if directory.is_absolute(): 812 ↛ 816line 812 didn't jump to line 816 because the condition on line 812 was always true
813 return directory
814 else:
815 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
816 return pathlib_Path(path_relpath(directory, pathlib_Path.cwd()))
818 @property
819 def Parent(self) -> Nullable['FileSet']:
820 """Property setting or returning the parent fileset this fileset is used in."""
821 return self._parent
823 @Parent.setter
824 def Parent(self, value: 'FileSet') -> None:
825 self._parent = value
826 value._fileSets[self._name] = self
827 # TODO: check it it already exists
828 # QUESTION: make an Add fileset method?
830 @property
831 def FileSets(self) -> Dict[str, 'FileSet']:
832 """Read-only property returning the dictionary of sub-filesets."""
833 return self._fileSets
835 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[bool, str, 'FileSet'] = None) -> Generator[File, None, None]:
836 """
837 Method returning the files of this fileset.
839 :arg fileType: A filter for file types. Default: ``Any``.
840 :arg fileSet: Specifies how to handle sub-filesets.
841 """
842 if fileSet is False: 842 ↛ 843line 842 didn't jump to line 843 because the condition on line 842 was never true
843 for file in self._files:
844 if file.FileType in fileType:
845 yield file
846 elif fileSet is None: 846 ↛ 854line 846 didn't jump to line 854 because the condition on line 846 was always true
847 for fileSet in self._fileSets.values(): 847 ↛ 848line 847 didn't jump to line 848 because the loop on line 847 never started
848 for file in fileSet.Files(fileType):
849 yield file
850 for file in self._files:
851 if file.FileType in fileType:
852 yield file
853 else:
854 if isinstance(fileSet, str):
855 fileSetName = fileSet
856 try:
857 fileSet = self._fileSets[fileSetName]
858 except KeyError as ex:
859 raise Exception(f"Fileset {fileSetName} not bound to fileset {self.Name}.") from ex
860 elif not isinstance(fileSet, FileSet):
861 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.")
863 for file in fileSet.Files(fileType):
864 yield file
866 def AddFileSet(self, fileSet: "FileSet") -> None:
867 """
868 Method to add a single sub-fileset to this fileset.
870 :arg fileSet: A fileset to add to this fileset as sub-fileset.
871 """
872 if not isinstance(fileSet, FileSet): 872 ↛ 873line 872 didn't jump to line 873 because the condition on line 872 was never true
873 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.")
874 elif fileSet in self._fileSets: 874 ↛ 875line 874 didn't jump to line 875 because the condition on line 874 was never true
875 raise Exception("Sub-fileset already contains this fileset.")
876 elif fileSet.Name in self._fileSets.keys(): 876 ↛ 877line 876 didn't jump to line 877 because the condition on line 876 was never true
877 raise Exception(f"Fileset already contains a sub-fileset named '{fileSet.Name}'.")
879 self._fileSets[fileSet.Name] = fileSet
880 fileSet._parent = self
882 def AddFileSets(self, fileSets: Iterable["FileSet"]) -> None:
883 """
884 Method to add a multiple sub-filesets to this fileset.
886 :arg fileSets: An iterable of filesets to add each to the fileset.
887 """
888 for fileSet in fileSets:
889 self.AddFileSet(fileSet)
891 @property
892 def FileSetCount(self) -> int:
893 """Returns number of file sets excl. sub-filesets."""
894 return len(self._fileSets)
896 @property
897 def TotalFileSetCount(self) -> int:
898 """Returns number of file sets incl. sub-filesets."""
899 fileSetCount = len(self._fileSets)
900 for fileSet in self._fileSets.values():
901 fileSetCount += fileSet.TotalFileSetCount
903 return fileSetCount
905 def AddFile(self, file: File) -> None:
906 """
907 Method to add a single file to this fileset.
909 :arg file: A file to add to this fileset.
910 """
911 if not isinstance(file, File):
912 raise TypeError("Parameter 'file' is not of type ProjectModel.File.")
913 elif file._fileSet is not None:
914 ex = ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}'.")
915 if version_info >= (3, 11): # pragma: no cover
916 ex.add_note(f"A file can't be assigned to another fileset.")
917 raise ex
918 elif file in self._set: 918 ↛ 919line 918 didn't jump to line 919 because the condition on line 918 was never true
919 ex = ValueError(f"File '{file.Path!s}' is already part of this fileset.")
920 if version_info >= (3, 11): # pragma: no cover
921 ex.add_note(f"A file can't be added twice to a fileset.")
922 raise ex
924 self._files.append(file)
925 self._set.add(file)
926 file._fileSet = self
928 def AddFiles(self, files: Iterable[File]) -> None:
929 """
930 Method to add a multiple files to this fileset.
932 :arg files: An iterable of files to add each to the fileset.
933 """
934 for file in files:
935 self.AddFile(file)
937 @property
938 def FileCount(self) -> int:
939 """Returns number of files excl. sub-filesets."""
940 return len(self._files)
942 @property
943 def TotalFileCount(self) -> int:
944 """Returns number of files incl. the files in sub-filesets."""
945 fileCount = len(self._files)
946 for fileSet in self._fileSets.values():
947 fileCount += fileSet.FileCount
949 return fileCount
951 def Validate(self) -> None:
952 """Validate this fileset."""
953 if self._name is None or self._name == "": 953 ↛ 954line 953 didn't jump to line 954 because the condition on line 953 was never true
954 raise Exception("Validation: FileSet has no name.")
956 if self._directory is None: 956 ↛ 957line 956 didn't jump to line 957 because the condition on line 956 was never true
957 raise Exception(f"Validation: FileSet '{self._name}' has no directory.")
958 try:
959 path = self.ResolvedPath
960 except Exception as ex:
961 raise Exception(f"Validation: FileSet '{self._name}' could not compute resolved path.") from ex
962 if not path.exists(): 962 ↛ 963line 962 didn't jump to line 963 because the condition on line 962 was never true
963 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' does not exist.")
964 if not path.is_dir(): 964 ↛ 965line 964 didn't jump to line 965 because the condition on line 964 was never true
965 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' is not a directory.")
967 if self._design is None: 967 ↛ 968line 967 didn't jump to line 968 because the condition on line 967 was never true
968 raise Exception(f"Validation: FileSet '{self._directory}' has no design.")
969 if self._project is None: 969 ↛ 970line 969 didn't jump to line 970 because the condition on line 969 was never true
970 raise Exception(f"Validation: FileSet '{self._directory}' has no project.")
972 for fileSet in self._fileSets.values(): 972 ↛ 973line 972 didn't jump to line 973 because the loop on line 972 never started
973 fileSet.Validate()
974 for file in self._files: 974 ↛ 975line 974 didn't jump to line 975 because the loop on line 974 never started
975 file.Validate()
977 def GetOrCreateVHDLLibrary(self, name) -> 'VHDLLibrary':
978 if name in self._vhdlLibraries:
979 return self._vhdlLibraries[name]
980 elif name in self._design._vhdlLibraries:
981 library = self._design._vhdlLibraries[name]
982 self._vhdlLibraries[name] = library
983 return library
984 else:
985 library = VHDLLibrary(name, design=self._design, vhdlVersion=self._vhdlVersion)
986 self._vhdlLibraries[name] = library
987 return library
989 @property
990 def VHDLLibrary(self) -> 'VHDLLibrary':
991 """Property setting or returning the VHDL library of this fileset."""
992 if self._vhdlLibrary is not None:
993 return self._vhdlLibrary
994 elif self._parent is not None: 994 ↛ 996line 994 didn't jump to line 996 because the condition on line 994 was always true
995 return self._parent.VHDLLibrary
996 elif self._design is not None:
997 return self._design.VHDLLibrary
998 else:
999 raise Exception("VHDLLibrary was neither set locally nor globally.")
1001 @VHDLLibrary.setter
1002 def VHDLLibrary(self, value: 'VHDLLibrary') -> None:
1003 self._vhdlLibrary = value
1005 @property
1006 def VHDLVersion(self) -> VHDLVersion:
1007 """Property setting or returning the VHDL version of this fileset."""
1008 if self._vhdlVersion is not None:
1009 return self._vhdlVersion
1010 elif self._parent is not None:
1011 return self._parent.VHDLVersion
1012 elif self._design is not None: 1012 ↛ 1015line 1012 didn't jump to line 1015 because the condition on line 1012 was always true
1013 return self._design.VHDLVersion
1014 else:
1015 raise Exception("VHDLVersion was neither set locally nor globally.")
1017 @VHDLVersion.setter
1018 def VHDLVersion(self, value: VHDLVersion) -> None:
1019 self._vhdlVersion = value
1021 @property
1022 def VerilogVersion(self) -> SystemVerilogVersion:
1023 """Property setting or returning the Verilog version of this fileset."""
1024 if self._verilogVersion is not None:
1025 return self._verilogVersion
1026 elif self._parent is not None:
1027 return self._parent.VerilogVersion
1028 elif self._design is not None: 1028 ↛ 1031line 1028 didn't jump to line 1031 because the condition on line 1028 was always true
1029 return self._design.VerilogVersion
1030 else:
1031 raise Exception("VerilogVersion was neither set locally nor globally.")
1033 @VerilogVersion.setter
1034 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1035 self._verilogVersion = value
1037 @property
1038 def SVVersion(self) -> SystemVerilogVersion:
1039 """Property setting or returning the SystemVerilog version of this fileset."""
1040 if self._svVersion is not None:
1041 return self._svVersion
1042 elif self._parent is not None:
1043 return self._parent.SVVersion
1044 elif self._design is not None: 1044 ↛ 1047line 1044 didn't jump to line 1047 because the condition on line 1044 was always true
1045 return self._design.SVVersion
1046 else:
1047 raise Exception("SVVersion was neither set locally nor globally.")
1049 @SVVersion.setter
1050 def SVVersion(self, value: SystemVerilogVersion) -> None:
1051 self._svVersion = value
1053 @property
1054 def SRDLVersion(self) -> SystemRDLVersion:
1055 if self._srdlVersion is not None:
1056 return self._srdlVersion
1057 elif self._parent is not None:
1058 return self._parent.SRDLVersion
1059 elif self._design is not None:
1060 return self._design.SRDLVersion
1061 else:
1062 raise Exception("SRDLVersion was neither set locally nor globally.")
1064 @SRDLVersion.setter
1065 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1066 self._srdlVersion = value
1068 def __len__(self) -> int:
1069 """
1070 Returns number of attributes set on this fileset.
1072 :returns: The number if attributes set on this fileset.
1073 """
1074 return len(self._attributes)
1076 def __getitem__(self, key: Type[Attribute]) -> Any:
1077 """Index access for returning attributes on this fileset.
1079 :param key: The attribute type.
1080 :returns: The attribute's value.
1081 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1082 """
1083 if not issubclass(key, Attribute): 1083 ↛ 1084line 1083 didn't jump to line 1084 because the condition on line 1083 was never true
1084 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1086 try:
1087 return self._attributes[key]
1088 except KeyError:
1089 return key.resolve(self, key)
1091 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1092 """
1093 Index access for adding or setting attributes on this fileset.
1095 :param key: The attribute type.
1096 :param value: The attributes value.
1097 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1098 """
1099 if not issubclass(key, Attribute): 1099 ↛ 1100line 1099 didn't jump to line 1100 because the condition on line 1099 was never true
1100 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1102 self._attributes[key] = value
1104 def __delitem__(self, key: Type[Attribute]) -> None:
1105 """
1106 Index access for deleting attributes on this fileset.
1108 :param key: The attribute type.
1109 """
1110 if not issubclass(key, Attribute): 1110 ↛ 1111line 1110 didn't jump to line 1111 because the condition on line 1110 was never true
1111 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1113 del self._attributes[key]
1115 def __str__(self) -> str:
1116 """Returns the fileset's name."""
1117 return self._name
1120@export
1121class VHDLLibrary(metaclass=ExtendedType, slots=True):
1122 """
1123 A :term:`VHDLLibrary` represents a group of VHDL source files compiled into the same VHDL library.
1125 :arg name: The VHDL libraries' name.
1126 :arg project: Project the VHDL library is associated with.
1127 :arg design: Design the VHDL library is associated with.
1128 :arg vhdlVersion: Default VHDL version for files in this VHDL library, if not specified for the file itself.
1129 """
1131 _name: str
1132 _project: Nullable['Project']
1133 _design: Nullable['Design']
1134 _files: List[File]
1135 _vhdlVersion: VHDLVersion
1137 _dependencyNode: Vertex
1139 def __init__(
1140 self,
1141 name: str,
1142 project: Nullable["Project"] = None,
1143 design: Nullable["Design"] = None,
1144 vhdlVersion: Nullable[VHDLVersion] = None
1145 ):
1146 self._name = name
1147 if project is not None:
1148 self._project = project
1149 self._design = project._defaultDesign if design is None else design
1150 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph)
1152 if name in self._design._vhdlLibraries: 1152 ↛ 1153line 1152 didn't jump to line 1153 because the condition on line 1152 was never true
1153 raise Exception(f"Library '{name}' already in design '{self._design.Name}'.")
1154 else:
1155 self._design._vhdlLibraries[name] = self
1157 elif design is not None:
1158 self._project = design._project
1159 self._design = design
1160 self._dependencyNode = Vertex(value=self, graph=design._vhdlLibraryDependencyGraph)
1162 if name in design._vhdlLibraries: 1162 ↛ 1163line 1162 didn't jump to line 1163 because the condition on line 1162 was never true
1163 raise Exception(f"Library '{name}' already in design '{design.Name}'.")
1164 else:
1165 design._vhdlLibraries[name] = self
1167 else:
1168 self._project = None
1169 self._design = None
1170 self._dependencyNode = None
1172 self._files = []
1173 self._vhdlVersion = vhdlVersion
1175 @property
1176 def Name(self) -> str:
1177 return self._name
1179 @property
1180 def Project(self) -> Nullable['Project']:
1181 """Property setting or returning the project this VHDL library is used in."""
1182 return self._project
1184 @Project.setter
1185 def Project(self, value: 'Project') -> None:
1186 if not isinstance(value, Project): 1186 ↛ 1187line 1186 didn't jump to line 1187 because the condition on line 1186 was never true
1187 raise TypeError("Parameter 'value' is not of type 'Project'.")
1189 if value is None: 1189 ↛ 1191line 1189 didn't jump to line 1191 because the condition on line 1189 was never true
1190 # TODO: unlink VHDLLibrary from project
1191 self._project = None
1192 else:
1193 self._project = value
1194 if self._design is None: 1194 ↛ exitline 1194 didn't return from function 'Project' because the condition on line 1194 was always true
1195 self._design = value._defaultDesign
1197 @property
1198 def Design(self) -> Nullable['Design']:
1199 """Property setting or returning the design this VHDL library is used in."""
1200 return self._design
1202 @Design.setter
1203 def Design(self, value: 'Design') -> None:
1204 if not isinstance(value, Design):
1205 raise TypeError("Parameter 'value' is not of type 'Design'.")
1207 if value is None:
1208 # TODO: unlink VHDLLibrary from design
1209 self._design = None
1210 else:
1211 if self._design is None:
1212 self._design = value
1213 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph)
1214 elif self._design is not value:
1215 # TODO: move VHDLLibrary to other design
1216 # TODO: create new vertex in dependency graph and remove vertex from old graph
1217 self._design = value
1218 else:
1219 pass
1221 if self._project is None:
1222 self._project = value._project
1223 elif self._project is not value._project:
1224 raise Exception("The design's project is not identical to the already assigned project.")
1226 @property
1227 def Files(self) -> Generator[File, None, None]:
1228 """Read-only property to return all files in this VHDL library."""
1229 for file in self._files:
1230 yield file
1232 @property
1233 def VHDLVersion(self) -> VHDLVersion:
1234 """Property setting or returning the VHDL version of this VHDL library."""
1235 if self._vhdlVersion is not None:
1236 return self._vhdlVersion
1237 elif self._design is not None: 1237 ↛ 1240line 1237 didn't jump to line 1240 because the condition on line 1237 was always true
1238 return self._design.VHDLVersion
1239 else:
1240 raise Exception("VHDLVersion is not set on VHDLLibrary nor parent object.")
1242 @VHDLVersion.setter
1243 def VHDLVersion(self, value: VHDLVersion) -> None:
1244 self._vhdlVersion = value
1246 def AddDependency(self, library: 'VHDLLibrary') -> None:
1247 library.parent = self
1249 def AddFile(self, vhdlFile: VHDLSourceFile) -> None:
1250 if not isinstance(vhdlFile, VHDLSourceFile): 1250 ↛ 1251line 1250 didn't jump to line 1251 because the condition on line 1250 was never true
1251 ex = TypeError(f"Parameter 'vhdlFile' is not a 'VHDLSourceFile'.")
1252 if version_info >= (3, 11): # pragma: no cover
1253 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlFile)}'.")
1254 raise ex
1256 self._files.append(vhdlFile)
1258 def AddFiles(self, vhdlFiles: Iterable[VHDLSourceFile]) -> None:
1259 for vhdlFile in vhdlFiles:
1260 if not isinstance(vhdlFile, VHDLSourceFile):
1261 raise TypeError(f"Item '{vhdlFile}' in parameter 'vhdlFiles' is not a 'VHDLSourceFile'.")
1263 self._files.append(vhdlFile)
1265 @property
1266 def FileCount(self) -> int:
1267 """Returns number of files."""
1268 return len(self._files)
1270 def __len__(self) -> int:
1271 """
1272 Returns number of attributes set on this VHDL library.
1274 :returns: The number if attributes set on this VHDL library.
1275 """
1276 return len(self._attributes)
1278 def __getitem__(self, key: Type[Attribute]) -> Any:
1279 """Index access for returning attributes on this VHDL library.
1281 :param key: The attribute type.
1282 :returns: The attribute's value.
1283 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1284 """
1285 if not issubclass(key, Attribute):
1286 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1288 try:
1289 return self._attributes[key]
1290 except KeyError:
1291 return key.resolve(self, key)
1293 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1294 """
1295 Index access for adding or setting attributes on this VHDL library.
1297 :param key: The attribute type.
1298 :param value: The attributes value.
1299 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1300 """
1301 if not issubclass(key, Attribute):
1302 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1304 self._attributes[key] = value
1306 def __delitem__(self, key: Type[Attribute]) -> None:
1307 """
1308 Index access for deleting attributes on this VHDL library.
1310 :param key: The attribute type.
1311 """
1312 if not issubclass(key, Attribute):
1313 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1315 del self._attributes[key]
1317 def __str__(self) -> str:
1318 """Returns the VHDL library's name."""
1319 return self._name
1322@export
1323class Design(metaclass=ExtendedType, slots=True):
1324 """
1325 A :term:`Design` represents a group of filesets and the source files therein.
1327 Each design contains at least one fileset - the :term:`default fileset`. For
1328 designs with VHDL source files, a independent `VHDLLibraries` overlay structure
1329 exists.
1331 :arg name: The design's name.
1332 :arg topLevel: Name of the design's toplevel.
1333 :arg directory: Path of this design (absolute or relative to the project).
1334 :arg project: Project the design is associated with.
1335 :arg vhdlVersion: Default VHDL version for files in this design, if not specified for the file itself.
1336 :arg verilogVersion: Default Verilog version for files in this design, if not specified for the file itself.
1337 :arg svVersion: Default SystemVerilog version for files in this design, if not specified for the file itself.
1338 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself.
1339 """
1341 _name: str
1342 _topLevel: Nullable[str]
1343 _project: Nullable['Project']
1344 _directory: pathlib_Path
1345 _fileSets: Dict[str, FileSet]
1346 _defaultFileSet: Nullable[FileSet]
1347 _attributes: Dict[Type[Attribute], typing_Any]
1349 _vhdlLibraries: Dict[str, VHDLLibrary]
1350 _vhdlVersion: VHDLVersion
1351 _verilogVersion: SystemVerilogVersion
1352 _svVersion: SystemVerilogVersion
1353 _srdlVersion: SystemRDLVersion
1354 _externalVHDLLibraries: List
1356 _vhdlLibraryDependencyGraph: Graph
1357 _fileDependencyGraph: Graph
1359 def __init__(
1360 self,
1361 name: str,
1362 topLevel: Nullable[str] = None,
1363 directory: pathlib_Path = pathlib_Path("."),
1364 project: Nullable["Project"] = None,
1365 vhdlVersion: Nullable[VHDLVersion] = None,
1366 verilogVersion: Nullable[SystemVerilogVersion] = None,
1367 svVersion: Nullable[SystemVerilogVersion] = None,
1368 srdlVersion: Nullable[SystemRDLVersion] = None
1369 ):
1370 self._name = name
1371 self._topLevel = topLevel
1372 self._project = project
1373 if project is not None:
1374 project._designs[name] = self
1375 self._directory = directory
1376 self._fileSets = {}
1377 self._defaultFileSet = FileSet("default", project=project, design=self)
1378 self._attributes = {}
1379 self._vhdlLibraries = {}
1380 self._vhdlVersion = vhdlVersion
1381 self._verilogVersion = verilogVersion
1382 self._svVersion = svVersion
1383 self._srdlVersion = srdlVersion
1384 self._externalVHDLLibraries = []
1386 self._vhdlLibraryDependencyGraph = Graph()
1387 self._fileDependencyGraph = Graph()
1389 @property
1390 def Name(self) -> str:
1391 """Property setting or returning the design's name."""
1392 return self._name
1394 @Name.setter
1395 def Name(self, value: str) -> None:
1396 self._name = value
1398 @property
1399 def TopLevel(self) -> str:
1400 """Property setting or returning the fileset's toplevel."""
1401 return self._topLevel
1403 @TopLevel.setter
1404 def TopLevel(self, value: str) -> None:
1405 self._topLevel = value
1407 @property
1408 def Project(self) -> Nullable['Project']:
1409 """Property setting or returning the project this design is used in."""
1410 return self._project
1412 @Project.setter
1413 def Project(self, value: 'Project') -> None:
1414 self._project = value
1416 @property
1417 def Directory(self) -> pathlib_Path:
1418 """Property setting or returning the directory this design is located in."""
1419 return self._directory
1421 @Directory.setter
1422 def Directory(self, value: pathlib_Path) -> None:
1423 self._directory = value
1425 @property
1426 def ResolvedPath(self) -> pathlib_Path:
1427 """Read-only property returning the resolved path of this fileset."""
1428 if self._directory.is_absolute(): 1428 ↛ 1429line 1428 didn't jump to line 1429 because the condition on line 1428 was never true
1429 return self._directory.resolve()
1430 elif self._project is not None: 1430 ↛ 1440line 1430 didn't jump to line 1440 because the condition on line 1430 was always true
1431 path = (self._project.ResolvedPath / self._directory).resolve()
1433 if path.is_absolute(): 1433 ↛ 1437line 1433 didn't jump to line 1437 because the condition on line 1433 was always true
1434 return path
1435 else:
1436 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
1437 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
1438 else:
1439 # TODO: message and exception type
1440 raise Exception("")
1442 @property
1443 def DefaultFileSet(self) -> FileSet:
1444 """Property setting or returning the default fileset of this design."""
1445 return self._defaultFileSet
1447 @DefaultFileSet.setter
1448 def DefaultFileSet(self, value: Union[str, FileSet]) -> None:
1449 if isinstance(value, str):
1450 if value not in self._fileSets.keys():
1451 raise Exception(f"Fileset '{value}' is not in this design.")
1453 self._defaultFileSet = self._fileSets[value]
1454 elif isinstance(value, FileSet):
1455 if value not in self.FileSets:
1456 raise Exception(f"Fileset '{value}' is not associated to this design.")
1458 self._defaultFileSet = value
1459 else:
1460 raise ValueError("Unsupported parameter type for 'value'.")
1462 # TODO: return generator with another method
1463 @property
1464 def FileSets(self) -> Dict[str, FileSet]:
1465 """Read-only property returning the dictionary of filesets."""
1466 return self._fileSets
1468 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[str, FileSet] = None) -> Generator[File, None, None]:
1469 """
1470 Method returning the files of this design.
1472 :arg fileType: A filter for file types. Default: ``Any``.
1473 :arg fileSet: Specifies if all files from all filesets (``fileSet=None``) are files from a single fileset are returned.
1474 """
1475 if fileSet is None:
1476 for fileSet in self._fileSets.values():
1477 for file in fileSet.Files(fileType):
1478 yield file
1479 else:
1480 if isinstance(fileSet, str): 1480 ↛ 1485line 1480 didn't jump to line 1485 because the condition on line 1480 was always true
1481 try:
1482 fileSet = self._fileSets[fileSet]
1483 except KeyError as ex:
1484 raise Exception(f"Fileset {fileSet.Name} not bound to design {self.Name}.") from ex
1485 elif not isinstance(fileSet, FileSet):
1486 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.")
1488 for file in fileSet.Files(fileType):
1489 yield file
1491 def Validate(self) -> None:
1492 """Validate this design."""
1493 if self._name is None or self._name == "": 1493 ↛ 1494line 1493 didn't jump to line 1494 because the condition on line 1493 was never true
1494 raise Exception("Validation: Design has no name.")
1496 if self._directory is None: 1496 ↛ 1497line 1496 didn't jump to line 1497 because the condition on line 1496 was never true
1497 raise Exception(f"Validation: Design '{self._name}' has no directory.")
1498 try:
1499 path = self.ResolvedPath
1500 except Exception as ex:
1501 raise Exception(f"Validation: Design '{self._name}' could not compute resolved path.") from ex
1502 if not path.exists(): 1502 ↛ 1503line 1502 didn't jump to line 1503 because the condition on line 1502 was never true
1503 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' does not exist.")
1504 if not path.is_dir(): 1504 ↛ 1505line 1504 didn't jump to line 1505 because the condition on line 1504 was never true
1505 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' is not a directory.")
1507 if len(self._fileSets) == 0: 1507 ↛ 1508line 1507 didn't jump to line 1508 because the condition on line 1507 was never true
1508 raise Exception(f"Validation: Design '{self._name}' has no fileset.")
1509 try:
1510 if self._defaultFileSet is not self._fileSets[self._defaultFileSet.Name]: 1510 ↛ 1511line 1510 didn't jump to line 1511 because the condition on line 1510 was never true
1511 raise Exception(f"Validation: Design '{self._name}'s default fileset is the same as listed in filesets.")
1512 except KeyError as ex:
1513 raise Exception(f"Validation: Design '{self._name}'s default fileset is not in list of filesets.") from ex
1514 if self._project is None: 1514 ↛ 1515line 1514 didn't jump to line 1515 because the condition on line 1514 was never true
1515 raise Exception(f"Validation: Design '{self._path}' has no project.")
1517 for fileSet in self._fileSets.values():
1518 fileSet.Validate()
1520 @property
1521 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]:
1522 return self._vhdlLibraries
1524 @property
1525 def VHDLVersion(self) -> VHDLVersion:
1526 if self._vhdlVersion is not None:
1527 return self._vhdlVersion
1528 elif self._project is not None: 1528 ↛ 1531line 1528 didn't jump to line 1531 because the condition on line 1528 was always true
1529 return self._project.VHDLVersion
1530 else:
1531 raise Exception("VHDLVersion was neither set locally nor globally.")
1533 @VHDLVersion.setter
1534 def VHDLVersion(self, value: VHDLVersion) -> None:
1535 self._vhdlVersion = value
1537 @property
1538 def VerilogVersion(self) -> SystemVerilogVersion:
1539 if self._verilogVersion is not None:
1540 return self._verilogVersion
1541 elif self._project is not None: 1541 ↛ 1544line 1541 didn't jump to line 1544 because the condition on line 1541 was always true
1542 return self._project.VerilogVersion
1543 else:
1544 raise Exception("VerilogVersion was neither set locally nor globally.")
1546 @VerilogVersion.setter
1547 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1548 self._verilogVersion = value
1550 @property
1551 def SVVersion(self) -> SystemVerilogVersion:
1552 if self._svVersion is not None:
1553 return self._svVersion
1554 elif self._project is not None: 1554 ↛ 1557line 1554 didn't jump to line 1557 because the condition on line 1554 was always true
1555 return self._project.SVVersion
1556 else:
1557 raise Exception("SVVersion was neither set locally nor globally.")
1559 @SVVersion.setter
1560 def SVVersion(self, value: SystemVerilogVersion) -> None:
1561 self._svVersion = value
1563 @property
1564 def SRDLVersion(self) -> SystemRDLVersion:
1565 if self._srdlVersion is not None:
1566 return self._srdlVersion
1567 elif self._project is not None:
1568 return self._project.SRDLVersion
1569 else:
1570 raise Exception("SRDLVersion was neither set locally nor globally.")
1572 @SRDLVersion.setter
1573 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1574 self._srdlVersion = value
1576 @property
1577 def ExternalVHDLLibraries(self) -> List:
1578 return self._externalVHDLLibraries
1580 def AddFileSet(self, fileSet: FileSet) -> None:
1581 if not isinstance(fileSet, FileSet):
1582 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.")
1583 elif fileSet in self._fileSets:
1584 raise Exception("Design already contains this fileset.")
1585 elif fileSet.Name in self._fileSets.keys():
1586 raise Exception(f"Design already contains a fileset named '{fileSet.Name}'.")
1588 self._fileSets[fileSet.Name] = fileSet
1589 fileSet.Design = self
1590 fileSet._parent = self
1592 def AddFileSets(self, fileSets: Iterable[FileSet]) -> None:
1593 for fileSet in fileSets:
1594 self.AddFileSet(fileSet)
1596 @property
1597 def FileSetCount(self) -> int:
1598 """Returns number of file sets excl. sub-filesets."""
1599 return len(self._fileSets)
1601 @property
1602 def TotalFileSetCount(self) -> int:
1603 """Returns number of file sets incl. sub-filesets."""
1604 fileSetCount = len(self._fileSets)
1605 for fileSet in self._fileSets.values():
1606 fileSetCount += fileSet.TotalFileSetCount
1608 return fileSetCount
1610 def AddFile(self, file: File) -> None:
1611 if file.FileSet is None: 1611 ↛ 1614line 1611 didn't jump to line 1614 because the condition on line 1611 was always true
1612 self._defaultFileSet.AddFile(file)
1613 else:
1614 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.")
1616 def AddFiles(self, files: Iterable[File]) -> None:
1617 for file in files:
1618 self.AddFile(file)
1620 def AddVHDLLibrary(self, vhdlLibrary: VHDLLibrary) -> None:
1621 if vhdlLibrary.Name in self._vhdlLibraries:
1622 if self._vhdlLibraries[vhdlLibrary.Name] is vhdlLibrary:
1623 raise Exception(f"The VHDLLibrary '{vhdlLibrary.Name}' was already added to the design.")
1624 else:
1625 raise Exception(f"A VHDLLibrary with same name ('{vhdlLibrary.Name}') already exists for this design.")
1628 def __len__(self) -> int:
1629 """
1630 Returns number of attributes set on this design.
1632 :returns: The number if attributes set on this design.
1633 """
1634 return len(self._attributes)
1636 def __getitem__(self, key: Type[Attribute]) -> Any:
1637 """Index access for returning attributes on this design.
1639 :param key: The attribute type.
1640 :returns: The attribute's value.
1641 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1642 """
1643 if not issubclass(key, Attribute): 1643 ↛ 1644line 1643 didn't jump to line 1644 because the condition on line 1643 was never true
1644 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1646 try:
1647 return self._attributes[key]
1648 except KeyError:
1649 return key.resolve(self, key)
1651 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1652 """
1653 Index access for adding or setting attributes on this design.
1655 :param key: The attribute type.
1656 :param value: The attributes value.
1657 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1658 """
1659 if not issubclass(key, Attribute): 1659 ↛ 1660line 1659 didn't jump to line 1660 because the condition on line 1659 was never true
1660 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1662 self._attributes[key] = value
1664 def __delitem__(self, key: Type[Attribute]) -> None:
1665 """
1666 Index access for deleting attributes on this design.
1668 :param key: The attribute type.
1669 """
1670 if not issubclass(key, Attribute): 1670 ↛ 1671line 1670 didn't jump to line 1671 because the condition on line 1670 was never true
1671 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1673 del self._attributes[key]
1675 def __str__(self) -> str:
1676 return self._name
1679@export
1680class Project(metaclass=ExtendedType, slots=True):
1681 """
1682 A :term:`Project` represents a group of designs and the source files therein.
1684 :arg name: The project's name.
1685 :arg rootDirectory: Base-path to the project.
1686 :arg vhdlVersion: Default VHDL version for files in this project, if not specified for the file itself.
1687 :arg verilogVersion: Default Verilog version for files in this project, if not specified for the file itself.
1688 :arg svVersion: Default SystemVerilog version for files in this project, if not specified for the file itself.
1689 """
1691 _name: str
1692 _rootDirectory: pathlib_Path
1693 _designs: Dict[str, Design]
1694 _defaultDesign: Design
1695 _attributes: Dict[Type[Attribute], typing_Any]
1697 _vhdlVersion: VHDLVersion
1698 _verilogVersion: SystemVerilogVersion
1699 _svVersion: SystemVerilogVersion
1700 _srdlVersion: SystemRDLVersion
1702 def __init__(
1703 self,
1704 name: str,
1705 rootDirectory: pathlib_Path = pathlib_Path("."),
1706 vhdlVersion: Nullable[VHDLVersion] = None,
1707 verilogVersion: Nullable[SystemVerilogVersion] = None,
1708 svVersion: Nullable[SystemVerilogVersion] = None
1709 ):
1710 self._name = name
1711 self._rootDirectory = rootDirectory
1712 self._designs = {}
1713 self._defaultDesign = Design("default", project=self)
1714 self._attributes = {}
1715 self._vhdlVersion = vhdlVersion
1716 self._verilogVersion = verilogVersion
1717 self._svVersion = svVersion
1719 @property
1720 def Name(self) -> str:
1721 """Property setting or returning the project's name."""
1722 return self._name
1724 @property
1725 def RootDirectory(self) -> pathlib_Path:
1726 """Property setting or returning the root directory this project is located in."""
1727 return self._rootDirectory
1729 @RootDirectory.setter
1730 def RootDirectory(self, value: pathlib_Path) -> None:
1731 self._rootDirectory = value
1733 @property
1734 def ResolvedPath(self) -> pathlib_Path:
1735 """Read-only property returning the resolved path of this fileset."""
1736 path = self._rootDirectory.resolve()
1737 if self._rootDirectory.is_absolute():
1738 return path
1739 else:
1740 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
1741 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
1743 # TODO: return generator with another method
1744 @property
1745 def Designs(self) -> Dict[str, Design]:
1746 return self._designs
1748 @property
1749 def DefaultDesign(self) -> Design:
1750 return self._defaultDesign
1752 def Validate(self) -> None:
1753 """Validate this project."""
1754 if self._name is None or self._name == "": 1754 ↛ 1755line 1754 didn't jump to line 1755 because the condition on line 1754 was never true
1755 raise Exception("Validation: Project has no name.")
1757 if self._rootDirectory is None: 1757 ↛ 1758line 1757 didn't jump to line 1758 because the condition on line 1757 was never true
1758 raise Exception(f"Validation: Project '{self._name}' has no root directory.")
1759 try:
1760 path = self.ResolvedPath
1761 except Exception as ex:
1762 raise Exception(f"Validation: Project '{self._name}' could not compute resolved path.") from ex
1763 if not path.exists(): 1763 ↛ 1764line 1763 didn't jump to line 1764 because the condition on line 1763 was never true
1764 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' does not exist.")
1765 if not path.is_dir(): 1765 ↛ 1766line 1765 didn't jump to line 1766 because the condition on line 1765 was never true
1766 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' is not a directory.")
1768 if len(self._designs) == 0: 1768 ↛ 1769line 1768 didn't jump to line 1769 because the condition on line 1768 was never true
1769 raise Exception(f"Validation: Project '{self._name}' has no design.")
1770 try:
1771 if self._defaultDesign is not self._designs[self._defaultDesign.Name]: 1771 ↛ 1772line 1771 didn't jump to line 1772 because the condition on line 1771 was never true
1772 raise Exception(f"Validation: Project '{self._name}'s default design is the same as listed in designs.")
1773 except KeyError as ex:
1774 raise Exception(f"Validation: Project '{self._name}'s default design is not in list of designs.") from ex
1776 for design in self._designs.values():
1777 design.Validate()
1779 @property
1780 def DesignCount(self) -> int:
1781 """Returns number of designs."""
1782 return len(self._designs)
1784 @property
1785 def VHDLVersion(self) -> VHDLVersion:
1786 # TODO: check for None and return exception
1787 return self._vhdlVersion
1789 @VHDLVersion.setter
1790 def VHDLVersion(self, value: VHDLVersion) -> None:
1791 self._vhdlVersion = value
1793 @property
1794 def VerilogVersion(self) -> SystemVerilogVersion:
1795 # TODO: check for None and return exception
1796 return self._verilogVersion
1798 @VerilogVersion.setter
1799 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1800 self._verilogVersion = value
1802 @property
1803 def SVVersion(self) -> SystemVerilogVersion:
1804 # TODO: check for None and return exception
1805 return self._svVersion
1807 @SVVersion.setter
1808 def SVVersion(self, value: SystemVerilogVersion) -> None:
1809 self._svVersion = value
1811 @property
1812 def SRDLVersion(self) -> SystemRDLVersion:
1813 # TODO: check for None and return exception
1814 return self._srdlVersion
1816 @SRDLVersion.setter
1817 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1818 self._srdlVersion = value
1820 def __len__(self) -> int:
1821 """
1822 Returns number of attributes set on this project.
1824 :returns: The number if attributes set on this project.
1825 """
1826 return len(self._attributes)
1828 def __getitem__(self, key: Type[Attribute]) -> Any:
1829 """Index access for returning attributes on this project.
1831 :param key: The attribute type.
1832 :returns: The attribute's value.
1833 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1834 """
1835 if not issubclass(key, Attribute): 1835 ↛ 1836line 1835 didn't jump to line 1836 because the condition on line 1835 was never true
1836 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1838 try:
1839 return self._attributes[key]
1840 except KeyError:
1841 return key.resolve(self, key)
1843 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1844 """
1845 Index access for adding or setting attributes on this project.
1847 :param key: The attribute type.
1848 :param value: The attributes value.
1849 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1850 """
1851 if not issubclass(key, Attribute): 1851 ↛ 1852line 1851 didn't jump to line 1852 because the condition on line 1851 was never true
1852 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1854 self._attributes[key] = value
1856 def __delitem__(self, key: Type[Attribute]) -> None:
1857 """
1858 Index access for deleting attributes on this project.
1860 :param key: The attribute type.
1861 """
1862 if not issubclass(key, Attribute): 1862 ↛ 1863line 1862 didn't jump to line 1863 because the condition on line 1862 was never true
1863 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1865 del self._attributes[key]
1867 def __str__(self) -> str:
1868 return self._name