aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/firrtl/options
diff options
context:
space:
mode:
authorSchuyler Eldridge2019-04-22 21:20:08 -0400
committerSchuyler Eldridge2019-04-25 16:24:15 -0400
commitef8f06f23b9ee6cf86de2450752dfd0fcd32da80 (patch)
tree79e2e8c5753903ca6d14e9b952c26a07442bd980 /src/main/scala/firrtl/options
parent47fe781c4ace38dff7f31da7e78f772e131d689e (diff)
Add ShellOption, DeletedWrapper
Abstracts away option writing such that users no longer have to understand scopt semantics. This adds a ShellOption class and a HasShellOptions trait for something which provides one or more ShellOptions. This refactors the FIRRTL codebase to use this style of option specification. Adds and uses DeletedWrapper to automatically generate DeletedAnnotations. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Diffstat (limited to 'src/main/scala/firrtl/options')
-rw-r--r--src/main/scala/firrtl/options/Phase.scala16
-rw-r--r--src/main/scala/firrtl/options/Registration.scala58
-rw-r--r--src/main/scala/firrtl/options/Shell.scala12
-rw-r--r--src/main/scala/firrtl/options/Stage.scala11
-rw-r--r--src/main/scala/firrtl/options/StageAnnotations.scala66
-rw-r--r--src/main/scala/firrtl/options/phases/DeletedWrapper.scala43
6 files changed, 138 insertions, 68 deletions
diff --git a/src/main/scala/firrtl/options/Phase.scala b/src/main/scala/firrtl/options/Phase.scala
index a660d08a..34739053 100644
--- a/src/main/scala/firrtl/options/Phase.scala
+++ b/src/main/scala/firrtl/options/Phase.scala
@@ -39,22 +39,6 @@ abstract class Phase extends TransformLike[AnnotationSeq] {
*/
lazy val name: String = this.getClass.getName
- /** Perform the transform of [[transform]] on an [[firrtl.AnnotationSeq AnnotationSeq]] and add
- * [[firrtl.annotations.DeletedAnnotation DeletedAnnotation]]s for any deleted [[firrtl.annotations.Annotation
- * Annotation]]s.
- * @param a
- */
- final def runTransform(annotations: AnnotationSeq): AnnotationSeq = {
- val ax = transform(annotations)
-
- val (in, out) = (mutable.LinkedHashSet() ++ annotations, mutable.LinkedHashSet() ++ ax)
-
- (in -- out).map {
- case DeletedAnnotation(n, a) => DeletedAnnotation(s"$n+$name", a)
- case a => DeletedAnnotation(name, a)
- }.toSeq ++ ax
- }
-
}
/** A [[TransformLike]] that internally ''translates'' the input type to some other type, transforms the internal type,
diff --git a/src/main/scala/firrtl/options/Registration.scala b/src/main/scala/firrtl/options/Registration.scala
index a826ec50..c832ec7c 100644
--- a/src/main/scala/firrtl/options/Registration.scala
+++ b/src/main/scala/firrtl/options/Registration.scala
@@ -4,40 +4,70 @@ package firrtl.options
import firrtl.{AnnotationSeq, Transform}
-import scopt.OptionParser
+import scopt.{OptionDef, OptionParser, Read}
+
+/** Contains information about a [[Shell]] command line option
+ * @tparam the type of the command line argument
+ * @param longOption a long, double-dash option
+ * @param toAnnotationSeq a function to convert the type into an [[firrtl.AnnotationSeq AnnotationSeq]]
+ * @param helpText help text
+ * @param shortOption an optional single-dash option
+ * @param helpValueName a string to show as a placeholder argument in help text
+ */
+final class ShellOption[A: Read] (
+ val longOption: String,
+ val toAnnotationSeq: A => AnnotationSeq,
+ val helpText: String,
+ val shortOption: Option[String] = None,
+ val helpValueName: Option[String] = None
+) {
+
+ /** Add this specific shell (command line) option to an option parser
+ * @param p an option parser
+ */
+ final def addOption(p: OptionParser[AnnotationSeq]): Unit = {
+ val f = Seq(
+ (p: OptionDef[A, AnnotationSeq]) => p.action( (x, c) => toAnnotationSeq(x).reverse ++ c ),
+ (p: OptionDef[A, AnnotationSeq]) => p.text(helpText),
+ (p: OptionDef[A, AnnotationSeq]) => p.unbounded()) ++
+ shortOption.map( a => (p: OptionDef[A, AnnotationSeq]) => p.abbr(a) ) ++
+ helpValueName.map( a => (p: OptionDef[A, AnnotationSeq]) => p.valueName(a) )
+
+ f.foldLeft(p.opt[A](longOption))( (a, b) => b(a) )
+ }
+}
/** Indicates that this class/object includes options (but does not add these as a registered class)
*/
-trait HasScoptOptions {
+trait HasShellOptions {
- /** This method will be called to add options to an OptionParser ('''OPTIONS SHOULD BE PREPENDED''')
- *
- *
- * '''The ordering of [[firrtl.annotations.Annotation Annotation]] is important and has meaning for parallel
- * compilations. For deterministic behavior, you should always prepend any annotations to the [[firrtl.AnnotationSeq
- * AnnotationSeq]]. The [[firrtl.AnnotationSeq AnnotationSeq]] will be automatically reversed after a [[Stage]]
- * parses it.'''
- *
+ /** A sequence of options provided
+ */
+ def options: Seq[ShellOption[_]]
+
+ /** Add all shell (command line) options to an option parser
* @param p an option parser
*/
- def addOptions(p: OptionParser[AnnotationSeq]): Unit
+ final def addOptions(p: OptionParser[AnnotationSeq]): Unit = options.foreach(_.addOption(p))
+
}
-/** A [[Transform]] that includes options that should be exposed at the top level.
+/** A [[Transform]] that includes an option 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 => }
+trait RegisteredTransform extends HasShellOptions { 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 {
+trait RegisteredLibrary extends HasShellOptions {
/** 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
index dbc403a5..28c0554a 100644
--- a/src/main/scala/firrtl/options/Shell.scala
+++ b/src/main/scala/firrtl/options/Shell.scala
@@ -16,10 +16,10 @@ import java.util.ServiceLoader
class Shell(val applicationName: String) {
/** Command line argument parser (OptionParser) with modifications */
- final val parser = new OptionParser[AnnotationSeq](applicationName) with DuplicateHandling with ExceptOnError
+ protected val parser = new OptionParser[AnnotationSeq](applicationName) with DuplicateHandling with ExceptOnError
/** Contains all discovered [[RegisteredLibrary]] */
- lazy val registeredLibraries: Seq[RegisteredLibrary] = {
+ final lazy val registeredLibraries: Seq[RegisteredLibrary] = {
val libraries = scala.collection.mutable.ArrayBuffer[RegisteredLibrary]()
val iter = ServiceLoader.load(classOf[RegisteredLibrary]).iterator()
while (iter.hasNext) {
@@ -33,7 +33,7 @@ class Shell(val applicationName: String) {
}
/** Contains all discovered [[RegisteredTransform]] */
- lazy val registeredTransforms: Seq[RegisteredTransform] = {
+ final lazy val registeredTransforms: Seq[RegisteredTransform] = {
val transforms = scala.collection.mutable.ArrayBuffer[RegisteredTransform]()
val iter = ServiceLoader.load(classOf[RegisteredTransform]).iterator()
if (iter.hasNext) { parser.note("FIRRTL Transform Options") }
@@ -61,11 +61,11 @@ class Shell(val applicationName: String) {
}
parser.note("Shell Options")
+ ProgramArgsAnnotation.addOptions(parser)
Seq( TargetDirAnnotation,
- ProgramArgsAnnotation,
InputAnnotationFileAnnotation,
OutputAnnotationFileAnnotation )
- .map(_.addOptions(parser))
+ .foreach(_.addOptions(parser))
parser.opt[Unit]("show-registrations")
.action{ (_, c) =>
@@ -85,5 +85,5 @@ class Shell(val applicationName: String) {
ClassLogLevelAnnotation,
LogFileAnnotation,
LogClassNamesAnnotation )
- .map(_.addOptions(parser))
+ .foreach(_.addOptions(parser))
}
diff --git a/src/main/scala/firrtl/options/Stage.scala b/src/main/scala/firrtl/options/Stage.scala
index 38cc9d42..f2780761 100644
--- a/src/main/scala/firrtl/options/Stage.scala
+++ b/src/main/scala/firrtl/options/Stage.scala
@@ -31,16 +31,19 @@ abstract class Stage extends Phase {
* @throws OptionsException if command line or annotation validation fails
*/
final def transform(annotations: AnnotationSeq): AnnotationSeq = {
- val annotationsx = Seq( new phases.GetIncludes,
- new phases.ConvertLegacyAnnotations )
- .foldLeft(annotations)((a, p) => p.runTransform(a))
+ val annotationsx =
+ Seq( new phases.GetIncludes,
+ new phases.ConvertLegacyAnnotations )
+ .map(phases.DeletedWrapper(_))
+ .foldLeft(annotations)((a, p) => p.transform(a))
Logger.makeScope(annotationsx) {
Seq( new phases.AddDefaults,
new phases.Checks,
new Phase { def transform(a: AnnotationSeq) = run(a) },
new phases.WriteOutputAnnotations )
- .foldLeft(annotationsx)((a, p) => p.runTransform(a))
+ .map(phases.DeletedWrapper(_))
+ .foldLeft(annotationsx)((a, p) => p.transform(a))
}
}
diff --git a/src/main/scala/firrtl/options/StageAnnotations.scala b/src/main/scala/firrtl/options/StageAnnotations.scala
index e8a1a288..e35a6afa 100644
--- a/src/main/scala/firrtl/options/StageAnnotations.scala
+++ b/src/main/scala/firrtl/options/StageAnnotations.scala
@@ -21,13 +21,16 @@ trait Unserializable { this: Annotation => }
*/
case class TargetDirAnnotation(directory: String = ".") extends NoTargetAnnotation with StageOption
-object TargetDirAnnotation extends HasScoptOptions {
- def addOptions(p: OptionParser[AnnotationSeq]): Unit = p.opt[String]("target-dir")
- .abbr("td")
- .valueName("<target-directory>")
- .action( (x, c) => TargetDirAnnotation(x) +: c )
- .unbounded() // See [Note 1]
- .text(s"Work directory for intermediate files/blackboxes, default is '.' (current directory)")
+object TargetDirAnnotation extends HasShellOptions {
+
+ val options = Seq(
+ new ShellOption[String](
+ longOption = "target-dir",
+ toAnnotationSeq = (a: String) => Seq(TargetDirAnnotation(a)),
+ helpText = "Work directory (default: '.')",
+ shortOption = Some("td"),
+ helpValueName = Some("<directory>") ) )
+
}
/** Additional arguments
@@ -36,7 +39,8 @@ object TargetDirAnnotation extends HasScoptOptions {
*/
case class ProgramArgsAnnotation(arg: String) extends NoTargetAnnotation with StageOption
-object ProgramArgsAnnotation extends HasScoptOptions {
+object ProgramArgsAnnotation {
+
def addOptions(p: OptionParser[AnnotationSeq]): Unit = p.arg[String]("<arg>...")
.unbounded()
.optional()
@@ -51,13 +55,16 @@ object ProgramArgsAnnotation extends HasScoptOptions {
*/
case class InputAnnotationFileAnnotation(file: String) extends NoTargetAnnotation with StageOption
-object InputAnnotationFileAnnotation extends HasScoptOptions {
- def addOptions(p: OptionParser[AnnotationSeq]): Unit = p.opt[String]("annotation-file")
- .abbr("faf")
- .unbounded()
- .valueName("<input-anno-file>")
- .action( (x, c) => InputAnnotationFileAnnotation(x) +: c )
- .text("Used to specify annotation file")
+object InputAnnotationFileAnnotation extends HasShellOptions {
+
+ val options = Seq(
+ new ShellOption[String](
+ longOption = "annotation-file",
+ toAnnotationSeq = (a: String) => Seq(InputAnnotationFileAnnotation(a)),
+ helpText = "An input annotation file",
+ shortOption = Some("faf"),
+ helpValueName = Some("<file>") ) )
+
}
/** An explicit output _annotation_ file to write to
@@ -66,13 +73,16 @@ object InputAnnotationFileAnnotation extends HasScoptOptions {
*/
case class OutputAnnotationFileAnnotation(file: String) extends NoTargetAnnotation with StageOption
-object OutputAnnotationFileAnnotation extends HasScoptOptions {
- def addOptions(p: OptionParser[AnnotationSeq]): Unit = p.opt[String]("output-annotation-file")
- .abbr("foaf")
- .valueName ("<output-anno-file>")
- .action( (x, c) => OutputAnnotationFileAnnotation(x) +: c )
- .unbounded()
- .text("use this to set the annotation output file")
+object OutputAnnotationFileAnnotation extends HasShellOptions {
+
+ val options = Seq(
+ new ShellOption[String](
+ longOption = "output-annotation-file",
+ toAnnotationSeq = (a: String) => Seq(OutputAnnotationFileAnnotation(a)),
+ helpText = "An output annotation file",
+ shortOption = Some("foaf"),
+ helpValueName = Some("<file>") ) )
+
}
/** If this [[firrtl.annotations.Annotation Annotation]] exists in an [[firrtl.AnnotationSeq AnnotationSeq]], then a
@@ -80,12 +90,12 @@ object OutputAnnotationFileAnnotation extends HasScoptOptions {
* [[firrtl.annotations.DeletedAnnotation DeletedAnnotation]]s when it writes to a file.
* - set with '--write-deleted'
*/
-case object WriteDeletedAnnotation extends NoTargetAnnotation with StageOption with HasScoptOptions {
+case object WriteDeletedAnnotation extends NoTargetAnnotation with StageOption with HasShellOptions {
- def addOptions(p: OptionParser[AnnotationSeq]): Unit = p
- .opt[Unit]("write-deleted")
- .unbounded()
- .action( (_, c) => WriteDeletedAnnotation +: c )
- .text("Include deleted annotations in the output annotation file")
+ val options = Seq(
+ new ShellOption[Unit](
+ longOption = "write-deleted",
+ toAnnotationSeq = (_: Unit) => Seq(WriteDeletedAnnotation),
+ helpText = "Include deleted annotations in the output annotation file" ) )
}
diff --git a/src/main/scala/firrtl/options/phases/DeletedWrapper.scala b/src/main/scala/firrtl/options/phases/DeletedWrapper.scala
new file mode 100644
index 00000000..0a959f32
--- /dev/null
+++ b/src/main/scala/firrtl/options/phases/DeletedWrapper.scala
@@ -0,0 +1,43 @@
+// See LICENSE for license details.
+
+package firrtl.options.phases
+
+import firrtl.AnnotationSeq
+import firrtl.annotations.DeletedAnnotation
+import firrtl.options.{Phase, Translator}
+
+import scala.collection.mutable
+
+/** Wrap a [[firrtl.options.Phase Phase]] such that any [[firrtl.annotations.Annotation Annotation]] removed by the
+ * wrapped [[firrtl.options.Phase Phase]] will be added as [[firrtl.annotations.DeletedAnnotation DeletedAnnotation]]s.
+ * @param p a [[firrtl.options.Phase Phase]] to wrap
+ */
+class DeletedWrapper(p: Phase) extends Phase with Translator[AnnotationSeq, (AnnotationSeq, AnnotationSeq)] {
+
+ override lazy val name: String = p.name
+
+ def aToB(a: AnnotationSeq): (AnnotationSeq, AnnotationSeq) = (a, a)
+
+ def bToA(b: (AnnotationSeq, AnnotationSeq)): AnnotationSeq = {
+
+ val (in, out) = (mutable.LinkedHashSet() ++ b._1, mutable.LinkedHashSet() ++ b._2)
+
+ (in -- out).map {
+ case DeletedAnnotation(n, a) => DeletedAnnotation(s"$n+$name", a)
+ case a => DeletedAnnotation(name, a)
+ }.toSeq ++ b._2
+
+ }
+
+ def internalTransform(b: (AnnotationSeq, AnnotationSeq)): (AnnotationSeq, AnnotationSeq) = (b._1, p.transform(b._2))
+
+}
+
+object DeletedWrapper {
+
+ /** Wrap a [[firrtl.options.Phase Phase]] in a [[DeletedWrapper]]
+ * @param p a [[firrtl.options.Phase Phase]] to wrap
+ */
+ def apply(p: Phase): DeletedWrapper = new DeletedWrapper(p)
+
+}