diff options
Diffstat (limited to 'src/main/scala/firrtl/options')
| -rw-r--r-- | src/main/scala/firrtl/options/Phase.scala | 16 | ||||
| -rw-r--r-- | src/main/scala/firrtl/options/Registration.scala | 58 | ||||
| -rw-r--r-- | src/main/scala/firrtl/options/Shell.scala | 12 | ||||
| -rw-r--r-- | src/main/scala/firrtl/options/Stage.scala | 11 | ||||
| -rw-r--r-- | src/main/scala/firrtl/options/StageAnnotations.scala | 66 | ||||
| -rw-r--r-- | src/main/scala/firrtl/options/phases/DeletedWrapper.scala | 43 |
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) + +} |
