Source code for pyIPCMI.Compiler

# EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t; python-indent-offset: 2 -*-
# vim: tabstop=2:shiftwidth=2:noexpandtab
# kate: tab-width 2; replace-tabs off; indent-width 2;
#
# ==============================================================================
# Authors:               Patrick Lehmann
#
# Python Sub Module:    TODO:
#
# License:
# ==============================================================================
# Copyright 2017-2018 Patrick Lehmann - Bötzingen, Germany
# Copyright 2007-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.
# ==============================================================================
#
from enum                         import unique, Enum
from pathlib                      import Path
from re                           import compile as re_compile, subn as re_subn, IGNORECASE as RE_IGNORECASE, MULTILINE as RE_MULTILINE, DOTALL as RE_DOTALL
from shutil                       import copy as shutil_copy

from flags                        import Flags

from lib.Functions                import Init
from lib.Parser                   import ParserException
from pyIPCMI.Base                 import IHost
from pyIPCMI.Base.Exceptions      import ExceptionBase, SkipableException
from pyIPCMI.Base.Project         import Environment, VHDLVersion, FileTypes
from pyIPCMI.Base.Shared          import Shared, to_time
from pyIPCMI.Parser.RulesParser   import CopyRuleMixIn, DeleteRuleMixIn, ReplaceRuleMixIn, AppendLineRuleMixIn
from pyIPCMI.DataBase.Solution    import RulesFile
from pyIPCMI.DataBase.TestCase    import SynthesisSuite, CompileStatus, Synthesis

# required for autoapi.sphinx
from lib.SphinxExtensions         import DocumentMemberAttribute


__api__ = [
	'CompilerException',
	'SkipableCompilerException',
	'CopyTask',
	'DeleteTask',
	'ReplaceTask',
	'AppendLineTask',
	'CompileState',
	'CompileResult',
	'Compiler'
]
__all__ = __api__


[docs]class CompilerException(ExceptionBase): """Base class for all CompilerException classes. It is raised while running compiler (synthesis) tasks in pyIPCMI. """
[docs]class SkipableCompilerException(CompilerException, SkipableException): """``SkipableCompilerException`` is a :py:exc:`CompilerException`, which can be skipped. """
[docs]class CopyTask(CopyRuleMixIn): """This class represents a 'copy task' and inherits the partial class :class:`CopyRuleMixIn <Parser.RulesParser.CopyRuleMixIn>`. """
[docs]class DeleteTask(DeleteRuleMixIn): """This class represents a 'delete task' and inherits the partial class :class:`DeleteRuleMixIn <Parser.RulesParser.DeleteRuleMixIn>`. """
[docs]class ReplaceTask(ReplaceRuleMixIn): """This class represents a 'replace task' and inherits the partial class :class:`ReplaceRuleMixIn <Parser.RulesParser.ReplaceRuleMixIn>`. """
[docs]class AppendLineTask(AppendLineRuleMixIn): """This class represents a 'append line task' and inherits the partial class :class:`AppendLineRuleMixIn <Parser.RulesParser.AppendLineRuleMixIn>`. """
@unique class CompileSteps(Flags): """Compiler step enumeration.""" Prepare = 1 << 0 CleanUpBefore = 1 << 1 CleanUpAfter = 1 << 2 Synthesize = 1 << 5 Merge = 1 << 6 AnalyzeTiming = 1 << 7 Place = 1 << 8 Route = 1 << 9 WriteBitfile = 1 << 10 ShowReport = 1 << 15 ShowGUI = 1 << 20 # Recompile = 1 << 25 # Resimulate = 1 << 26 # Review = 1 << 27
[docs]@unique class CompileState(Enum): """Compile state enumeration.""" Prepare = 0 PreCopy = 10 PrePatch = 11 Compile = 50 # Analyze = 61 # Elaborate = 62 # Optimize = 63 # Translate = 64 # Map = 65 # Place = 66 # Route = 67 PostCopy = 90 PostPatch = 91 PostDelete = 92 CleanUp = 99
[docs]@unique class CompileResult(Enum): """Compilation result enumeration.""" NotRun = 0 Error = 1 Failed = 2 Success = 3
[docs]class Compiler(Shared): """Base class for all Compiler classes.""" ENVIRONMENT = Environment.Synthesis VHDL_VERSION = VHDLVersion.VHDL93 class __Directories__(Shared.__Directories__): Netlist = None Source = None Destination = None
[docs] @DocumentMemberAttribute() def __init__(self, host : IHost, dryRun, noCleanUp): """Class initializer :type host: object :param host: The hosting instance for this instance. :type dryRun: bool :param dryRun: Enable dry-run mode :type noCleanUp: bool :param noCleanUp: Don't clean up after a run. """ super().__init__(host, dryRun) self._noCleanUp = noCleanUp self._testSuite = SynthesisSuite() # TODO: This includes not the read ini files phases ... self._state = CompileState.Prepare self._preTasksTime = None self._compileTime = None self._postTasksTime = None
@property def NoCleanUp(self): return self._noCleanUp
[docs] def _PrepareCompiler(self): """Prepare for compilation. This method forwards to :py:meth:`Base.Compiler.Compiler._Prepare`, which is inherited from :py:class:`Base.Shared.Shared`. """
[docs] def TryRun(self, netlist, *args, **kwargs): """Try to run a testbench. Skip skipable exceptions by printing the error and its cause.""" __COMPILE_STATE_TO_SYNTHESIS_STATUS__ = { CompileState.Prepare: CompileStatus.InternalError, CompileState.PreCopy: CompileStatus.SystemError, CompileState.PrePatch: CompileStatus.SystemError, CompileState.Compile: CompileStatus.CompileError, CompileState.PostCopy: CompileStatus.SystemError, CompileState.PostPatch: CompileStatus.SystemError, CompileState.PostDelete: CompileStatus.SystemError } synthesis = Synthesis(netlist) self._testSuite.AddSynthesis(synthesis) synthesis.StartTimer() try: self.Run(netlist, *args, **kwargs) # synthesis.UpdateStatus(netlist.Result) synthesis.Status = CompileStatus.CompileSuccess except SkipableCompilerException as ex: synthesis.Status = __COMPILE_STATE_TO_SYNTHESIS_STATUS__[self._state] self.LogQuiet(" {RED}ERROR:{NOCOLOR} {0}".format(ex.message, **Init.Foreground)) cause = ex.__cause__ if (cause is not None): self.LogQuiet(" {YELLOW}{ExType}:{NOCOLOR} {ExMsg!s}".format(ExType=cause.__class__.__name__, ExMsg=cause, **Init.Foreground)) cause = cause.__cause__ if (cause is not None): self.LogQuiet(" {YELLOW}{ExType}:{NOCOLOR} {ExMsg!s}".format(ExType=cause.__class__.__name__, ExMsg=cause, **Init.Foreground)) self.LogQuiet(" {RED}[SKIPPED DUE TO ERRORS]{NOCOLOR}".format(**Init.Foreground)) except CompilerException: synthesis.Status = __COMPILE_STATE_TO_SYNTHESIS_STATUS__[self._state] raise except ExceptionBase: synthesis.Status = CompileStatus.SystemError raise finally: synthesis.StopTimer()
[docs] def Run(self, netlist, board): """Run a testbench.""" self.LogQuiet("{CYAN}IP core: {0!s}{NOCOLOR}".format(netlist.Parent, **Init.Foreground)) # # TODO: refactor # self.LogNormal("Checking for dependencies:") # for dependency in netlist.Dependencies: # print(" " + str(dependency)) # setup all needed paths to execute fuse self._PrepareCompilerEnvironment(board.Device) self._WriteSpecialSectionIntoConfig(board.Device) self._CreatepyIPCMIProject(netlist.ModuleName, board) if netlist.FilesFile is not None: self._AddFileListFile(netlist.FilesFile) if (netlist.RulesFile is not None): self._AddRulesFiles(netlist.RulesFile)
[docs] def _PrepareCompilerEnvironment(self, device): self.LogNormal("Preparing synthesis environment...") self.Directories.Destination = self.Directories.Netlist / str(device) self._PrepareEnvironment() # create output directory for CoreGen if not existent if (not self.Directories.Destination.exists()) : self.LogVerbose("Creating output directory for generated files.") self.LogDebug("Output directory: {0!s}.".format(self.Directories.Destination)) try: self.Directories.Destination.mkdir(parents=True) except OSError as ex: raise CompilerException("Error while creating '{0!s}'.".format(self.Directories.Destination)) from ex
[docs] def _WriteSpecialSectionIntoConfig(self, device): # add the key Device to section SPECIAL at runtime to change interpolation results self.Host.Config['SPECIAL'] = {} self.Host.Config['SPECIAL']['Device'] = device.ShortName self.Host.Config['SPECIAL']['DeviceSeries'] = device.Series self.Host.Config['SPECIAL']['OutputDir'] = self.Directories.Working.as_posix()
[docs] def _AddRulesFiles(self, rulesFilePath): self.LogVerbose("Reading rules from '{0!s}'".format(rulesFilePath)) # add the *.rules file, parse and evaluate it try: rulesFile = self._pyIPCMIProject.AddFile(RulesFile(rulesFilePath)) rulesFile.Parse() except ParserException as ex: raise SkipableCompilerException("Error while parsing '{0!s}'.".format(rulesFilePath)) from ex self.LogDebug("Pre-process rules:") for rule in rulesFile.PreProcessRules: self.LogDebug(" {0!s}".format(rule)) self.LogDebug("Post-process rules:") for rule in rulesFile.PostProcessRules: self.LogDebug(" {0!s}".format(rule))
[docs] def _RunPreCopy(self, netlist): self.LogVerbose("Copy further input files into temporary directory...") rulesFiles = [file for file in self.pyIPCMIProject.Files(fileType=FileTypes.RulesFile)] # FIXME: get rulefile from netlist object as a rulefile object instead of a path preCopyTasks = [] if (rulesFiles): for rule in rulesFiles[0].PreProcessRules: if isinstance(rule, CopyRuleMixIn): sourcePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.SourcePath, {}) destinationPath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.DestinationPath, {}) task = CopyTask(Path(sourcePath), Path(destinationPath)) preCopyTasks.append(task) else: preCopyRules = self.Host.Config[netlist.ConfigSectionName]['PreCopyRules'] self._ParseCopyRules(preCopyRules, preCopyTasks, "pre") if (len(preCopyTasks) != 0): self._ExecuteCopyTasks(preCopyTasks, "pre") else: self.LogDebug("Nothing to copy")
[docs] def _RunPostCopy(self, netlist): self.LogVerbose("copy generated files into netlist directory...") rulesFiles = [file for file in self.pyIPCMIProject.Files(fileType=FileTypes.RulesFile)] # FIXME: get rulefile from netlist object as a rulefile object instead of a path postCopyTasks = [] if (rulesFiles): for rule in rulesFiles[0].PostProcessRules: if isinstance(rule, CopyRuleMixIn): sourcePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.SourcePath, {}) destinationPath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.DestinationPath, {}) task = CopyTask(Path(sourcePath), Path(destinationPath)) postCopyTasks.append(task) else: postCopyRules = self.Host.Config[netlist.ConfigSectionName]['PostCopyRules'] self._ParseCopyRules(postCopyRules, postCopyTasks, "post") if (len(postCopyTasks) != 0): self._ExecuteCopyTasks(postCopyTasks, "post") else: self.LogDebug("Nothing to copy")
[docs] def _ParseCopyRules(self, rawList, copyTasks, text): # read copy tasks if (len(rawList) != 0): self.LogDebug("Parsing {0}-copy tasks from config file:".format(text)) rawList = rawList.split("\n") copyRegExpStr = r"^\s*(?P<SourceFilename>.*?)" # Source filename copyRegExpStr += r"\s->\s" # Delimiter signs copyRegExpStr += r"(?P<DestFilename>.*?)$" # Destination filename copyRegExp = re_compile(copyRegExpStr) for item in rawList: preCopyRegExpMatch = copyRegExp.match(item) if (preCopyRegExpMatch is None): raise CompilerException("Error in copy rule '{0}'.".format(item)) task = CopyTask( Path(preCopyRegExpMatch.group('SourceFilename')), Path(preCopyRegExpMatch.group('DestFilename')) ) copyTasks.append(task) self.LogDebug(" {0!s}".format(task)) else: self.LogDebug("No {0}-copy tasks specified in config file.".format(text))
[docs] def _ExecuteCopyTasks(self, tasks, text): for task in tasks: if (not self.DryRun and not task.SourcePath.exists()): raise CompilerException("Cannot {0}-copy '{1!s}' to destination.".format(text, task.SourcePath)) from FileNotFoundError(str(task.SourcePath)) if not task.DestinationPath.parent.exists(): if self.DryRun: self.LogDryRun("mkdir '{0!s}'.".format(task.DestinationPath.parent)) else: try: task.DestinationPath.parent.mkdir(parents=True) except OSError as ex: raise CompilerException("Error while creating '{0!s}'.".format(task.DestinationPath.parent)) from ex self.LogDebug("{0}-copying '{1!s}'.".format(text, task.SourcePath)) if self.DryRun: self.LogDryRun("Copy '{0!s}' to '{1!s}'.".format(task.SourcePath, task.DestinationPath)) else: try: shutil_copy(str(task.SourcePath), str(task.DestinationPath)) except OSError as ex: raise CompilerException("Error while copying '{0!s}'.".format(task.SourcePath)) from ex
[docs] def _RunPostDelete(self, netlist): self.LogVerbose("copy generated files into netlist directory...") rulesFiles = [file for file in self.pyIPCMIProject.Files(fileType=FileTypes.RulesFile)] # FIXME: get rulefile from netlist object as a rulefile object instead of a path postDeleteTasks = [] if (rulesFiles): for rule in rulesFiles[0].PostProcessRules: if isinstance(rule, DeleteRuleMixIn): filePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.FilePath, {}) task = DeleteTask(Path(filePath)) postDeleteTasks.append(task) else: postDeleteRules = self.Host.Config[netlist.ConfigSectionName]['PostDeleteRules'] self._ParseDeleteRules(postDeleteRules, postDeleteTasks, "post") if self.NoCleanUp: self.LogWarning("Disabled cleanup. Skipping post-delete rules.") elif (len(postDeleteTasks) != 0): self._ExecuteDeleteTasks(postDeleteTasks, "post") else: self.LogDebug("Nothing to delete")
[docs] def _ParseDeleteRules(self, rawList, deleteTasks, text): # read delete tasks if (len(rawList) != 0): self.LogDebug("Parse {0}-delete tasks from config file:".format(text)) rawList = rawList.split("\n") deleteRegExpStr = r"^\s*(?P<Filename>.*?)$" # filename deleteRegExp = re_compile(deleteRegExpStr) for item in rawList: deleteRegExpMatch = deleteRegExp.match(item) if (deleteRegExpMatch is None): raise CompilerException("Error in delete rule '{0}'.".format(item)) task = DeleteTask(Path(deleteRegExpMatch.group('Filename'))) deleteTasks.append(task) self.LogDebug(" {0!s}".format(task)) else: self.LogDebug("No {0}-delete tasks specified in config file.".format(text))
[docs] def _ExecuteDeleteTasks(self, tasks, text): for task in tasks: if (not self.DryRun and not task.FilePath.exists()): raise CompilerException("Cannot {0}-delete '{1!s}'.".format(text, task.FilePath)) from FileNotFoundError(str(task.FilePath)) self.LogDebug("{0}-deleting '{1!s}'.".format(text, task.FilePath)) if self.DryRun: self.LogDryRun("Delete '{0!s}'.".format(task.FilePath)) else: try: task.FilePath.unlink() except OSError as ex: raise CompilerException("Error while deleting '{0!s}'.".format(task.FilePath)) from ex
[docs] def _RunPreReplace(self, netlist): self.LogVerbose("Patching files in temporary directory...") rulesFiles = [file for file in self.pyIPCMIProject.Files(fileType=FileTypes.RulesFile)] # FIXME: get rulefile from netlist object as a rulefile object instead of a path preReplaceTasks = [] if (rulesFiles): for rule in rulesFiles[0].PreProcessRules: if isinstance(rule, ReplaceRuleMixIn): filePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.FilePath, {}) searchPattern = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.SearchPattern, {}) replacePattern = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.ReplacePattern, {}) task = ReplaceTask(Path(filePath), searchPattern, replacePattern, rule.RegExpOption_MultiLine, rule.RegExpOption_DotAll, rule.RegExpOption_CaseInsensitive) preReplaceTasks.append(task) elif isinstance(rule, AppendLineRuleMixIn): filePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.FilePath, {}) appendPattern = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.AppendPattern, {}) task = AppendLineTask(Path(filePath), appendPattern) preReplaceTasks.append(task) elif isinstance(rule, CopyRuleMixIn): pass else: raise CompilerException("Unknown pre-process rule '{0!s}'.".format(rule)) else: preReplaceRules = self.Host.Config[netlist.ConfigSectionName]['PreReplaceRules'] self._ParseReplaceRules(preReplaceRules, preReplaceTasks, "pre") if (len(preReplaceTasks) != 0): self._ExecuteReplaceTasks(preReplaceTasks, "pre") else: self.LogDebug("Nothing to patch.")
[docs] def _RunPostReplace(self, netlist): self.LogVerbose("Patching files in netlist directory...") rulesFiles = [file for file in self.pyIPCMIProject.Files(fileType=FileTypes.RulesFile)] # FIXME: get rulefile from netlist object as a rulefile object instead of a path postReplaceTasks = [] if (rulesFiles): for rule in rulesFiles[0].PostProcessRules: if isinstance(rule, ReplaceRuleMixIn): filePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.FilePath, {}) searchPattern = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.SearchPattern, {}) replacePattern = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.ReplacePattern, {}) task = ReplaceTask(Path(filePath), searchPattern, replacePattern, rule.RegExpOption_MultiLine, rule.RegExpOption_DotAll, rule.RegExpOption_CaseInsensitive) postReplaceTasks.append(task) elif isinstance(rule, AppendLineRuleMixIn): filePath = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.FilePath, {}) appendPattern = self.Host.Config.Interpolation.interpolate(self.Host.Config, netlist.ConfigSectionName, "RulesFile", rule.AppendPattern, {}) task = AppendLineTask(Path(filePath), appendPattern) postReplaceTasks.append(task) elif isinstance(rule, (CopyRuleMixIn, DeleteRuleMixIn)): pass else: raise CompilerException("Unknown post-process rule '{0!s}'.".format(rule)) else: postReplaceRules = self.Host.Config[netlist.ConfigSectionName]['PostReplaceRules'] self._ParseReplaceRules(postReplaceRules, postReplaceTasks, "post") if (len(postReplaceTasks) != 0): self._ExecuteReplaceTasks(postReplaceTasks, "post") else: self.LogDebug("Nothing to patch.")
[docs] def _ParseReplaceRules(self, rawList, replaceTasks, text): # read replace tasks if (len(rawList) != 0): self.LogDebug("Parsing {0}-replacement tasks:".format(text)) rawList = rawList.split("\n") # FIXME: Rework inline replace rule syntax. replaceRegExpStr = r"^\s*(?P<Filename>.*?)\s+:" # Filename replaceRegExpStr += r"(?P<Options>[dim]{0,3}):\s+" # RegExp options replaceRegExpStr += r"\"(?P<Search>.*?)\"\s+->\s+" # Search regexp replaceRegExpStr += r"\"(?P<Replace>.*?)\"$" # Replace regexp replaceRegExp = re_compile(replaceRegExpStr) for item in rawList: replaceRegExpMatch = replaceRegExp.match(item) if (replaceRegExpMatch is None): raise CompilerException("Error in replace rule '{0}'.".format(item)) task = ReplaceTask( Path(replaceRegExpMatch.group('Filename')), replaceRegExpMatch.group('Search'), replaceRegExpMatch.group('Replace'), # replaceRegExpMatch.group('Options'), # FIXME: # replaceRegExpMatch.group('Options'), # FIXME: # replaceRegExpMatch.group('Options'), # FIXME: False, False, False ) replaceTasks.append(task) self.LogDebug(" {0!s}".format(task)) else: self.LogDebug("No {0}-replace tasks specified in config file.".format(text))
[docs] def _ExecuteReplaceTasks(self, tasks, text): for task in tasks: if (not self.DryRun and not task.FilePath.exists()): raise CompilerException("Cannot {0}-replace in file '{1!s}'.".format(text, task.FilePath)) from FileNotFoundError(str(task.FilePath)) self.LogDebug("{0}-replace in file '{1!s}': search for '{2}' replace by '{3}'.".format(text, task.FilePath, task.SearchPattern, task.ReplacePattern)) if self.DryRun: self.LogDryRun("Patch '{0!s}'.".format(task.FilePath)) else: regExpFlags = 0 if task.RegExpOption_CaseInsensitive: regExpFlags |= RE_IGNORECASE if task.RegExpOption_MultiLine: regExpFlags |= RE_MULTILINE if task.RegExpOption_DotAll: regExpFlags |= RE_DOTALL # compile regexp regExp = re_compile(task.SearchPattern, regExpFlags) # open file and read all lines with task.FilePath.open('r') as fileHandle: FileContent = fileHandle.read() # replace NewContent,replaceCount = re_subn(regExp, task.ReplacePattern, FileContent) if (replaceCount == 0): self.LogWarning(" Search pattern '{0}' not found in file '{1!s}'.".format(task.SearchPattern, task.FilePath)) # open file to write the replaced data with task.FilePath.open('w') as fileHandle: fileHandle.write(NewContent)
[docs] def PrintOverallCompileReport(self): self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground)) self.LogQuiet("{HEADLINE}{headline: ^80s}{NOCOLOR}".format(headline="Overall Compile Report", **Init.Foreground)) self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground)) # table header self.LogQuiet("{Name: <24} | {Duration: >5} | {Status: ^11}".format(Name="Name", Duration="Time", Status="Status")) self.LogQuiet("-" * 80) self.PrintCompileReportLine(self._testSuite, 0, 24) self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground)) self.LogQuiet("Time: {time: >5} Count: {count: <3} Success: {success: <3} Failed: {failed: <2} Errors: {error: <2}".format( time=to_time(self._testSuite.OverallRunTime), count=self._testSuite.Count, success=self._testSuite.SuccessCount, failed=self._testSuite.FailedCount, error=self._testSuite.ErrorCount )) self.LogQuiet("{HEADLINE}{line}{NOCOLOR}".format(line="=" * 80, **Init.Foreground))
__COMPILE_REPORT_COLOR_TABLE__ = { CompileStatus.Unknown: "RED", CompileStatus.InternalError: "DARK_RED", CompileStatus.SystemError: "DARK_RED", CompileStatus.CompileError: "RED", CompileStatus.CompileSuccess: "GREEN" } __COMPILE_REPORT_STATUS_TEXT_TABLE__ = { CompileStatus.Unknown: "-- ?? --", CompileStatus.InternalError: "INT. ERROR", CompileStatus.SystemError: "SYS. ERROR", CompileStatus.CompileError: "COMP. ERROR", CompileStatus.CompileSuccess: "SUCCESS" }
[docs] def PrintCompileReportLine(self, testObject, indent, nameColumnWidth): _indent = " " * indent for group in testObject.Groups.values(): pattern = "{indent}{{groupName: <{nameColumnWidth}}} | | ".format(indent=_indent, nameColumnWidth=nameColumnWidth) self.LogQuiet(pattern.format(groupName=group.Name)) self.PrintCompileReportLine(group, indent + 1, nameColumnWidth - 2) for synthesis in testObject.Synthesises.values(): pattern = "{indent}{{netlistName: <{nameColumnWidth}}} | {{duration: >5}} | {{{color}}}{{status: ^11}}{{NOCOLOR}}".format( indent=_indent, nameColumnWidth=nameColumnWidth, color=self.__COMPILE_REPORT_COLOR_TABLE__[synthesis.Status]) self.LogQuiet(pattern.format( netlistName=synthesis.Name, duration=to_time(synthesis.OverallRunTime), status=self.__COMPILE_REPORT_STATUS_TEXT_TABLE__[synthesis.Status], **Init.Foreground ))