More on gradle closures

There are closures on a project object that are documented in the API for a project

it is important to know what the delegate for a Closure is

Closure uses the delegate to resolve methods and properties

Closures in Gradle are used often to call methods on its delegate to configure that delegate

when a closure on a particular task is used, you are configuring the properties of that task to change its behavior

when you use a closure on a maven repository you are calling maven repository objects properties to add URLs.


buildscript {
   repositories {
       maven { url = "some-url" }
   }
}

1. project defines a method with closure called buildscript. Passes ScriptHandler as the delegate

2. So ScriptHandler has a method repositories() that takes a closure. This closure uses RepoistoryHandler as the delegate.

3. maven is a method on RepositoryHandler with a closure. Documentation says it passes a Maven handler as a "parameter". Not a delegate.

4. However it appears as if it is also passed as a delegate

5. maven handler has a get/set property setUrl(url) to set the url(s) //i think

groovy maven url property

Search for: groovy maven url property

this, delegate, it, owner in groovy closures

Search for: this, delegate, it, owner in groovy closures


buildscript {
   repositories {
       maven { 
         url = "some-url" 
         println this
         println it
         println delegate
         println owner
       }
   }
}

Start here for groovy closures

Owner, delegate, this is explained here

The "it" is defined here

DefaultArtifactMavenRepository.groovy source code

Search for: DefaultArtifactMavenRepository.groovy source code

Here is the source code for that

Odd, how can you add multiple urls for a single URL property in maven repository of Gradle?

Search for: Odd, how can you add multiple urls for a single URL property in maven repository of Gradle?

This is explored here

Source code is correct. URL is a single value field.


repositories {
    mavenCentral()
    maven {
        url "http://maven.springframework.org/release"
        url "http://maven.restlet.org"
    }
}

repositories {
  maven { url "http://maven.springframework.org/release" }
  maven { url "https://maven.fabric.io/public" }
}

if you see the "maven" method on the repositories handler it says this closure will "add and configure". Means each line or method call is an add of a new repo of maven!

That makes sense

In understanding this further you will need a link to the nature of RepositoryHandler

RepositoryHandler is a sub class of ArtifactRepositoryContainer

What is a ResolverContainer in Gradle?

Search for: What is a ResolverContainer in Gradle?

It is a collection of ArtifactRepositories

RepositoryHandler has a number of methods to add specific repositories

Its base calass ArtifactRepositoryContainer and its super classes can be used to retrieve the individual repositories


this - usual this object in java
   true even if the closure is a nested closure

owner - The parent object of the current clsoure
   may be same as this for direct closures
   will differ for inner closure
   for an inner closure the the it is 
   the previous closure.

delegate - An external object acts like an owner
  variables are scoped against the delegate as well
  By default, delegate is set to owner

Note that delegate is set and hence an object


class Person {
    String name
}
class Thing {
    String name
}

def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')

Also notice the auto named arg constructors.


//Notice name is an unresolved variable
def cl = { name.toUpperCase() }

//Set the delegate to be "p" from above
cl.delegate = p                                 
assert cl() == 'NORMAN' 

//Set the delegate to be "t" from above
cl.delegate = t
assert cl() == 'IGOR' 

Closure.OWNER_FIRST is the default strategy. If a property/method exists on the owner, then it will be called on the owner. If not, then the delegate is used.

Closure.DELEGATE_FIRST reverses the logic: the delegate is used first, then the owner

Closure.OWNER_ONLY will only resolve the property/method lookup on the owner: the delegate will be ignored.

Closure.DELEGATE_ONLY will only resolve the property/method lookup on the delegate: the owner will be ignored.

Closure.TO_SELF can be used by developers who need advanced meta-programming techniques and wish to implement a custom resolution strategy: the resolution will not be made on the owner or the delegate but only on the closure class itself. It makes only sense to use this if you implement your own subclass of Closure.

When a closure does not explicitly define a parameter list (using ->), a closure always defines an implicit parameter, named it.


def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'

buildscript {
   repositories {
       maven { 
         url = "some-url" 
         println this
         println it
         println delegate
         println owner
       }
   }
}

In Java lambda expressions are instances of single method interfaces with inner class semantics

In Groovy closures are instances of Closure class with delegation capability


//So this is wrong
repositories {
    mavenCentral()
    maven {
        url "http://maven.springframework.org/release"
        url "http://maven.restlet.org"
    }
}

//Correct way
repositories {
  maven { url "http://maven.springframework.org/release" }
  maven { url "https://maven.fabric.io/public" }
}

Remember, Each child of respositories is "A REPOSITORY".

So mavenCentrl() is a repo.

maven (with a url) is ONE Repo

maven IS NOT a collection of URLs, in other words maven() is not a collection of maven repositories.

The underlying repository MavenArtifactRespoistory() has a method setURL() which sets the URL. Calling it again will overr write the previous one.

So it all depends on how the underlying java classes and their methods are implemented.


repositories {
    flatDir name: 'libs', dirs: "$projectDir/libs"
    flatDir dirs: ["$projectDir/libs1", "$projectDir/libs2"]
}

There are 2 repos here

Both of type flatDir, or just a directory to look for assets

First Flat directory

It is called "libs"

it has a root directory specified

Second Flat Directory

it has no name

it is a list of directories


There are 2 repos here
Both of type flatDir, or just a directory to look for assets

First Flat directory
   It is called "libs"
   it has a root directory specified

Second Flat Directory
  it has no name
  it is a list of directories

FlatDirectoryArtifactRepository flatDir(Map<String, ?> args)

It is a map that is the input
It is really an associate array
Meaning it should have been an object definition like

   FileDirSpec
     name
     List<String> dirs;

Instead this is represented as a generic map
This map can have only 2 keys
   name - A string
   dirs - A string, or a list of strings

repositories {
    flatDir name: 'libs', dirs: "$projectDir/libs"
    flatDir dirs: ["$projectDir/libs1", "$projectDir/libs2"]
}

//First one
map1.add("name", "libs")
map1.add("dirs","$projectDir/libs");
flatDir(map1)

//Similarly second one

See this to understand named arguments to constructors and methods