Gradle - Build lifecycle
Gradle #gradleBefore executing any task, gradle creates Directed Acyclic Graph
of the all the tasks and makes sure tasks are executed only once
and in order of their dependencies.
Build Lifecycle
Gradle goes through following three phases before executing the tasks
- Initialization
- Configuration
- Execution
Initialization
During initialization, gradle checks which projects are going to be part of the build and creates
Project
instance for each of these projects i.e a Project instance is created for eachbuild.gradle
file. Information about which projects will take part in the build is fetched fromsettings.gradle
file in case of multi-project build.
Configuration
During this phase, previously created project objects are configured i.e. build scripts (
build.gradle
) of all the projects taking part in the build are run against these project objects. All property access and methods inbuild.gradle
file are delegate to these Project instances.
Execution
At this point, gradle has already created the list of tasks and their dependencies for each project. Now, during this phase, gradle determines the task name arguments passed to
gradle
command and executes each of the tasks in order specified on command line
Settings.gradle file
settings.gradle
file is required in case of multi-project build and is executed during initialization
phase.
Gradle determines this file by naming convention, so it should be named as settings.gradle
in the root project of multi-project build. This file is not required in case of single-project build.
settings.gradle
file defines which projects will take part in this build. Just like instance ofProject, an instance of Settings class is created during initialization phase forsettings.gradle
and allmethod calls
&property access
insetting.gradle
file are delegated to this instance.
Example
// settings.gradle
rootProject.name = "demo"
println("This is executed during the initialization phase.")
// build.gradle
println("This is executed during the configuration phase.")
tasks.register("test") {
doLast {
println("This is executed during the execution phase.")
}
}
- Run
gradle -q
(no task is specified)
// output
This is executed during the initialization phase.
This is executed during the configuration phase.
:help SKIPPED
- Run
gradle -q test
(runtest
task)
// output
This is executed during the initialization phase.
This is executed during the configuration phase.
This is executed during the execution phase.
Initialization
In case of multi-project build, if build is triggered from a directory with settings.gradle
file, gradle uses it to configure the build, however, if the build is triggered from any sub-project then gradle will try to find the settings.gradle
file in following ways
- It looks for
settings.gradle
in parent directories. - If not found, the build is executed as a
single
project build. - If a
settings.gradle
file is found, Gradle checks if the current project is part of the multi-project hierarchy defined in the found settings.gradle file. If not, the build is executed as a single project build. Otherwise a multi-project build is executed.
From docs
What is the purpose of this behavior? Gradle needs to determine whether the project you are in is a subproject of a multi-project build or not. Of course, if it is a subproject, only the subproject and its dependent projects are built, but Gradle needs to create the build configuration for the whole multi-project build.
If current project contains settings.gradle
file then build is always executed according to this file either single or multi-project build.
The automatic search for a
settings.gradle
file only works for multi-project builds with a default project layout where project paths match the physical subproject layout on disk
Gradle creates a Project object for every project taking part in the build. For a multi-project build these are the projects specified in the Settings object (plus the root project)
Each project object has by default a name equal to the name of its top level directory, and every project except the root project has a parent project. Any project may have child projects.
Detecting lifecycle in build scripts
Gradle fires notifications as build progress through its lifecycle. You can either add closure to execute when notification is fired or use a listener interface. We will be using closures here
// build.gradle.kts
// for the same build.gradle as above
allprojects {
// executed when project is evaluated i.e. at the end of configuration phase
afterEvaluate {
println("after evaluation ${this.name}")
}
}
// output
This is executed during the initialization phase.
This is executed during the configuration phase.
after evaluation demo
evaluation
block can be used to add extra configuration once all definitions in a build script have been applied.
// executed when any project is evaluated, it doesn't matter if evaluation was successful or not
gradle.afterProject {
if (state.failure != null) {
println("Evaluation of $project FAILED")
} else {
println("Evaluation of $project succeeded")
}
}
Detecting task creation
// build.gradle.kts
// run this closure when a task is added to tasks list
tasks.whenTaskAdded {
// create srcDir extra for the task with was added
extra["srcDir"] = "src/main/java"
}
val a by tasks.registering
println("source dir is ${a.get().extra["srcDir"]}")