Saturday, November 26, 2011

Stupid Gradle Tricks #1: what can I do in this block?

In the previous post, we saw that each labeled block in a Gradle build file is really a closure with DELEGATE_FIRST scope. Each of these blocks has access to all the methods on the delegate object. The class of that delegate object varies by block.

Even looking at the source code to identify the class of the delegate won't tell you every method that's available on it. This is because Gradle plugins dynamically add methods to these things.

Here is a sneaky trick to find out what's available to you in a particular block. If you give the closure one argument, Gradle will populate it with the delegate. Then you can have the build print out information about it.

The following build script prints out the class of the repositories delegate and every method that's on it, formatting the output to remove bulky package information.

apply plugin: 'java'

def printDelegateInfo (String blockName, Object d) {
  println "---- ${blockName} block ----"
  println "Delegate is of class " + d.class
  d.class.methods.each {
     println (("Method: " + it + " from " + it.declaringClass =~ /[a-zA-Z\.]*\./).replaceAll("") )
  }
  println "----------------------------"
}

repositories { d ->
  printDelegateInfo("repositories", d)
}


The output is something like:
---- repositories block ----
Delegate is of class class org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler_Decorated
Method: public void DefaultRepositoryHandler_setProperty(String,Object) from class DefaultRepositoryHandler_Decorated
Method: public Object DefaultRepositoryHandler_getProperty(String) from class DefaultRepositoryHandler_Decorated
Method: public Class DefaultRepositoryHandler_getType() from class DefaultRepositoryHandler_Decorated
Method: public String DefaultRepositoryHandler_getDisplayName() from class DefaultRepositoryHandler_Decorated
Method: public FileResolver DefaultRepositoryHandler_getFileResolver() from class DefaultRepositoryHandler_Decorated
Method: public void DefaultRepositoryHandler_setConvention(Convention) from class DefaultRepositoryHandler_Decorated
...
Method: public void whenObjectAdded(Closure) from class AbstractDomainObjectCollection
Method: public Action whenObjectRemoved(Action) from class AbstractDomainObjectCollection
Method: public Set findAll(Spec) from class AbstractDomainObjectCollection
Method: public DomainObjectCollection withType(Class,Action) from class AbstractDomainObjectCollection
Method: public DomainObjectCollection withType(Class,Closure) from class AbstractDomainObjectCollection
...
Method: public final native void notify() from class Object
Method: public final native void notifyAll() from class Object
----------------------------


You could call printDelegateInfo in any similar block in Gradle, including a nested one. Just add the "d ->" at the beginning of your block to capture the delegate in a variable.

This trick is not as cool as autocomplete, but it can give you some clues. Note that a delegate's method with a Closure argument means that you can probably create a block with that label and do more detailed configuration.

No comments:

Post a Comment