aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/Driver.scala4
-rw-r--r--src/main/scala/firrtl/ExecutionOptionsManager.scala80
-rw-r--r--src/main/scala/firrtl/LoweringCompilers.scala2
-rw-r--r--src/main/scala/logger/Logger.scala101
4 files changed, 182 insertions, 5 deletions
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala
index 5dbbf16a..ba5527b4 100644
--- a/src/main/scala/firrtl/Driver.scala
+++ b/src/main/scala/firrtl/Driver.scala
@@ -4,6 +4,8 @@ package firrtl
import java.io.FileNotFoundException
+import logger.Logger
+
import scala.io.Source
import Annotations._
@@ -74,6 +76,8 @@ object Driver {
def execute(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): FirrtlExecutionResult = {
val firrtlConfig = optionsManager.firrtlOptions
+ Logger.setOptions(optionsManager)
+
val firrtlSource = firrtlConfig.firrtlSource match {
case Some(text) => text.split("\n").toIterator
case None =>
diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala
index 05a9f487..ff725e30 100644
--- a/src/main/scala/firrtl/ExecutionOptionsManager.scala
+++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala
@@ -5,6 +5,7 @@ package firrtl
import firrtl.Annotations._
import firrtl.Parser._
import firrtl.passes.memlib.ReplSeqMemAnnotation
+import logger.LogLevel
import scopt.OptionParser
import scala.collection.Seq
@@ -15,6 +16,10 @@ import scala.collection.Seq
*/
trait ComposableOptions
+abstract class HasParser(applicationName: String) {
+ final val parser: OptionParser[Unit] = new OptionParser[Unit](applicationName) {}
+}
+
/**
* Most of the chisel toolchain components require a topName which defines a circuit or a device under test.
* Much of the work that is done takes place in a directory.
@@ -22,10 +27,22 @@ trait ComposableOptions
* For example, in chisel, by deferring this it is possible for the execute there to first elaborate the
* circuit and then set the topName from that if it has not already been set.
*/
-case class CommonOptions(topName: String = "", targetDirName: String = "test_run_dir") extends ComposableOptions
-
-abstract class HasParser(applicationName: String) {
- final val parser: OptionParser[Unit] = new OptionParser[Unit](applicationName) {}
+case class CommonOptions(
+ topName: String = "",
+ targetDirName: String = "test_run_dir",
+ globalLogLevel: LogLevel.Value = LogLevel.Error,
+ logToFile: Boolean = false,
+ logClassNames: Boolean = false,
+ classLogLevels: Map[String, LogLevel.Value] = Map.empty) extends ComposableOptions {
+
+ def getLogFileName(optionsManager: ExecutionOptionsManager): String = {
+ if(topName.isEmpty) {
+ optionsManager.getBuildFileName("log", "firrtl")
+ }
+ else {
+ optionsManager.getBuildFileName("log")
+ }
+ }
}
trait HasCommonOptions {
@@ -49,6 +66,61 @@ trait HasCommonOptions {
}
.text(s"This options defines a work directory for intermediate files, default is ${commonOptions.targetDirName}")
+ parser.opt[String]("log-level")
+ .abbr("ll").valueName("<Error|Warn|Info|Debug|Trace>")
+ .foreach { x =>
+ val level = x.toLowerCase match {
+ case "error" => LogLevel.Error
+ case "warn" => LogLevel.Warn
+ case "info" => LogLevel.Info
+ case "debug" => LogLevel.Debug
+ case "trace" => LogLevel.Trace
+ }
+ commonOptions = commonOptions.copy(globalLogLevel = level)
+ }
+ .validate { x =>
+ if (Array("error", "warn", "info", "debug", "trace").contains(x.toLowerCase)) parser.success
+ else parser.failure(s"$x bad value must be one of ignore|use|gen|append")
+ }
+ .text(s"This options defines a work directory for intermediate files, default is ${commonOptions.targetDirName}")
+
+ parser.opt[Seq[String]]("class-log-level")
+ .abbr("cll").valueName("<FullClassName:[Error|Warn|Info|Debug|Trace]>[,...]")
+ .foreach { x =>
+ val logAssignments = x.map { y =>
+ val className :: levelName :: _ = y.split(":").toList
+
+ val level = levelName.toLowerCase match {
+ case "error" => LogLevel.Error
+ case "warn" => LogLevel.Warn
+ case "info" => LogLevel.Info
+ case "debug" => LogLevel.Debug
+ case "trace" => LogLevel.Trace
+ case _ =>
+ throw new Exception(s"Error: bad command line arguments for --class-log-level $x")
+ }
+ className -> level
+ }
+
+ commonOptions = commonOptions.copy(classLogLevels = commonOptions.classLogLevels ++ logAssignments)
+
+ }
+ .text(s"This options defines a work directory for intermediate files, default is ${commonOptions.targetDirName}")
+
+ parser.opt[Unit]("log-to-file")
+ .abbr("ltf")
+ .foreach { _ =>
+ commonOptions = commonOptions.copy(logToFile = true)
+ }
+ .text(s"default logs to stdout, this flags writes to topName.log or firrtl.log if no topName")
+
+ parser.opt[Unit]("log-class-names")
+ .abbr("lcn")
+ .foreach { _ =>
+ commonOptions = commonOptions.copy(logClassNames = true)
+ }
+ .text(s"shows class names and log level in logging output, useful for target --class-log-level")
+
parser.help("help").text("prints this usage text")
}
diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala
index 53491922..33553179 100644
--- a/src/main/scala/firrtl/LoweringCompilers.scala
+++ b/src/main/scala/firrtl/LoweringCompilers.scala
@@ -27,11 +27,11 @@ MODIFICATIONS.
package firrtl
-import com.typesafe.scalalogging.LazyLogging
import java.io.Writer
import firrtl.passes.Pass
import firrtl.ir.Circuit
import Annotations._
+import logger.LazyLogging
// ===========================================
// Utility Traits
diff --git a/src/main/scala/logger/Logger.scala b/src/main/scala/logger/Logger.scala
new file mode 100644
index 00000000..07855c47
--- /dev/null
+++ b/src/main/scala/logger/Logger.scala
@@ -0,0 +1,101 @@
+// See LICENSE for license details.
+
+package logger
+
+import java.io.{PrintStream, File, FileOutputStream}
+
+import firrtl.ExecutionOptionsManager
+
+/**
+ * This provides a facility for a log4scala* type logging system. Why did we write our own? Because
+ * the canned ones are just to darned hard to turn on, particularly when embedded in a distribution.
+ * This one can be turned on programmatically or with the options exposed in the [[firrtl.CommonOptions]]
+ * and [[ExecutionOptionsManager]] API's in firrtl.
+ * There are 4 main options.
+ * * a simple global option to turn on all in scope (and across threads, might want to fix this)
+ * * turn on specific levels for specific fully qualified class names
+ * * set a file to write things to, default is just to use stdout
+ * * include the class names and level in the output. This is useful to figure out what
+ * the class names that extend LazyLogger are
+ *
+ * This is not overly optimized but does pass the string as () => String to avoid string interpolation
+ * occurring if the the logging level is not sufficiently high. This could be further optimized by playing
+ * with methods
+ */
+/**
+ * The supported log levels, what do they mean? Whatever you want them to.
+ */
+object LogLevel extends Enumeration {
+ val Error, Warn, Info, Debug, Trace = Value
+}
+
+/**
+ * extend this trait to enable logging in a class you are implementing
+ */
+trait LazyLogging {
+ val logger = new Logger(this.getClass.getName)
+}
+
+/**
+ * Singleton in control of what is supposed to get logged, how it's to be logged and where it is to be logged
+ */
+object Logger {
+ var globalLevel = LogLevel.Error
+ val classLevels = new scala.collection.mutable.HashMap[String, LogLevel.Value]
+ var logClassNames = false
+
+ def showMessage(level: LogLevel.Value, className: String, message: => String): Unit = {
+ if(globalLevel == level || (classLevels.nonEmpty && classLevels.getOrElse(className, LogLevel.Error) >= level)) {
+ if(logClassNames) {
+ stream.println(s"[$level:$className] $message")
+ }
+ else {
+ stream.println(message)
+ }
+ }
+ }
+
+ var stream: PrintStream = System.out
+
+ def setOutput(fileName: String): Unit = {
+ stream = new PrintStream(new FileOutputStream(new File(fileName)))
+ }
+ def setConsole(): Unit = {
+ stream = Console.out
+ }
+ def setClassLogLevels(namesToLevel: Map[String, LogLevel.Value]): Unit = {
+ classLevels ++= namesToLevel
+ }
+
+ def setOptions(optionsManager: ExecutionOptionsManager): Unit = {
+ val commonOptions = optionsManager.commonOptions
+ globalLevel = commonOptions.globalLogLevel
+ setClassLogLevels(commonOptions.classLogLevels)
+ if(commonOptions.logToFile) {
+ setOutput(commonOptions.getLogFileName(optionsManager))
+ }
+ logClassNames = commonOptions.logClassNames
+ }
+}
+
+/**
+ * Classes implementing [[LazyLogging]] will have logger of this type
+ * @param containerClass passed in from the LazyLogging trait in order to provide class level logging granularity
+ */
+class Logger(containerClass: String) {
+ def error(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Error, containerClass, message)
+ }
+ def warn(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Warn, containerClass, message)
+ }
+ def info(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Info, containerClass, message)
+ }
+ def debug(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Debug, containerClass, message)
+ }
+ def trace(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Trace, containerClass, message)
+ }
+}