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.


--
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