-
Notifications
You must be signed in to change notification settings - Fork 176
Annotations as Options Replacement, Immutable Option Parsing #765
Conversation
96c9617
to
02ec860
Compare
We should also think about possible command-line option collisions that could occur if two custom transforms (likely in different projects), try to claim the same option. I'm sure scopt errors when this occurs, but I wonder if there's some way we can prevent this from happening. Would it be possible to enforce that custom transforms have a prefix on their option or something? This might be really hard or onerous to do, but maybe it's possible! |
Good thought, @jackkoenig. While I think that this will be unlikely for some time, it's a huge issue... Surprisingly, scopt does not error for this... All that appears to be happening is the last callback is the one that wins. This, unfortunately, appears to happen entirely silently... I think that the only way to utterly disambiguate things is to use the full class path. That seems reasonable to me, even if it's verbose. It's just a question of whether this should be enforced and how it should be enforced. |
Scala 2.12.4 seems to be bumping up against Travis memory limits. The closest that I've gotten to replicating is that I can locally run myself out of metaspace if I repeatedly run |
9caba74
to
c4e9340
Compare
Did you remove the class-finder stuff due to 2.12.4 issues with Travis memory limits? |
@jackkoenig: No, not motivated by that (should have clarified in the commit). After discussing this last week, the consensus was that this was not the right approach for the following two reasons:
Summarily, everyone thought that there should exist a cleaner solution and that this PR should be separated from that. I'm working to get this more towards only dealing with refactoring the options manager to provide:
|
@seldridge Ah, sorry I missed the meeting--had a big paper push going on. I'm totally down with automatically finding such transforms to a separate PR since this it is a fairly separate piece of work. It also relates to potentially improving annotation deserialization and some way of automatically instantiating transforms based on the presence of certain annotations (ie. moving that functionality from Chisel to FIRRTL). Just for my own understanding, how slow is it? I wanted to use ClassFinder for finding classes that extend Annotation, but if it's really that slow then I guess it won't work. As you alluded to above, maybe it will be fast enough if we can be judicious in which packages we search (wg. explicitly exclude sbt, scala, and specific plugins)... |
It's actually not that bad, but it was an annoying shock from not using it. It takes about 2 seconds to do the class path search with Now that I think about it, this may not be all that bad from a usability perspective. It's basically the same as |
I see. Well we can take a look at a later time! This PR looks to be shaping up well! |
6a40f9d
to
e8d70e4
Compare
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
parser.opt[Unit]("show-registrations") | ||
.hidden() | ||
.action{ (_, c) => | ||
println(s"""|Registered Libraries: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
println or should this be logger.info? logger.warn?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is safe to leave in as it is. I could wrap this in a logger, but this is really information that I want to get to the command line for debugging before a logger may exist and I want this to happen even if a logger doesn't exist or I have the incorrect log level.
I.e., I think I can wrap the scopt parse
call with a logger, but I'd want to pass in an explicit log level which would seem to violate the motivation for having a logger at all.
This adds support for libraries (a class internal to FIRRTL or external in some project or library dependency) or transforms to register via java.util.ServiceLoader that they contain options that should be included on the command line. During the generation of an AnnotationSeq, these will be parsed. To facilitate debugging, a hidden option, --show-registrations, is added to the parent ExecutionOptionsManager that will verbosely list all registered libraries and transforms that it was able to find. This should be everything necessary for chipsalliance#552, chipsalliance#558. h/t @ucbjrl for finding the ServiceLoader approach and @chick for implementing a concrete example using ServiceLoader Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
This changes an ExecutionOptionsManager to not contain toolOptions (e.g., FirrtlExecutionOptionsManager no longer contains firrtlOptions). An ExecutionOptionsManager is now a container of option parsing that converts command line options to an AnnotationSeq. Conversion to toolOptions exists in the Driver of a specific tool. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
This uses an explicit AnnotationSeq as opposed to an implicit one when performing a view[T]. This provides greater flexibility for passing the annotations as an argument. This removes a kludge necessary to pass annotations implicitly for the private Driver.execute method. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
This creates an abstract class representing a Big5 Driver and uses it as the parent class for FIRRTL's Driver. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
This changes the API of RunFirrtlTransformAnnotation to use an actual Transform class as opposed to a stringly-typed Transform class name. This aligns with (and subsequently replaces) Chisel's RunFirrtlTransform. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Using default arguments limits the ability of a user extending options.Driver.execute to provide alternative execute methods with default arguments. This avoids that by using an execute method without defaults. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
This converts FirrtlExecutionOptions method signatures to use AnnotationSeq instead of Seq[Annotation]. The former is more canonical and gives us some control over the underlying implementation. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
This aligns the behavior of BlackBoxTargetDirAnnotation to match TargetDirAnnotation for both command line options and annotations. The correct behavior is supposed to be that the default value of BlackBoxTargetDirAnno is the value of TargetDirAnnotation. This would previously be the case for command line options, but not for annotations. In the latter case, the BlackBoxTargetDirAnno would get the default TargetDirAnnotation string, ".". Now, the option parsing for TargetDirAnnotation ("--target-dir/-td") generates _only_ a TargetDirAnnotation. The default value is now fully handled when constructing a FirrtlExecutionOptions view. This adds one test case that ensures that the correct BlackBoxTargetDirAnnotation is generated. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
/* Check that no long or short option declarations are duplicated */ | ||
override def parse(args: Seq[String], init: AnnotationSeq): Option[AnnotationSeq] = { | ||
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 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be responsible for:
An exception or error caused a run to abort: scopt.OptionDef.shortOpt()Lscala/Option;
java.lang.NoSuchMethodError: scopt.OptionDef.shortOpt()Lscala/Option;
at firrtl.options.DuplicateHandling$$anonfun$5.apply(OptionParser.scala:22)
at firrtl.options.DuplicateHandling$$anonfun$5.apply(OptionParser.scala:22)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
at scala.collection.immutable.List.foreach(List.scala:392)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:45)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
at scala.collection.AbstractTraversable.map(Traversable.scala:104)
at firrtl.options.DuplicateHandling$class.parse(OptionParser.scala:22)
at firrtl.options.ExecutionOptionsManager$$anon$1.parse(ExecutionOptionsManager.scala:20)
at firrtl.options.ExecutionOptionsManager.parse(ExecutionOptionsManager.scala:68)
at firrtl_interpreter.Driver$.execute(Driver.scala:33)
at firrtl_interpreter.DriverSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply$mcV$sp(DriverSpec.scala:21)
at firrtl_interpreter.DriverSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply(DriverSpec.scala:10)
at firrtl_interpreter.DriverSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply(DriverSpec.scala:10)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
False alarm. This was due to IntelliJ using an old version of scopt for the firrtl-interpreter build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly a stale scopt version? That method, shortOpt
, showed up in 3.7.0.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
2c2ad06
to
a709b21
Compare
case a => a | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chick: Here's what I came up with for using Annotation
s for internal tool data movement of things which we can't serialize with json4s (currently). The idea here is that you mix this into an Annotation
and before this is serialized to JSON, it will be converted to an annotation that is serializable. This is damn ugly as I wasn't able to think of a way to do this with type checking. Maybe there's a way with proper variances, but nothing was jumping out at me. This should enable something (which I'll add to Chisel) of a ChiselCircuitGeneratorAnnotation(dut: () => RawModule)
that will serialize to a ChiselCircuitAnnotation(circuit: Circuit)
but can be used for Chisel testers.
@jackkoenig: This is to address a narrow problem of passing annotations into the Driver
of Chisel testers. It needs a () => MultiIOModule
to pass to a PeekPokeTester
and this is information that can't be contained in a serializable annotation without completely rewriting how Chisel testers works. Can you think of any way to add type safety here?
This adds a new trait that indicates a particular annotation is incapable of being serialized into JSON. This includes an abstract, idempotent method to convert this to another annotation that is safe for serialization (indicated as it not subclassing Unserializable). This includes a round trip serialization test. The intended (very limited) use of this is to pass around metadata that cannot be automatically serialized by json4s (though we could, eventually serialize it with custom annotations). The motivating use case is providing an annotation containing a dut generator anonymous function, i.e., `() => Module`. There may be a better way to do this with appropriate variances. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
Ping @jackkoenig, @chick: I have a separate branch with this PR rebased and refactored massively. I could PR this as either sequential commits or about 9 PRs (possibly more...). Note that only one of those 9 PRs would have an actual functional impact on FIRRTL (the one that migrates to the "options are annotations" pattern). Everything else is either small patches or adding the framework to enable "options are annotations". I haven't tested that every commit builds, but they are supposed to (or that's the intention). I'll spoon feed them to Travis to get those ✔️s. Would it be a good idea to close this and reopen? Any thoughts on separate PRs or one PR with a chain of sequential, reasoned patches? See: master...seldridge:issue-764-refactor FWIW, if anyone else is trying to do this type of rebasing, instead of fighting with rebasing a dumpster 🔥 of 77 commits with endless merge conflicts, I had luck with applying the equivalent patch onto a new branch and then repeatedly rebasing to break out logical items. |
This makes sense to me. I'm opting for the series of PRs approach. I'll re-open if people feel differently. |
I think the series of PRs sounds great, thanks! |
Overview
Annotations and command line options should be fungible. A user calling the FIRRTL compiler should be able to interact with the compiler in the same way that an earlier frontend that produces annotations, but does not interact with a
Driver
(and vice versa). This does the following high level refactors:AnnotationSeq
The latter change enables immutable option parsing to be split across disjoint traits while using the same parser (e.g.,
HasFirrtlOptions
,HasChiselExecutionOptions
both parse using a sharedOptionParser[AnnotationSeq]
). The drivers or transforms that consume command line options are then expected to extract what they need from the command-line createdAnnotationSeq
, deleting what is specific to them. This then stages for a logical refactor ofDriver
subclassingTransform
.API changes
setTopName
)ExecutionOptionsManager
can only be constructed using(String, Array[String])
FirrtlOptions
) useOption[T]
to store optional data, as opposed to empty stringsCommonOptions
no longer exists and has been rolled intoFirrtlExecutionOptions
API Deprecations
FirrtlExecutionOptions.{getInputFileName, getOutputConfig, getTargetFile}
(moved toHasFirrtlOptions
)API additions
InputAnnotationFileAnnotation
with automatic include guard checkingDriver.execute(args: String)
HasFirrtlOptions.{getInputFileName, getOutputConfig, getTargetFile}
Miscellaneous changes
HasFirrtlOptions
from ExecutionOptionsManager.scala into FirrtlExecutionOptions.scalacommonOptions
,firrtlOptions
) are necessarily lazily evaluatedIssues
Todo
[skip chisel tests]
(I'm not sure this can be merged without this...)[ ] All FIRRTL options should be deleted after use(I'm opting to leave this in for now---it's unclear to me if other transforms need these)ExecutionOptionsManager with X with Y with Z
as a parameter[ ] SealAnnotation
s that should not be emitted byTransform
s, e.g.,RunFirrtlTransform
RunFirrtlTransformAnnotation
as Chisel'sDriver
uses this. Similar logic applies for "Driver-only" annotations, likeTopNameAnnotation
. A transform could reasonably change this. There's a lot of power here, but I think the better approach would be to roll this into adding checks for weirdness at the form levels.