Order of configure closure binding changed between Gradle 1.8 and 2.0


(t.enderling) #1

I’m trying to migrate from Gradle 1.8 to 2.0 and noticed an intricate change in behavior of the DSL.

The following snippet demonstrates the change:

class SimpleDomainObject
{
    String name
          public SimpleDomainObject(String name)
    {
                this.name = name
    }
      void methodInSimpleDomainObject() {
        println "methodInSimpleDomainObject called for object '$name'"
    }
}
  extensions.add('container', project.container(SimpleDomainObject))
  container {
    repositories {
           methodInSimpleDomainObject()
    }
}
  println 'Objects in container: ' + container*.name

Executing this code in Gradle 1.8 prints:

methodInSimpleDomainObject called for object 'repositories'
Objects in container: [repositories]

Executing it in Gradle 2.0:

Build file 'build.gradle' line: 19
  * What went wrong:
A problem occurred evaluating root project 'closurebinding'.
> Could not find method methodInSimpleDomainObject() for arguments [] on repository container.

This only occurs if the name of the domain object is a method in an enclosing closure delegate (like in this case the project). This is pretty suprising.

Digging through the changes, the culprit seems to be changes to ConfigureDelegate: http://code-review.gradle.org/changelog/Gradle/?cs=cf0af3757a35aed8801acccd5110d15935482058

Even more surprising: If you omit the call to methodInSimpleDomainObject, both versions yield the same result. But then there is no object created in the container!

Objects in container: []

I see, why the new behavior in Gradle 2.0 is more reliable than the behavior in 1.8. I’m still not convinced however, why one would want to call a configure-method, i.e. a method call with a single closure parameter, onto an object in some outer scope instead of creating a new object in the directly surrounding container.

Maybe you can reconsider and give the delegate precedence over the owner for all closure-like methods. If not, you should at least document this as potential breaking change in Gradle 2.0, as it is very hard to find and investigate.