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