Dev-Mind

Gradle in practice (EN)

02/08/2020
Gradle 

In this training, we will learn how Gradle works and how use it in our projects. It’s just a fast presentation of this tool and you will find more informations on official website https://docs.gradle.org/current/userguide/userguide.html

Gradle by Dev-Mind

Gradle is an open source tool for automating the build of your Java, Kotlin, Android, Web projects…​ This tool is like Ant and Maven. Gradle has the Ant flexibility and apply many Maven conventions. Maven helps to build only one project. But Gradle was created to be more flexible, you can build several artifacts. That’s why the Android community has chosen this tool.

Introduction

Highly customizable
you have a convention but everything is customizable. You can write your scripts in Groovy (default) or in Kotlin (default in the future)

Fast
Incremental build : Gradle completes reuses outputs from previous executions, processing only inputs that changed, and executing tasks in parallel.

Powerful
Gradle build projects in several languages but its success is linked to Android developers.

Useful
Based on Ivy and supports Maven, it simplifies the project dependencies (libraries).You can use transitive dependencies

Why a build tool?

Write once, run everywhere

Build your project

  • To generate your delivery packages of your app (apk, jar, war…​)

  • To run tests

  • To generate documentation and reports

  • To deploy your app

…​

Usage

Principle

  • Gradle was not the first build tool.In the past we used Ant and after Maven

  • Ant is very powerful, but the configuration is not readable and difficult to use on a project with a lot of people

  • Maven is

    • very verbose (you have to write a lot of XML)

    • not very flexible (governance and execution)

Note

For the governance Maven is managed by only one person, Jason Vanzyl and his society Sonatype. it’s difficult to contribute to this project For Gradle it’s totally different. You can open projects on Github and see all the contributors.

Starting with Gradle

You can install the last version on https://gradle.org/install/

Open a terminal on your laptop or computer to create and initialize a new Gradle project. You will create a directory and link it to Gradle

mkdir gradle-demo
cd gradle-demo
gradle init

If you use version 7.2 you will have to respond to few questions

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Project name (default: test):

You should have this message after

> Task :init
Get more help with your project: Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.2/samples

BUILD SUCCESSFUL in 1m 56s
2 actionable tasks: 2 executed

Gradle should have generated this tree

|-- build.gradle  // (1)
|-- gradle
|   | -- wrapper
|       | -- gradle-wrapper.jar  // (2)
|       | -- gradle-wrapper.properties  // (3)
|-- gradlew  // (4)
|-- gradlew.bat  // (5)
|-- settings.gradle  // (6)
  • (1). Gradle configuration script for the project

  • (2). This jar contains Gradle Wrapper classes and libraries

  • (3). Wrapper configuration file

  • (4). and 5. these script are used to launch Gradle via the wrapper (2 scripts, one for Unix one for Windows)

  • (6). general configuration file (used to declare Gradle sub modules, and global variables)

Gradle wrapper

When you use a build tool, all team members must use the same version.From one project to another, you can have different versions of the tool (it’s difficult to maintain on your computer).

Gradle wrapper resolves these problems.The recommended way to execute any Gradle build is with the help of the Gradle Wrapper (in short just “Wrapper”).The Wrapper is a script that invokes a declared version of Gradle (it fixes the version used in your project), downloading it beforehand if necessary.As a result, developers can get up and running with a Gradle project quickly without having to follow manual installation.

wrapper
$ ./gradlew -v
Downloading https://services.gradle.org/distributions/gradle-7.2-bin.zip
..........10%...........20%...........30%...........40%...........50%...........60%...........70%...........80%...........90%...........100%

------------------------------------------------------------
Gradle 7.2
------------------------------------------------------------

Build time:   2021-08-17 09:59:03 UTC
Revision:     a773786b58bb28710e3dc96c4d1a7063628952ad

Kotlin:       1.5.21
Groovy:       3.0.8
Ant:          Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM:          11.0.11 (Ubuntu 11.0.11+9-Ubuntu-0ubuntu2.20.10)
OS:           Linux 5.8.0-63-generic amd64

An example

Go in IntelliJ in the menu FileNewProject From Existing Sources

idea1
|-- app
|   | -- src
|      | -- main
|          | -- java
|          | -- resources
|      | -- test
|          | -- java
|          | -- resources
|   | -- build.gradle
|-- gradle
|   | -- wrapper
|       | -- gradle-wrapper.jar
|       | -- gradle-wrapper.properties
|-- gradlew
|-- gradlew.bat
|-- settings.gradle

Open the main file called build.gradle. This is a Java project. So we use Java plugin provided by Gradle

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
    id 'java'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'

    // This dependency is used by the application.
    implementation 'org.springframework:spring-context:5.2.16.RELEASE'
}

application {
    // Define the main class for the application.
    mainClass = 'com.devmind.gradle.MyApplication'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

You can now launch this command

$ ./gradlew build
BUILD SUCCESSFUL in 509ms
8 actionable tasks: 8 up-to-date

Gradle execute tasks and in our case Java plugin has launched 8 tasks to build the project. You can launch the run task to execute your app

$ ./gradlew :app:run
> Task :app:run
I want to learn Gradle

BUILD SUCCESSFUL in 503ms
3 actionable tasks: 1 executed, 2 up-to-date

With IntelliJ, we have a synthetic view of dependencies and tasks

idea3
$ ./gradlew tasks --all

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'gradle-demo'
------------------------------------------------------------

Application tasks
-----------------
app:run - Runs this project as a JVM application

Build tasks
-----------
app:assemble - Assembles the outputs of this project.
app:build - Assembles and tests this project.
app:buildDependents - Assembles and tests this project and all projects that depend on it.
app:buildNeeded - Assembles and tests this project and all projects it depends on.
app:classes - Assembles main classes.
app:clean - Deletes the build directory.
app:jar - Assembles a jar archive containing the main classes.
app:testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

Distribution tasks
------------------
app:assembleDist - Assembles the main distributions
app:distTar - Bundles the project as a distribution.
app:distZip - Bundles the project as a distribution.
app:installDist - Installs the project as a distribution as-is.

Documentation tasks
-------------------
app:javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'gradle-demo'.
app:buildEnvironment - Displays all buildscript dependencies declared in project ':app'.
dependencies - Displays all dependencies declared in root project 'gradle-demo'.
app:dependencies - Displays all dependencies declared in project ':app'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradle-demo'.
app:dependencyInsight - Displays the insight into a specific dependency in project ':app'.
help - Displays a help message.
app:help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
app:javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of root project 'gradle-demo'.
app:outgoingVariants - Displays the outgoing variants of project ':app'.
projects - Displays the sub-projects of root project 'gradle-demo'.
app:projects - Displays the sub-projects of project ':app'.
properties - Displays the properties of root project 'gradle-demo'.
app:properties - Displays the properties of project ':app'.
tasks - Displays the tasks runnable from root project 'gradle-demo' (some of the displayed tasks may belong to subprojects).
app:tasks - Displays the tasks runnable from project ':app'.

Verification tasks
------------------
app:check - Runs all checks.
app:test - Runs the unit tests.

Other tasks
-----------
app:compileJava - Compiles main Java source.
app:compileTestJava - Compiles test Java source.
components - Displays the components produced by root project 'gradle-demo'. [deprecated]
app:components - Displays the components produced by project ':app'. [deprecated]
dependentComponents - Displays the dependent components of components in root project 'gradle-demo'. [deprecated]
app:dependentComponents - Displays the dependent components of components in project ':app'. [deprecated]
model - Displays the configuration model of root project 'gradle-demo'. [deprecated]
app:model - Displays the configuration model of project ':app'. [deprecated]
prepareKotlinBuildScriptModel
app:processResources - Processes main resources.
app:processTestResources - Processes test resources.
app:startScripts - Creates OS specific scripts to run the project as a JVM application.

BUILD SUCCESSFUL in 495ms

How Gradle works ?

How Gradle works ?
  1. Gradle connects to a remote plugin repository to load them.A plugin brings a task set

  2. Gradle connects to a remote library repository and retrieves those declared for execution and testing

  3. A task will act with our application

  4. A task has a result (OK, KO, directory deletion, packaging jar …​)

So, a project managed by Gradle is a configuration file that will indicate

  • how to download Gradle plugins (that provide a set of tasks)

  • how to download dependencies of our project (Java libraries)

  • tasks define a life cycle

  • everything is configured via a DSL (Domain Specific Language) written in Groovy or Kotlin#

Tasks

You have many predefined tasks (provided by plugins)

Defines what to do on a set of resources

A task may depend on one or more tasks.

Gradle creates a Directed Acyclic Graph (DAG) that defines a path to a task

Dependant task

Add these lines to your build.gradle file

task hello {
    doLast {
        println 'Hello'
    }
}

task world(dependsOn: hello) {
    doLast {
        println 'World'
    }
}

Test by launching theses tasks

$ ./gradlew hello
$ ./gradlew world

Life cycle

A Gradle build has 3 steps

Initialization
Gradle determines which projects are involved in the build. A project can have subprojects. All of them have a build.gradle.

Configuration
Gradle parses the build.gradle configuration file (or more if subprojects). After this step, Gradle has his task tree

Execution Gradle execute one or several tasks (arguments added to ./gradlew) according to this task graph. Gradle execute tasks one by one in the order defined in the graph.

Plugins

A plugin provide a task set and entry points to configure this plugin.For example

plugins {
    id 'application'
    id 'java'
}

Effect of this line :

Fonctionnement de Gradle

Another example

In the next TP we will use Spring and Spring Boot.We will use Gradle to manage our projects

buildscript {
    plugins { // 1
      id 'org.springframework.boot' version '2.5.4'
      id 'io.spring.dependency-management' version '1.0.11.RELEASE'
      id 'java'
    }

    group = 'com.devmind.faircorp'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '11'

    repositories { // (2)
      mavenCentral()
    }

    dependencies {
      implementation 'org.springframework.boot:spring-boot-starter' // (3)
      testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }

    test {
      useJUnitPlatform()  // (4)
    }
}
  • (1). Gradle plugin used

  • (2). repository used to download plugins or app libraries

  • (3). Application dependencies (libraries used by the project)

  • (4). Personnalization of the plugin. Each plugin has a documentation

Customize tasks

Open your project gradle-demo in IntelliJ and add the following code in build.gradle

println 'This is executed during the configuration phase.'

task configured {
    println 'This (configured) is also executed during the configuration phase.'
}

task testWrite {
    doLast {
        println 'This (testWrite) is executed during the execution phase.'
    }
}

task testWriteBoth {
    doFirst {
        println 'This (testWriteBoth) is executed first during the execution phase.'
    }
    doLast {
        println 'This (testWriteBoth) is executed last during the execution phase.'
    }
    println 'This (testWriteBoth) is executed during the configuration phase as well.'
}

Launch

$ ./gradlew tasks

Then

$ ./gradlew testWrite

And

$ ./gradlew testWriteBoth

Try to understand what happens ?

$ ./gradlew tasks

> Configure project :
This is executed during the configuration phase.
This (configured) is also executed during the configuration phase.
This (testWriteBoth) is executed during the configuration phase as well.

...

Dependencies

Dependances
  1. Gradle looks in his cache if the dependency is present

  2. It parses the given remote repository(ies), downloads the dependency and stores it in his cache

  3. Dependency can be provided to project

  4. If this dependency has another dependencies, Gradle loads them transitively

When an dependency needs to be loaded

  • The repositories are analyzed in the order of their definition.

  • Maven or Ivy repositories can be used

  • If the version number is dynamic like 1.+ Gradle will take the highest version (For example if you have versions 1.1, 1.2, 1.3, 1.+ is the 1.3 version) ⇒ bad practice

  • If the target is a Maven repository and the pom.xml has a parent, Gradle tries to load them