In the first blog post, we discussed the need for a build system and features of the Gradle build tool. In this second article on understanding the Gradle build system, we are going to cover the following things:
- Gradle files.
- Gradle Properties.
Before getting into the Gradle build lifecycle, we should know about the build script files and properties.
Gradle Files
Gradle supports putting a common type of code/data in a separate file. For the beginner, this often becomes the cause of confusion and raising the questions like:
- How does Gradle know where to look for these files?
- Where should we put the build logic, properties, and project dependencies?
- In which order Gradle will execute these files?
In this section, we will see the answers to these questions. Also, we are going to learn about the different types of files, including their purpose, as understanding the Gradle without knowing these files is impossible. So let’s first learn the different types of files in the Gradle.
Gradle supports the following types of files:
- Settings.
- Build scripts.
- Init scripts.
- Property files.
Let’s dig into each of these types of files. Are you ready?
Settings
The default name of this file is either settings.gradle( for Groovy) or settings.gradle.kts( for Kotlin). The primary purpose of the settings file is to register sub-projects, which will be part of the build process. We can register sub-projects in the settings file using the include
method.
How does the Gradle know that the current build process is part of a single or multi-project build?
As we know, the Gradle supports both single and multi-project build, To determine the type of current build process, it searches for the settings file first in the current directory and then in its parent hierarchy and follow the below steps to determine the type of build:
- If both parent and current directory don’t contain settings file, then the Gradle will consider the build as a single project build.
- If a current directory has the settings file, then the Gradle will consider it as multi-project build and considers the current directory as a parent(root) project. Then it reads settings file to determine the sub-projects which need to include in the build process.
- If a current directory doesn’t contain the settings file, but if settings file is in the parent directory, then the Gradle will consider it as multi-project build. Then it checks whether or not the current subdirectory is registered as a subproject in the root project’s settings file. If the current project is part of the root project, then it is executed as a part of the multi-project build, otherwise, as a single project build.
Things to keep in mind about settings file are:
- We have access to gradle and rootProject instances in this file.
- We have access to properties, both declared in gradle.properties, and provided from command line.
Get more information on Settings here.
Build Scripts:
The default name of the build script file is either build.gradle( for Groovy) or build.gradle.kts( for Kotlin). There is a one-to-one relationship between a project
instance and a build file.
The single project builds — Build file contains dependencies and repository information for both build script and project.
Multi-project build — Each sub-project may have its build file, and each sub-project will be evaluated by executing its build file, if present. The build file of a root project is generally used for sharing common things that will be needed in all sub-projects. For example, build script dependencies, and repository links to locate those dependencies. And common tasks like clean
, which will delete the previously created resources by root project and each sub-project.
# Build script dependency Vs. Project dependency
Build script dependency means the libraries or plugins on which our Gradle build is dependent. For example, we want to build an Android project using Gradle then we need to add build script dependency on the com.android.tools.build:gradle
, then only Gradle will know how to build an android application.
On the other hand, project dependency is related to code which is going to be built. For example, let’s say our project need JUnit library to execute tests; hence we will need to declare JUnit dependency in the project scope and not in the build script scope.
BuildSrc
In a complex build project, we often need to write custom tasks or plugin or some build logic. These custom task and plugin implementations should not live in the build script file as that will increase the complexity of the build script file. We can keep this logic in the buildSrc
directory. Note — if this build script code needs to be shared between multiple, independent projects, then we can create separate Gradle project for this and publish its plugin.
Once Gradle discovers the buildSrc
directory, then Gradle automatically compiles and tests this code and puts it in the class-path of the build script. There can be only one buildSrc directory (even for the multi-project build), and it has to be in the root project directory.
Advantages of separating build script related code in buildSrc directory are, it is easier to maintain, refactor, and test the code. Get more information on BuildSrc here.
Init Scripts
Init scripts, also known as Initialisation scripts, are similar to build scripts in Gradle. Init scripts run before the build starts. These scripts act as global scripts that are shared for all the projects on the current machine. These scripts can be used for:
- Defining machine-specific details, such as where JDKs are installed.
- To set up properties based on the current environment, such as developer’s machine vs. continuous integration server.
- To supply personal information about the user that is required by the build, such as repository or database authentication credentials.
Note — Init scripts run before the project instance is created; therefor we can’t access classes in the buildSrc
project and project properties(gradle.properties) in the init scripts.
Where to declare init scripts?
As we know, init scripts are available to all projects on the current machine, so it has to available in the global shared area between all projects.
- We can provide init script file path from command line using
-I
or--init-script
option. - If we have only one init script, then we can name it as
init.gradle
orinit.gradle.kts
and put it in theUSER_HOME/.gradle/
directory. - If we have more than one init script files, then create directory named
init.d
and put it in theUSER_HOME/.gradle/
directory. The advantage of havinginit.d
directory is that we can have all init script files at one commonplace, and we can name the file differently than theinit.gradle
. The init scripts in theinit.d
directory gets executed in alphabetical order.
You can find more information on init scripts here.
Init Scripts Vs. Build Scripts
Build scripts are specific to a project. Gradle will create an instance of aproject
for each build script file, and it is implicitly available in that script file. There is one to one association between build file and project instance.
On the other hand, Init scripts are like global scripts that are shared between all the projects on the current machine. Gradle will create an instance of gradle
for each init script, and it is implicitly available in that script file.
Gradle properties:
Gradle allows to configure/customise build using properties. There are two types of properties project properties and system properties.
The system properties are passed to the JVM, which runs Gradle. We can use system properties to make sure the entire team works on the same environment, for example, some particular version of java.
The project properties are used to customise the project build using user-defined properties.
There is one more difference between the way system and project properties are handled. If we access some system property that doesn’t exist, then we get null
value for it, and Gradle execution will not stop/fail because of that. On the other hand, if we try to access some user property that is not available, then we will get MissingPropertyException
, and Gradle stops the execution, which means build will fail.
We can declare properties using the following ways:
- gradle.properties file.
- Extra Properties.
- Command line option.
# gradle.properties File
We can declare both system and project properties in the gradle.properties file. And we can have properties file for specific to a project as well as common file for all projects. Project specific property file resides in the same directory as root project. Shared, Global file properties file resides in the USER_HOME/.gradle/
directory. Use systemProp
prefix to declare system properties.
# Extra Properties
All enhanced objects in Gradle’s domain model can hold extra user-defined properties. This includes, projects, tasks, and source sets. Extra properties can be added, read and set via the owning object’s ext property. Alternatively, an ext block can be used to add multiple properties at once.
# Properties From Command Line
We can set both project and system properties from command line at the time of executing the build.
To set system property from command line use -D
option, for example, let’s say there is system property named javaVersion
and if we want to set it from command line, then we can use -DjavaVersion=1.8
with gradle command.
To set project property from command line use -P
option, for example, let’s say there is project property named testable
and if we want to set it from the command line, then we can use -Ptestable=true
with gradle command.
Thanks for reading this blog. I hope you are enjoying this series on the Gradle and learning something new. If you have any suggestions or questions, please add it in the comment below, Happy Learning 👏.