Oh right, I'm sold to the plan. On Wed, Aug 29, 2012 at 2:04 AM, Adam Murdoch <[email protected]>wrote:
> > On 29/08/2012, at 6:58 AM, Luke Daley wrote: > > > > > > On 28/08/2012, at 9:23 PM, Adam Murdoch <[email protected]> > wrote: > > > On 29/08/2012, at 12:02 AM, Luke Daley wrote: > > > > On 28/08/2012, at 3:20 AM, Adam Murdoch wrote: > > > Hi, > > > One thing that the new build comparison/migration stuff needs to do is run > a Gradle build and then assemble a description of what the build produced, > in order to compare it. > > > We're currently using the tooling API for this, but it's a little awkward. > First we run the build using a BuildLauncher. Then we ask for the > ProjectOutcomes model. One problem with this is that it's not entirely > accurate, as the model builder can only guess what the build would have > done. Another problem is that it's potentially quite slow, as we have to > configure the build model twice. > > > Both these problems would be addressed if we had some way to run the build > and assemble the model in one operation. We have a few options about how we > might model this. > > > Here are some use cases we want to aim for (for the following, 'build > model' means the version-specific Gradle model): > > > * Request the Eclipse model: configure the build model, apply the eclipse > plugin, assemble the Eclipse model. > > * Request the project tasks model: configure the build model, assemble the > project tasks model. > > * Run a build from the IDE: run the selected tasks, assemble a build > result model (successful/failed). > > * Run a build for comparison: run the selected tasks, assemble the > outcomes model from the DAG (outcomes model extends build result model > above). > > * Run a build from an integration test: inject classpath from test > process, run the selected tasks, assemble a test build result model. > > * Configure a build from an integration test: inject classpath from test > process, configure the model, make some assertions, assemble a test build > result model. > > * Newer consumer fills out missing pieces of model provided by older > provider: inject classpath from consumer process, invoke client provided > action around the existing behaviour, client action decorates the result. > > * Create a new Gradle project from the IDE: configure the build model, > apply the build-initialize plugin, run some tasks, assemble a build result > model. > > * Tooling API client builds its own model: inject classpath from client > process, invoke a client provided action, serialise result back. This > allows, for example, an IDE to opt in to being able to ask any question of > the Gradle model, but in a version specific way. > > > What we want to sort out for the 1.2 release is the minimum set of > consumer <-> provider protocol changes we can make, to later allow us to > evolve towards supporting these use cases. Clearly, we don't want all this > stuff for the 1.2 release. > > > Something else to consider is how notifications might be delivered to the > client. Here are some use cases: > > > * IDE is notified when a change to the Eclipse model is made (either by a > local change or a change in the set of available dependencies). > > * IDE is notified when an updated version of a dependency is available. > > * For the Gradle 'keep up-to-date' use case, the client is notified when a > change to the inputs of the target output is made. > > > Another thing to consider here is support for end-of-life for various > (consumer, producer) combinations. > > > There's a lot of stuff here. I think it pretty much comes down to a single > operation on the consumer <-> provider connection: build request comes in, > and build result comes out. > > > The build request would specify (most of this stuff is optional): > > - Client provided logging settings: log level, stdin/stdout/stderr and > progress listener, etc. > > - Build environment: Java home, JVM args, daemon configuration, Gradle > user home, etc. > > - Build parameters: project dir, command-line args, etc. > > - A set of tasks to run. Need to distinguish between 'don't run any > tasks', 'run the default tasks', and 'run these tasks'. > > - A client provided action to run. This is probably a classpath, and a > serialised action of some kind. Doesn't matter exactly what. > > - A listener to be notified when the requested model changes. > > > The build result would return: > > - The failures, if any (the failure might be 'this request is no longer > supported'). > > - The model of type T. > > - whether the request is deprecated, and why. > > - Perhaps some additional diagnostics. > > > So, given that we only want a subset of the above for 1.2, we need to come > up with a strategy for evolving. The current strategy is probably > sufficient. We currently have something like this: > > > <T> T getTheModel(Class<T> type, BuildOperationParametersVersion1 > operationParameters); > > > The provider dynamically inspects the operationParameters instance. So, > for example, if it has a getStandardOutput() method, then the provider > assumes that it should use this to get the OutputStream to write the > build's standard output to. > > > This means that an old provider will ignore the build request features > that it does not understand. To deal with this, the consumer queries the > provider version, and uses this to decide whether the provider supports a > given feature (the consumer knows which version a given feature was added). > > > To implement this, I think we want to add a new interface, detangled from > the old interfaces, perhaps something like: > > > interface ProviderConnection extends InternalProtocolInterface { > > <T> BuildResult<T> build(Class<T> type, BuildRequest request) > > } > > > On the provider side, DefaultConnection would implement both the old and > new interface. On the consumer side, AdaptedConnection would prefer the new > interface over the old interface. > > > For BuildResult and BuildRequest, we could go entirely dynamic, so that > these interfaces have (almost) no methods. Or we could go static with > method for the stuff we need now and dynamic for new stuff. I'm tempted to > go dynamic. > > > There also may be a case for providing parameters to the model builder. > > > At the moment, we are hardcoding that the only artifacts that we care > about are on the “archives” configuration. This is hardcoded in the > provider. I think it would be reasonable that one day we would want to give > the user control over this so they can deal with multiple configurations. > > > Even if we decide we can hardcode “archives” forever, it seems not > unreasonable that you might want to provide a spec to the model builder to > influence it. This seems similar to executing tests and injecting a > classpath (or providing anything from client to provider really). > > > Possibly. Do you have some examples? > > > Not beyond "here is the extra class path I want you to run with" at this > time. > > > I think the strategy we have for growing the tooling API can deal with > adding something like this when we need to. > > > -- > Adam Murdoch > Gradle Co-founder > http://www.gradle.org > VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting > http://www.gradleware.com > > -- Szczepan Faber Principal engineer@gradleware Lead@mockito
