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

Reply via email to