The third of Gradle Learning Series--reading and understanding Gradle grammar

Source: Internet
Author: User
Tags closure object object git clone
the third of Gradle Learning Series--reading and understanding Gradle grammar

In the previous article in this series, we talked about the various ways to create a task, and in this article we will learn how to read gradle.

Download the GitHub sample code for this article series in the following ways:

git clone https://github.com/davenkin/gradle-learning.git

Gradle is a declarative building tool. At execution time, Gradle does not sequentially execute the contents of the Build.gradle file, but is divided into two phases, the first phase being the configuration phase, and then the actual execution phase. During the configuration phase, Gradle will read all the contents of all Build.gradle files to configure project and task, such as setting the property of project and task, handling dependencies between tasks, and so on.

Although many times we just need to follow the online example to write our own DSL statement, but at this time we know that there are only so many. If we can understand the internal working mechanism of the Gradle DSL, then we can achieve the extrapolate effect. As we mentioned in the previous article, Gradle's DSL is just the internal DSL of the groovy language, and it must follow groovy's grammatical rules. Now, let's take a look at the following very simple task:

Task ShowDescription1 << {
   Description = ' This is Task showdescription '
   println description
}


Task ShowDescription2 << {
   println description
}
showdescription2.description = ' This is task Showdescription '


task ShowDescription3 << {
   println description
}

showDescription3 {
   Description = ' This is Task Showdescription '
}

The above 3 tasks complete the same function, which is to set the Description property of the task before outputting it to the command line. However, they have different ways of setting up the description. For ShowDescription1, we set description while defining a task, and for ShowDescription2, it is a property of project, and for ShowDescription3 , we set the description in a method with the same name as it.

In fact, for each task,gradle, a property is created in project with the same name, so we can access the task as a property, which is the case with ShowDescription2. In addition, Gradle creates a method with the same name, which accepts a closure, which we can configure with this method. Task,showdescription3 is the case.

To read Gradle, we first need to understand two concepts in the groovy language, a bean concept in groovy, and a delegate mechanism for groovy closures.

There is a big difference between a bean in groovy and a Bean in Java, which is that groovy automatically generates getters and setters for each field, and we can invoke getter and setter like the field itself, such as:

Class Groovybeanexample {
   private String name
}

def bean = new Groovybeanexample ()
bean.name = ' This is NA Me '
println bean.name

As we can see, groovybeanexample only defines a private name attribute, and does not have getter and setter. However, when used, we can access the name directly, whether read or write. In fact, we are not directly accessing the Name property, and when we execute "bean.name = ' This is name '", we actually call "Bean.setname (' This is Name ')" while calling "println Bean.name ", we actually called" println Bean.getname () ". The reason for this is that groovy dynamically creates getter and setter for name, with the intent of direct access to increase the readability of the code and make it more natural, while inside, groovy is still calling setter and getter methods. In this way, we can understand the description setting principle of facing showDescription2.

In addition, Gradle uses the delegate mechanism of groovy closures extensively. Simply put, the delegate mechanism allows us to set the object of execution code in a closure to any other object. Like what:

Class Child {
   private String name
}

class "Parent {child"
   = new Child ();

   void Configchild (Closure c) {
      c.delegate = child
      c.setresolvestrategy closure.delegate_first
      C ()
   }
}

def parent = new parent ()
parent.configchild {
name = "Child Name"
}

println Parent.child.name

In the example above, when we call the Configchild () method, we do not indicate that the name attribute belongs to the child, but it does set the child's Name property. In fact the light from the call of the method, we do not know what name belongs to which object, you may think that it belongs to the parent. The truth is that, by default, name is considered to belong to the parent, but we do it in the definition of the Configchild () method so that it no longer accesses the name in the parent (the parent has no Name attribute), but the child's name. In the Configchild () method, we set the delegate of the closed packet accepted by the method to child, and then set the resolvestrategy of the closure to Delegate_first. Thus, when calling Configchild (), the code in the closed packet is proxied to the child, i.e. the code is actually executed on the child. In addition, the closure of the Resolvestrategy is Owner_first by default, that is, it will first find the owner of the closure (this is the parent), and if the owner exists, execute the code in the closure on owner. Here we set it to delegate_first that the closure will first look for delegate (in this case, child), and if found, the closure will be executed on delegate. This is the case for the above ShowDescription3. Of course, the reality is slightly more complicated, such as the ShowDescription3 () method calls the ShowDescription3 configure () method internally, and then executes the code in the closure in the Configure () method.

You may find that when using gradle, we do not indicate the object of the method call like the above Parent.configchild (), but instead call the task () directly in the Build.gradle file, apply (), and the configuration ( ) method, because Gradle automatically sets the calling object to the current project without stating the calling object. For example, invoking the Apply () method and calling the Project.apply () method have the same effect. Check out the project documentation for Gradle and you'll see that these methods are all methods of the project class.

For another example, for the Configurations () method (which we'll talk about in a later article), the method actually sets the delegate of the closed package to Configurationcontainer, The code in the closure is then executed on the Configurationcontainer. For example, the dependencies () method sets the delegate of the closed packet to Dependencyhandler.

Also, project defines the Configure (object Object,closure configureclosure) method, which is designed to configure objects (such as task). It sets the delegate of Configureclosure to object, after which the execution code in Configureclosure is actually executed on object. Like groovy beans, one of the benefits of the delegate mechanism is that it increases the readability of the DSL you create.

In the next article, we'll talk about how to build incrementally.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.