aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorSchuyler Eldridge2018-11-07 13:48:06 -0500
committerGitHub2018-11-07 13:48:06 -0500
commit17b4e9835bd95dcf91c5ea5a4d7c52280031ea93 (patch)
treee54301b8019d17cce4448ce9d09589815a7315d5 /src/main
parentd04af59c233cec994087df3d0d3fff14e20ac04c (diff)
parent27c1b366ce58e93434e77e964365474f5e7aa8d7 (diff)
Merge pull request #879 from seldridge/issue-764-refactor-pr-pointer-optionsPackage
- Adds firrtl.options package and tests - Does not use firrtl.options in any way
Diffstat (limited to 'src/main')
-rw-r--r--src/main/scala/firrtl/options/OptionParser.scala39
-rw-r--r--src/main/scala/firrtl/options/OptionsView.scala27
-rw-r--r--src/main/scala/firrtl/options/Registration.scala36
-rw-r--r--src/main/scala/firrtl/options/Shell.scala63
-rw-r--r--src/main/scala/firrtl/options/Stage.scala61
5 files changed, 226 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/options/OptionParser.scala b/src/main/scala/firrtl/options/OptionParser.scala
new file mode 100644
index 00000000..6d8095c0
--- /dev/null
+++ b/src/main/scala/firrtl/options/OptionParser.scala
@@ -0,0 +1,39 @@
+// See LICENSE for license details.
+
+package firrtl.options
+
+import firrtl.{FIRRTLException, AnnotationSeq}
+
+import scopt.OptionParser
+
+/** Causes an OptionParser to not call exit (call `sys.exit`) if the `--help` option is passed
+ */
+trait DoNotTerminateOnExit { this: OptionParser[_] =>
+ override def terminate(exitState: Either[String, Unit]): Unit = Unit
+}
+
+/** A modified OptionParser with mutable termination and additional checks
+ */
+trait DuplicateHandling extends OptionParser[AnnotationSeq] {
+
+ override def parse(args: Seq[String], init: AnnotationSeq): Option[AnnotationSeq] = {
+
+ /** Message for found duplicate options */
+ def msg(x: String, y: String) = s"""Duplicate $x "$y" (did your custom Transform or OptionsManager add this?)"""
+
+ val longDups = options.map(_.name).groupBy(identity).collect{ case (k, v) if v.size > 1 && k != "" => k }
+ val shortDups = options.map(_.shortOpt).flatten.groupBy(identity).collect{ case (k, v) if v.size > 1 => k }
+
+
+ if (longDups.nonEmpty) {
+ throw new OptionsException(msg("long option", longDups.map("--" + _).mkString(",")), new IllegalArgumentException)
+ }
+
+ if (shortDups.nonEmpty) {
+ throw new OptionsException(msg("short option", shortDups.map("-" + _).mkString(",")), new IllegalArgumentException)
+ }
+
+ super.parse(args, init)
+ }
+
+}
diff --git a/src/main/scala/firrtl/options/OptionsView.scala b/src/main/scala/firrtl/options/OptionsView.scala
new file mode 100644
index 00000000..dade5c56
--- /dev/null
+++ b/src/main/scala/firrtl/options/OptionsView.scala
@@ -0,0 +1,27 @@
+// See LICENSE for license details.
+
+package firrtl.options
+
+import firrtl.AnnotationSeq
+
+/** Type class defining a "view" of an [[AnnotationSeq]]
+ * @tparam T the type to which this viewer converts an [[AnnotationSeq]] to
+ */
+trait OptionsView[T] {
+
+ /** Convert an [[AnnotationSeq]] to some other type
+ * @param options some annotations
+ */
+ def view(options: AnnotationSeq): Option[T]
+}
+
+/** A shim to manage multiple "views" of an [[AnnotationSeq]] */
+object Viewer {
+
+ /** Convert annotations to options using an implicitly provided [[OptionsView]]
+ * @param options some annotations
+ * @param optionsView a converter of options to the requested type
+ * @tparam T the type to which the input [[AnnotationSeq]] should be viewed as
+ */
+ def view[T](options: AnnotationSeq)(implicit optionsView: OptionsView[T]): Option[T] = optionsView.view(options)
+}
diff --git a/src/main/scala/firrtl/options/Registration.scala b/src/main/scala/firrtl/options/Registration.scala
new file mode 100644
index 00000000..481c095b
--- /dev/null
+++ b/src/main/scala/firrtl/options/Registration.scala
@@ -0,0 +1,36 @@
+// See LICENSE for license details.
+
+package firrtl.options
+
+import firrtl.{AnnotationSeq, Transform}
+
+import scopt.OptionParser
+
+/** Indicates that this class/object includes options (but does not add these as a registered class)
+ */
+trait HasScoptOptions {
+
+ /** This method will be called to add options to an OptionParser
+ * @param p an option parser
+ */
+ def addOptions(p: OptionParser[AnnotationSeq]): Unit
+}
+
+/** A [[Transform]] that includes options that should be exposed at the top level.
+ *
+ * @note To complete registration, include an entry in
+ * src/main/resources/META-INF/services/firrtl.options.RegisteredTransform */
+trait RegisteredTransform extends HasScoptOptions { this: Transform => }
+
+/** A class that includes options that should be exposed as a group at the top level.
+ *
+ * @note To complete registration, include an entry in
+ * src/main/resources/META-INF/services/firrtl.options.RegisteredLibrary */
+trait RegisteredLibrary extends HasScoptOptions {
+
+ /** The name of this library.
+ *
+ * This will be used when generating help text.
+ */
+ def name: String
+}
diff --git a/src/main/scala/firrtl/options/Shell.scala b/src/main/scala/firrtl/options/Shell.scala
new file mode 100644
index 00000000..b9278f30
--- /dev/null
+++ b/src/main/scala/firrtl/options/Shell.scala
@@ -0,0 +1,63 @@
+// See LICENSE for license details.
+
+package firrtl.options
+
+import firrtl.AnnotationSeq
+
+import scopt.OptionParser
+
+import java.util.ServiceLoader
+
+/** Indicate an error in [[firrtl.options]]
+ * @param msg a message to print
+ */
+case class OptionsException(msg: String, cause: Throwable = null) extends Exception(msg, cause)
+
+/** A utility for working with command line options
+ * @param applicationName the application associated with these command line options
+ */
+class Shell(val applicationName: String) {
+
+ /** Command line argument parser (OptionParser) with modifications */
+ final val parser = new OptionParser[AnnotationSeq](applicationName) with DoNotTerminateOnExit with DuplicateHandling
+
+ /** Contains all discovered [[RegisteredLibrary]] */
+ lazy val registeredLibraries: Seq[RegisteredLibrary] = {
+ val libraries = scala.collection.mutable.ArrayBuffer[RegisteredLibrary]()
+ val iter = ServiceLoader.load(classOf[RegisteredLibrary]).iterator()
+ while (iter.hasNext) {
+ val lib = iter.next()
+ libraries += lib
+ parser.note(lib.name)
+ lib.addOptions(parser)
+ }
+ libraries
+ }
+
+ /** Contains all discovered [[RegisteredTransform]] */
+ lazy val registeredTransforms: Seq[RegisteredTransform] = {
+ val transforms = scala.collection.mutable.ArrayBuffer[RegisteredTransform]()
+ val iter = ServiceLoader.load(classOf[RegisteredTransform]).iterator()
+ parser.note("FIRRTL Transform Options")
+ while (iter.hasNext) {
+ val tx = iter.next()
+ transforms += tx
+ tx.addOptions(parser)
+ }
+ transforms
+ }
+
+ /** The [[AnnotationSeq]] generated from command line arguments
+ *
+ * This requires lazy evaluation as subclasses will mixin new command
+ * line options via methods of [[Shell.parser]]
+ */
+ def parse(args: Array[String], initAnnos: AnnotationSeq = Seq.empty): AnnotationSeq = {
+ registeredTransforms
+ registeredLibraries
+ parser
+ .parse(args, initAnnos)
+ .getOrElse(throw new OptionsException("Failed to parse command line options", new IllegalArgumentException))
+ }
+
+}
diff --git a/src/main/scala/firrtl/options/Stage.scala b/src/main/scala/firrtl/options/Stage.scala
new file mode 100644
index 00000000..cc651943
--- /dev/null
+++ b/src/main/scala/firrtl/options/Stage.scala
@@ -0,0 +1,61 @@
+// See LICENSE for license details.
+
+package firrtl.options
+
+import firrtl.AnnotationSeq
+
+/** Utilities mixed into something that looks like a [[Stage]] */
+object StageUtils {
+ /** Print a warning message (in yellow)
+ * @param message error message
+ */
+ //scalastyle:off regex
+ def dramaticWarning(message: String): Unit = {
+ println(Console.YELLOW + "-"*78)
+ println(s"Warning: $message")
+ println("-"*78 + Console.RESET)
+ }
+
+ /** Print an error message (in red)
+ * @param message error message
+ * @note This does not stop the Driver.
+ */
+ //scalastyle:off regex
+ def dramaticError(message: String): Unit = {
+ println(Console.RED + "-"*78)
+ println(s"Error: $message")
+ println("-"*78 + Console.RESET)
+ }
+}
+
+/** A [[Stage]] represents one stage in the FIRRTL hardware compiler framework
+ *
+ * The FIRRTL compiler is a stage as well as any frontend or backend that runs before/after FIRRTL. Concretely, Chisel
+ * is a [[Stage]] as is FIRRTL's Verilog emitter. Each stage performs a mathematical transformation on an
+ * [[AnnotationSeq]] where some input annotations are processed to produce different annotations. Command line options
+ * may be pulled in if available.
+ */
+abstract class Stage {
+
+ /** A utility that helps convert command line options to annotations */
+ val shell: Shell
+
+ /** Run this [[Stage]] on some input annotations
+ * @param annotations input annotations
+ * @return output annotations
+ */
+ def execute(annotations: AnnotationSeq): AnnotationSeq
+
+ /** Run this [[Stage]] on on a mix of arguments and annotations
+ * @param args command line arguments
+ * @param initialAnnotations annotation
+ * @return output annotations
+ */
+ def execute(args: Array[String], annotations: AnnotationSeq): AnnotationSeq =
+ execute(shell.parse(args, annotations))
+
+ /** The main function that serves as this [[Stage]]'s command line interface
+ * @param args command line arguments
+ */
+ def main(args: Array[String]): Unit = execute(args, Seq.empty)
+}