Tuesday 1 December 2015

Lesson 1 - The Gradle build lifecycle

The Gradle build lifecycle




Just as android has a lifecycle for an activity the gradle build system also has its own lifecycle.  Gradle builds a complete dependency graph before any task is executed. This is actually the core of gradle and we can achieve awesomeness with it.  Let’s jump right into it.


The Phases  


The gradle lifecycle can be summarized by the following phases:

·   Initialization

·   Configuration

·   Execution


Initialization phase  

During this initial phase, gradle will figure out which projects need be used in the build.  It creates a project instance for each. 

Configuration phase  

During this secondary phase, gradle will configure the project instances themselves. So that means that the build scripts for ALL projects are invoked right here.   So any configuration scripts should run in this phase. Well see an example later.  But this is an important phase.

Execution phase  

Here gradle will gather tasks that might have been created during configuration phase. These task will need to be executed in this phase.  For example, supplying gradle a task on the command line such as gradlew someTask will execute a task called someTask during this phase.

 More on this later.  For now lets keep it simple.

The build lifecycle 


     Create a new android project  and make sure its set up and can be loaded without any errors.  We now should have a single project setup with a project/module called ‘app’ .

            Open up the settings.gradle file. It will be located in the top most level of the project structure.


           

add a println statement to the file, mine looks like this:

include ':app'
println 'hello says settings.gradle, via the initialization phase'

Since settings.gradle is apart of the initialization phase, this println should be the very first thing executed. 

Next, lets open up the build.gradle file for our ‘app’ project/module.



 At the very bottom of the file add a few lines like I have below:

task aExecutionTask << {

    println 'hello says aExecutionTask, via the execution phase'

}



task aConfigurationTask{

    println 'hello says aConfigurationTask, via the configuration phase'

}

println 'hello from the app project build.gradle, via configuration phase'

What I am about to demonstrate (after you sync your gradle file) is that after the initialization phase runs, the configuration and then the execution phases are next. Its always in this order. 

After you sync the project open the gradle console in android studio:





Search for the word hello or just read the logs, notice the order of whats being executed looks like this:

hello says settings.gradle, via the initialization phase 
hello says aConfigurationTask, via the configuration phase 
hello from the app project build.gradle, via configuration phase

the aExecutionTask did not execute but configuration phase ran. One way to start the execution phase would be from the command line in the projects root, by doing this:

./gradlew aExecutionTask

now the output will be:

hello says settings.gradle, via the initialization phase 
hello says aConfigurationTask, via the configuration phase 
hello from the app project build.gradle, via configuration phase 
hello says aExecutionTask, via the execution phase 

Execution phase close up 


When we use the “<<” in a task we are really saying to gradle “hey do this last in the execution phase”.  Lets see how we can re-work the aExecutionTask so that it does not use the “<<” but is still run in the execution phase.

Here is how the task will look now:

task aExecutionTask  {

    doFirst{
        println 'hello says aExecutionTask first, via the execution phase'
    }
    doLast{

        println 'hello says aExecutionTask last, via the execution phase'
    }
}
Since we used these closures doFirst and DoLast, it will be executed in the Execution phase. 
Lets run this task from android studio itself instead of the command line this time. I’ll show you how:

From android studio, open gradle task viewer like this:

 


Expand your app’s task list like this:



and double click aExecutionTask.  The output of this task will be shown below and now looks like this:

hello says settings.gradle, via the initialization phase 
hello says aConfigurationTask, via the configuration phase 
hello from the app project build.gradle, via configuration phase  
:app:aExecutionTask 
hello says aExecutionTask first, via the execution phase 
hello says aExecutionTask last, via the execution phase

Pay attention 
When a task is executed the configuration tasks MUST be evaluated first in order to build the gradle dependency graph. That's why in the above execution, when we ran aExecutionTask, surprisingly the configuration items were executed before the actual execution.  When in a configuration task remember that anything in doLast (or using << ) is a execution task, outside this scope is configuration.  So repeat after me... When i run an execution task, configurations run first. Got it ? Good, now lets move on.   

project instances


In the initialization phase, gradle creates project instances for each project. We can gain a reference to the project and change attributes on it.  Here is an  example:

println rootProject.name

or

println project(':app').name


Conclusion


     Of course gradle supports multi-project builds, this  tutorial only highlights a single project build but gives us a sound understanding of how gradle executes tasks in its lifecycle.  As you can see, the core of the gradle build system is dependency based – initialization, configuration and execution.   

In the absence of any other proof, the thumb alone would convince me of God's existence. -Isaac Newton



1 comment:

  1. Informative blog.
    and also we are providing E-Learning Portal Videos for students and working Professionals
    Hurry Up! Bag All Courses in Rs - 10000 /- + taxes
    41 Career building courses.
    Designed by 33 industrial experts
    600+ hours of video Content
    DevOps and Cloud E-Learning Portal

    ReplyDelete