On 07/05/2013, at 4:50 PM, Adam Murdoch <adam.murd...@gradleware.com> wrote:

> Hi,
> 
> For the C++ support, I'd like to make use of the deferred configuration 
> stuff. This will mean growing it to make it more usable.
> 
> There are 2 use cases I'd like to use deferred configuration for:
> 
> 1. Defer creation of tasks until we know which binaries to build. This allows 
> the creation of tool chain specific task implementations. We might also use 
> this to skip creation of tasks for binaries that cannot be built locally.
> 
> 2. Add default variants when none defined. This allows us to select a tool 
> chain based on whatever's available and add a variant for this.
> 
> For use case #1, this means being able to register some logic that runs after 
> a domain object has been configured. For use case #2, this means being able 
> to register some logic that runs just before a domain object is made 
> available for consumption, but after all other configuration logic.
> 
> One simple option is to allow the plugin that defines an extension (and only 
> that plugin) to register an action to run once the extension is configured. 
> This action can take care of both #1 and #2:
> 
>       project.extensions.createDeferred('libraries', NativeLibraries.class, { 
> … define defaults and then create tasks ... })
> 
> A couple of downsides: The main one is that there's no way to configure the 
> domain object without creating the tasks for that domain object and we want 
> to move away from this. The other is that there's no way to override the 
> 'infer the defaults' logic or the 'create the tasks' logic - it's baked into 
> the definition of the domain object.
> 
> A similar option is to allow logic to be registered that fires at various 
> points in the domain object's lifecycle:
> 
>       // Define a delayed extension (replaces @DeferredConfigurable) and 
> return a promise for the result
>       Delayed<NativeLibraries> extension = 
> project.extensions.define('libraries', NativeLibraries.class)
> 
>       extension.beforeConfigured { … called before any configure actions are 
> executed … }
>       extension.configure { … called to configure the object … }
>       extension.finishConfiguration { … called after all configure actions 
> are executed … }
>       extension.whenConfigured { … called once the object is configured … }
> 
>       // Force configuration, if you really need to
>       NativeLibraries libraries = extension.get()
> 
> Anything with access to the project would be able to do stuff with the 
> delayed object:
> 
>       Delayed<NativeLibraries> extension = 
> project.extensions.getDelayed(NativeLibraries.class);
>       extension.whenConfigured { … }
> 
> And via the DSL:
> 
>       Delayed<NativeLibraries> extension = 
> project.extensions.delayed.libraries
>       project.extensions.delayed.libraries.whenConfigured { … }
> 
> To infer default values, we might instead of `finishConfiguration` have a 
> single `addDefaults` action to be registered, and you can replace this action 
> with whatever logic you like.
> 
> To create tasks, we might add another lifecycle event, so that a single 
> action `createTasksFor` action can be registered. This is called somewhere 
> after configuring the domain object and building the task graph, and only 
> when we need the tasks for the domain object.

As part of project configuration, we'd only configure those objects for which a 
`createTasksFor` action had been registered. We'd probably only allow such 
actions to be attached to things that hang off a project (rather than, say, a 
task or the `gradle` instance).

The idea here is that the configuration of everything else will be triggered on 
demand as the actions that create the tasks access the things that they need. 
This would propagate through the model.

Something that might not be clear from the above is that eager extension 
objects would also have the same lifecycle attached to them. The only 
difference would be that configure actions in the DSL would be executed eagerly:

project.extensions.create('libraries', NativeLibraries.class)

def extension = project.extensions.delayed.libraries
extension.beforeConfigured { … executed before the first configure action, 
error if this has already happened ... }
extension.configure { … executes later, error if already configured ... }
extension.finishConfiguration { … executed after last configure action, error 
if this has already happened ... }
extension.createTasksFor { … executed later … }

project.libraries { … triggers configuration … }

// triggers configuration
def lib = project.libraries.main


--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com

Join us at the Gradle Summit 2013, June 13th and 14th in Santa Clara, CA: 
http://www.gradlesummit.com

Reply via email to