How can I get NamedDomainObjectContainer elements *after* they are configured?

(Ben Jansen) #1

What is the best way to execute some code against the elements in a NamedDomainObjectContainer after they have been completely configured? Using #all(), my code is executed before the elements are configured, and I haven’t found another method that behaves differently.

Here are more details in case the answer is problem-specific:

I am writing a custom plugin to implement the logic for taking some artifacts from a repository and shuffling their contents around. Specifically, the source artifacts are tar archives, and the output is a rearranged version of the archives, with some extra signatures and metadata files. I wanted to encapsulate all the fiddly bits into a plugin and extend Gradle’s DSL to give the build writers a fluent way to express the source artifacts that should be included and required metadata about them.

Here is the style I’d like to see in the build:

thingsToSign {
  thingOne {
    artifact group: 'foo', name: 'thing-one', version: '7.+'
    metadata 'bar'
    thingTwo {
    artifact group: 'foo', name: 'thing-two', version: '4.+'
    metadata 'baz'

To support this declaration, in my plugin I used the Project#container() method to create a NamedDomainObjectContainer for my Things, and added it as a project extension. Now, I am attempting to add dependencies upon the artifacts for thingOne and thingTwo to a configuration created by my plugin. This is how I tried to do that, but it doesn’t work, because the Thing’s artifact property is not set before my closure is called:

// In Plugin<Project>#apply()
def things = project.container(Thing)
project.extensions.thingsToSign = things
things.all { Thing thing ->
    // At this point, thing.artifact is null
    project.dependencies.add(THINGS_CONFIGURATION_NAME, thing.artifact)

(Peter Niederwieser) #2

You can use a hook such as ‘project.afterEvaluate { … }’ or the (internal) configuration mapping feature. There is also a new solution in the making.

(Ben Jansen) #3

Okay, thanks. I went with the ‘project.afterEvaluate {}’ route, and made the ‘afterEvaluate {}’ callback available for the unit test to invoke, based on your suggestion at:

This combination seems to have worked well & maintained testability.