How does implicit creation of objects in containers work?

I’ve been going through the gradle source code and the groovy language to understand this. But so far the answer has eluded me.

How does the implicit creation of objects in containers work in gradle? More specifically, implementations of NamedObjectContainer seem to have this magical ability to create objects if they don’t exist. I’m going to provide specific examples.

The example here includes this.

apply plugin: 'java' //so that I can use 'compile', 'testCompile' configurations

configurations {
  //adding a configuration:
  myConfiguration

  //adding a configuration that extends existing configuration:
  //(testCompile was added by the java plugin)
  myIntegrationTestsCompile.extendsFrom(testCompile)

  //configuring existing configurations not to put transitive dependencies on the compile classpath
  //this way you can avoid issues with implicit dependencies to transitive libraries
  compile.transitive = false
  testCompile.transitive = false
}

The configuration myConfiguration is created implicitly here. An explicit creation would look like this.

configurations.create('myConfiguration')

The same method is used in the distribution plugin.

apply plugin: 'distribution'

version = '1.2'
distributions {
    custom {}
}

I know the basics of the meta object protocol in groovy. I also know gradle uses AST transformation to get the convention mapping keys to be properties of the enclosing object. But none of that seems to explain what’s going on here.

The addRule method on NamedDomainObjectCollection can be used to do this. But I did a fairly thorough examination of where the method was being used and found that it doesn’t seem to be used to get this behavior.

I would assume this comes from the “secret sauce” of the Groovy DSL mechanism, the “methodMissing” and “propertyMissing” methods.

@David_Karr It doesn’t seem likely. Those were the first things I checked.

These are the non-test files containing methodMissing. I explored most of them and they don’t seem to have much do with the question. And this would be a method call and not a property reference unless I’m terribly mistaken.

$ git grep --name-only 'methodMissing' | grep -iv 'test'

buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractDynamicObject.java
subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java
subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultConvention.java
subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultExtraPropertiesExtension.java
subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultIsolatedAntBuilder.groovy
subprojects/core/src/main/groovy/org/gradle/groovy/scripts/BasicScript.java
subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java
subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployer.groovy
subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelMapGroovyDecorator.java
subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGenerator.java
subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/NonTransformedModelDslBacking.java
subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatoryProvider.groovy

Let me guide you briefly through the sources to understand how it is done in the gradle codebase.

your example snippet

configurations {
    //adding a configuration:
    myConfiguration
}

is delegated `Project#configurations(Closure):

https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java#L834-836`

As you can see this calls ConfigurationContainer.configure(Closure) which does https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractNamedDomainObjectContainer.java#L67-70

In this method you can see that a specific delegate for that closure is created. This delegate contains the logic for creating items on the fly: https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/groovy/org/gradle/api/internal/NamedDomainObjectContainerConfigureDelegate.java#L34-40

Hope that helps understanding how this stuff works

@Rene Thank you so much for the pointers. They’re really helpful.

Thes URLs are now at

Noitice /main/groovy becake /main/java