Gradle Quick Start - kaushikdas/TechnicalWritings GitHub Wiki

1. Introduction

1.1. Six Key Features of Gradle

  1. Build File
  2. Construction of a Graph of Tasks
  3. Execution of Tasks
  4. Gradle Wrapper
  5. Dependency Management
  6. Use of Repositories
  7. Self Updating
1.1.1. Build File
  • Human and machine readable instruction file
  • Partly declarative and partly programmatic
    • Uses DSL (Domain Specific Language) to describe the build
    • Uses Groovy (or Kotlin) to specify lower level details of the build
  • Default name: build.gradle
1.1.2. Graph of Tasks
  • Tasks: detailed build steps
  • Gradle parses the build file and creates a DAG (Directed Acyclic Graph) of tasks
~ $ mkdir hello-gradle

~ $ cd hello-gradle

Create a file build.gradle:

~/hello-gradle $ vi build.gradle
task helloGradleTask { // Create task named helloGradleTask
    doLast { // add a method to the task
        println "Hello Gradle!"
    }
}

Run the created task

~/hello-gradle $ gradle helloGradleTask

> Task :helloGradleTask
Hello Gradle!

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

If we choose to use Kotlin the gradle file name should be build.gradle.kts:

tasks.create("helloGradleTask") {
    doLast {
        println("Hello Gradle!")
    }
}
1.1.3. Execution of Tasks
  • Executes as per the order indicated by the DAG
    • Output of task = Input of the (next) task
    • Saves output of the each task
      • When we rerun the build if the output of a task remains the same then there is no need to run the next task because its output will not change
1.1.4. Gradle Wrapper
  • A wrapper over gradle that allows to specify the required Gradle version for the project. If that version is not present it will be automatically installed

  • Create gradle wrapper using command gradle wrapper

~/hello-gradle $ gradle wrapper

~/hello-gradle $ ls -l
total 13
-rw-r--r-- 1 KausPratMaha 197121  141 Jun 12 20:15 build.gradle
drwxr-xr-x 1 KausPratMaha 197121    0 Jun 12 20:31 gradle/
-rwxr-xr-x 1 KausPratMaha 197121 5770 Jun 12 20:31 gradlew*
-rw-r--r-- 1 KausPratMaha 197121 3058 Jun 12 20:31 gradlew.bat

This command creates the gradle directory and two executable files gradlew and gradlew.bat required for x-nix type of system and Windows system, respectively. These executables run the jar gradle/wrapper/gradle-wrapper to check which version of the gradle is installed and which version is required. It knows which version of gradle is required using the entry distributionUrl in the file gradle/wrapper/gradle-wrapper.properties. If the required gradle version is not present it downloads the required gradle version.

  1. We should check-in all these generated by gradle wrapper to source control.
  2. We can specify the gradle version we need using --gradle-version: gradle wrapper --gardle-version 6.4.1
~/hello-gradle $ cat gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

To use gradle wrapper we can use the gradlew and gradle task that we want to run. For example:

~/hello-gradle $ .\gradlew.bat helloGradleTask

> Task :helloGradleTask
Hello Gradle!

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

Benefits of using Gradle Wrapper

  1. Required gradle version is automatically installed (if not available already)
  2. Works the same way on continuoys intergration servers like Jenkins
1.1.5. Dependency Management
  • Dependencies to external Java code and libraries
  • May have transitive dependencies
  • Ensures that right version of the dependencies
1.1.6. Use of Repositories
  • Repositories: Storehouse of external dependencies
    • On the Internet/with the enterprise/on the local machine
1.1.7. Self Updating
  • Automatically updates to newer version
  • Auto-retrieval of newer versions of dependencies

1.2. Essentials

1.2.1. Conventions

  • Single project build

    root_project_name
    β”œβ”€β”€ build.gradle
    β”œβ”€β”€ src
        └──
        ...
    
    • src is a directory
  • Multi-project build

    root_project_name
    β”œβ”€β”€ build.gradle
    β”œβ”€β”€ settings.gradle
    β”œβ”€β”€ module_1
    β”‚   β”œβ”€β”€ build.gradle
    β”‚   β”œβ”€β”€ src
    β”‚   ...
    β”œβ”€β”€ module_2
    β”‚   β”œβ”€β”€ build.gradle
    β”‚   β”œβ”€β”€ src
    β”‚   ...
    
  • Settings file settings.gradle inside the root directory

    • Declares the participating projects (in case of multi-project build)
    • Can change the default, viz. the project name
      • By default the project name is the name the of the root directory
      • We can get the the project name (and other project related info. using the projects tasks. For example if we run the projects from inside hello-gradle directory we get the root project name as 'hello-gradle'
    ~/hello-gradle $ ./gradlew projects
    
    > Task :projects
    
    ------------------------------------------------------------
    Root project
    ------------------------------------------------------------
    
    Root project 'hello-gradle'
    No sub-projects
    
    To see a list of the tasks of a project, run gradlew <project-path>:tasks
    For example, try running gradlew :tasks
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 executed
    
    • We can create the file settings.gradle and add the rootProject.name property to set the project name (to some other meaningful name)
    ~/hello-gradle $ cat settings.gradle
    rootProject.name = "gradle-essentials"
    ~/hello-gradle $ ./gradlew projects
    
    > Task :projects
    
    ------------------------------------------------------------
    Root project
    ------------------------------------------------------------
    
    Root project 'gradle-essentials'
    No sub-projects
    
    To see a list of the tasks of a project, run gradlew <project-path>:tasks
    For example, try running gradlew :tasks
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 executed
    • It also shows that this vanila project has not sub-projects
  • Gradle properties file β€” gradle.properties

    • Resides in the project root directory or Gradle user home directory
      • If exists in the Gradle user home directory, then it's configuration applies to all Gradle projects executed on the machine
    • Preconfigures the runtime behaviour
    root_project_name
    β”œβ”€β”€ build.gradle
    β”œβ”€β”€ settings.gradle
    β”œβ”€β”€ gradle.properties
    ...
    
    ~/hello-gradle $ cat gradle.properties
    org.gradle.logging.level = info
    version = 1.0.0
    
    • org.gradle.logging.level = info changes to info logging level
    • version = 1.0.0 specifies the version of the project as 1.0.0
    • These propery values can also be accessed from build.gradle
    task helloGradleTask { // Create task named helloGradleTask
      doLast { // add a method to the task
          println "Hello Gradle! version " + version
      }
    }
    ~/hello-gradle $ ./gradlew helloGradleTask
    Initialized native services in: C:\Users\KausPratMaha\.gradle\native
    Found daemon DaemonInfo{pid=19928, address=[ec7419e0-52e6-41df-b1ce-c4e9a28273d5 port:55545, addresses:[/127.0.0.1]], state=Idle, lastBusy=1623506306426, context=DefaultDaemonContext[uid=13fd02a0-d0c8-4676-abb2-dd9d8032e19a,javaHome=C:\Program Files\Java\jdk1.8.0_221,daemonRegistryDir=C:\Users\KausPratMaha\.gradle\daemon,pid=19928,idleTimeout=10800000,priority=NORMAL,daemonOpts=-XX:MaxMetaspaceSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xms256m,-Xmx512m,-Dfile.encoding=windows-1252,-Duser.country=IN,-Duser.language=en,-Duser.variant]} however its context does not match the desired criteria.
    Java home is different.
    Wanted: DefaultDaemonContext[uid=null,javaHome=C:\Program Files\AdoptOpenJDK\jdk-11.0.9.101-hotspot,daemonRegistryDir=C:\Users\KausPratMaha\.gradle\daemon,pid=2916,idleTimeout=null,priority=NORMAL,daemonOpts=--add-opens,java.base/java.util=ALL-UNNAMED,--add-opens,java.base/java.lang=ALL-UNNAMED,--add-opens,java.base/java.lang.invoke=ALL-UNNAMED,--add-opens,java.prefs/java.util.prefs=ALL-UNNAMED,-XX:MaxMetaspaceSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xms256m,-Xmx512m,-Dfile.encoding=windows-1252,-Duser.country=IN,-Duser.language=en,-Duser.variant]
    Actual: DefaultDaemonContext[uid=13fd02a0-d0c8-4676-abb2-dd9d8032e19a,javaHome=C:\Program Files\Java\jdk1.8.0_221,daemonRegistryDir=C:\Users\KausPratMaha\.gradle\daemon,pid=19928,idleTimeout=10800000,priority=NORMAL,daemonOpts=-XX:MaxMetaspaceSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xms256m,-Xmx512m,-Dfile.encoding=windows-1252,-Duser.country=IN,-Duser.language=en,-Duser.variant]
    
      Looking for a different daemon...
    The client will now receive all logging from the daemon (pid: 15608). The daemon log file: C:\Users\KausPratMaha\.gradle\daemon\6.4.1\daemon-15608.out.log
    Starting 15th build in daemon [uptime: 2 hrs 29 mins 16.03 secs, performance: 98%, non-heap usage: 19% of 268.4 MB]
    Using 4 worker leases.
    Starting Build
    Settings evaluated using settings file 'C:\Users\KausPratMaha\hello-gradle\settings.gradle'.
    Projects loaded. Root project using build file 'C:\Users\KausPratMaha\hello-gradle\build.gradle'.
    Included projects: [root project 'gradle-essentials']
    
    > Configure project :
    Evaluating root project 'gradle-essentials' using build file 'C:\Users\KausPratMaha\hello-gradle\build.gradle'.
    Compiling build file 'C:\Users\KausPratMaha\hello-gradle\build.gradle' using SubsetScriptTransformer.
    Compiling build file 'C:\Users\KausPratMaha\hello-gradle\build.gradle' using BuildScriptTransformer.
    All projects evaluated.
    Selected primary task 'helloGradleTask' from project :
    Tasks to be executed: [task ':helloGradleTask']
    Tasks that were excluded: []
    :helloGradleTask (Thread[Daemon worker Thread 9,5,main]) started.
    
    > Task :helloGradleTask
    Caching disabled for task ':helloGradleTask' because:
      Build cache is disabled
    Task ':helloGradleTask' is not up-to-date because:
      Task has not declared any outputs despite executing actions.
    Hello Gradle! version 1.0.0
    :helloGradleTask (Thread[Daemon worker Thread 9,5,main]) completed. Took 0.021 secs.
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 executed
    - This time in prints lot of information because of `info` log 
      level and also shows the version as `1.0.0` as set in the
      `gradle.properties` file
    

1.2.2. Gradle Tasks

  • Defines an executable unit of work
  • Task = set of actions
    • Actions contain logic to be executed at runtime
  • Two types of tasks
    • Ad hoc tasks

      • Implements one-off simplicstic action implementation by defining doFirst or doLast
      • Automatically uses the default implementation of task interface
        • Extends DefaultTask without explicit declaration
        • Ex. task helloGradleTask explained in the previous section
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚              β”‚
      β”‚ Default Task β”‚
      β”‚              β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β–³
             β”‚
             β”‚ extends
             β”‚
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚             β”‚
      β”‚ Ad hoc Task β”‚
      β”‚             β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      
      
    • Of explicitly declared type

      • Complex class logic can be also abstracted by task implementation β€” types taks in Gradle terminalogy
      • Explicitly declares type (viz. copy) and does not define actions as those are provided by the type (task)
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚              β”‚
      β”‚  (ex.) Copy  β”‚  ←── This can copy 
      β”‚              β”‚      files & dirs
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β–³
             β”‚
             β”‚ extends
             β”‚
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚            β”‚
       β”‚ Typed Task β”‚
       β”‚            β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      
      
      • While using types tasks we need not implement any methods, we can just call the methods defined in the typed task and provide values to required properties

      • Let us add a task copyDocs in build.gradle using Copy typed tasks that will recursively copy all the .md files from md directory to build/docs directory

        Target (build/docs) directory will be created is not present

      ~/hello-gradle $ tree
      .
      β”œβ”€β”€ build.gradle
      β”œβ”€β”€ gradle
      β”‚   └── wrapper
      β”‚       β”œβ”€β”€ gradle-wrapper.jar
      β”‚       └── gradle-wrapper.properties
      β”œβ”€β”€ gradle.properties
      β”œβ”€β”€ gradlew
      β”œβ”€β”€ gradlew.bat
      β”œβ”€β”€ md
      β”‚   β”œβ”€β”€ ReadMe.txt
      β”‚   β”œβ”€β”€ readme.md
      β”‚   β”œβ”€β”€ sd
      β”‚   β”‚   β”œβ”€β”€ errors.txt
      β”‚   β”‚   └── read_style.md
      β”‚   └── sd_bak
      └── settings.gradle
      task copyDocs(type: Copy) {
          from "md"           // Call the `from` method of Copy type task
          into "build/docs"   // Call the `to` method
          include "**/*.md"   // ANT like include patterns
                              // .. `**` -> scan all sub-dirs
      
          includeEmptyDirs = false  // because by default empty dirs are
                                    // ... also copied
      }    

      Execute the copyDocs task:

      ~/hello-gradle $ gradle copyDocs
      
      BUILD SUCCESSFUL in 2s       
      1 actionable task: 1 executed
      

      Now the build/docs directory have the .md files from md dir:

      ~/hello-gradle $ tree build
      build
      └── docs
          β”œβ”€β”€ readme.md
          └── sd
              └── read_style.md
      • Let us now add another typed task createZip using the task type Zip to zip the contents from build/docs to build/dist (again this destination directory may not exists already)
      task createZip(type: Zip) {
        from "build/docs"
        archiveFileName = "dccs.zip"
        destinationDirectory = file("build/dist")
      }
      ~/hello-gradle $ gradle createZip
      
      BUILD SUCCESSFUL in 1s
      1 actionable task: 1 executed
      ~/hello-gradle $ tree build
      build
      β”œβ”€β”€ dist
      β”‚   └── dccs.zip    # created zip file
      └── docs
          β”œβ”€β”€ readme.md
          └── sd
              └── read_style.md
      • We can make the createZip task to depend on copyDocs task by calling dependsOn method β€” then if we invoke createZip task that will first execute the copyDocs task and then createZip task:
      task copyDocs(type: Copy) {
          from "md"           // Call the `from` method of Copy type task
          into "build/docs"   // Call the `into` method
          include "**/*.md"   // ANT like include patterns
                              // .. `**` -> scan all sub-dirs
      
          includeEmptyDirs = false // because by default empty dirs are copied
      }
      
      task createZip(type: Zip) {
          from "build/docs"
          archiveFileName = "dccs.zip"
          destinationDirectory = file("build/dist")
          dependsOn copyDocs
      }
      ~/hello-gradle $ gradle createZip
      
      BUILD SUCCESSFUL in 1s
      2 actionable tasks: 2 up-to-date

1.2.3. Build lifecyle phases

   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚Initializationβ”‚   --> Evaluates the settings.gradle file
   β”‚    Phase     β”‚       and sets up the build
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”š
           β”‚
           β”‚
           β•½
           β–Ό
   β”Œβ”€β”€β”€β”€β”€-────────┐
   β”‚Configuration β”‚   --> Parses and evaluates the build script(s)  
   β”‚    Phase     β”‚       and runs the configuration logic like
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       assigning values to properties or
           β”‚              calling task methods as exposed by the 
           β”‚              APIs
           β•½
           β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Execution   β”‚   --> Execute the task actions in correct order
   β”‚    Phase     β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
// configuration code

task helloGradleTask {
  // configuration code

  doFirst {
    // execution code
  }

  doLast {
    // execution code
  }
}
  • We can add to a task as many actions we want using doFirst or doLast
task multiActionTask {
    // Initial declaration of task containing the first & last actions 
    doFirst {
        println "[2] Initially declared FIRST action"
    }

    doLast {
        println "[3] Initially declared LAST action"
    }
}

// Additive doFirst closures are inserted at the BEGINNING of the list
// of actions 
multiActionTask.doFirst {
    println '[1] another doFirst'
}
// Additive doFirst closures are inserted at the BEGINNING of the list
// of actions 
multiActionTask.doFirst {
    println '[0] another doFirst'
}
// Additive doLast closures are inserted at the END of the actions list
multiActionTask.doLast {
    println '[4] another doLast'
}
// Additive doLast closures are inserted at the END of the actions list
multiActionTask.doLast {
    println '[5] another doLast'
}
~/hello-gradle $ ./gradlew multiActionTask

> Task :multiActionTask
[0] another doFirst
[1] another doFirst
[2] Initially declared FIRST action
[3] Initially declared LAST action
[4] another doLast
[5] another doLast

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

1.2.4. Plugins

Goals of Plugins

  • Avoid repetitive code
  • Make build logic more maintainable
  • Poivide reusable functionality across projects

Types of Plugins

  1. Script plugins

    • Just another build script that can be included in the main build.gradle script
                 /------ includes ----> publishing.gradle
                /
    build.gradle
                \
                 \------ includes ----> deployment.gradle
    
    
    • Useful to split the build logic in multiple scripts for better maintainability
  2. Binary plugins

    • Used for more complex logic
    • Implemented as class files; bundled as JAR files
                 /------ includes ----> Gradle core plugins
                /
    build.gradle
                \
                 \------ includes ----> Community pluginss
    
    

1.2.5. A demo exercise

  1. Create a tsk of type Tar bundling text files
  2. Rename the file extension from .text to .txt
  3. Should not include empty directories
  4. Compressed by Gzip and produced in builds or distributions
  5. The task should print message before start and end of archiving
  6. Create another task that depends on this task

We will consider below directory for this exercise:

$ cd archive-demo
~/archive-demo $ tree
.
β”œβ”€β”€ customers.csv
└── src
    β”œβ”€β”€ bin
    β”‚   └── 1.0.0.dat
    β”‚   └── versions.text
    β”œβ”€β”€ bugs.text
    β”œβ”€β”€ inputs
    └── readme.text

In the final archive we do not expect the empty inputs directory.

STEPS

  1. Let us first create the wrapper:
~/archive-demo $ gradle wrapper

BUILD SUCCESSFUL in 10s      
1 actionable task: 1 executed
  1. Create the build.gradle file:
~/archive-demo $ vi build.gradle
// We will use (our) script plugin - archiver.gradle
apply from: 'archiver.gradle'   // This applies the script plugin
  1. Create the script plugin file used by the build.gradle file:
~/archive-demo $ vi archiver.gradle
// We will use `base` plugin calling the method apply
// .. This plugin provides some naming conventions, for example,
// .. the output archive's directory is build/distributions
apply plugin: 'base'  // eqv. apply(plugin: 'base')

task archiveTextFiles(type: Tar) { // Use Tar type task
    from 'src'              // Copy the input files from 'src' dir
    into 'text'              // Output of copy

    include '**/*.text'      // Copy  .text files including sub-dirs
    includeEmptyDirs = false        // Exclude empty dirs

    rename '(.+).text', '$1.txt'    // rename .text -> .txt

    compression = Compression.GZIP  // `Compression.GZIP` is an enum

    // We do not need below 2 line because base plugin provides
    // these confgurations
    //  archiveFileName = "dccs.zip"
    //  destinationDirectory = file("build/dist")

    // Render output messages
    doFirst {
        println 'Starting archiving task'
    }

    doLast {
        println 'Archive complete. Good bye!'
    }
}

task createTextArchive() {
    // This is just an AGGREGATOR task
    dependsOn archiveTextFiles
}
  1. Run the aggregator task createTextArchive:
~/archive-demo $ ./gradlew createTextArchive --console=verbose

> Task :archiveTextFiles
Starting archiving task 
Archive complete. Good bye!

> Task :createTextArchive

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
  1. Verify the output
~/archive-demo $ tree
.
β”œβ”€β”€ archiver.gradle
β”œβ”€β”€ build
β”‚   └── distributions
β”‚       └── archive-demo.tgz      # Created output archive
β”œβ”€β”€ build.gradle
β”œβ”€β”€ customers.csv
β”œβ”€β”€ gradle
β”‚   └── wrapper
β”‚       β”œβ”€β”€ gradle-wrapper.jar
β”‚       └── gradle-wrapper.properties
β”œβ”€β”€ gradlew
β”œβ”€β”€ gradlew.bat
└── src
    β”œβ”€β”€ bin
    β”‚   β”œβ”€β”€ 1.0.0.dat
    β”‚   └── versions.text
    β”œβ”€β”€ bugs.text
    β”œβ”€β”€ inputs
    └── readme.text

7 directories, 12 files
~/archive-demo $ tar -tzvf build/distributions/archive-demo.tgz
drwxr-xr-x 0/0               0 2021-06-20 15:03 text/
drwxr-xr-x 0/0               0 2021-06-20 14:56 text/bin/
-rw-r--r-- 0/0               0 2021-06-20 14:56 text/bin/versions.txt
-rw-r--r-- 0/0               0 2021-06-20 14:06 text/bugs.txt
-rw-r--r-- 0/0               0 2021-06-20 14:05 text/readme.txt

2. Creating Java Project in IntelliJ (Community 2020.1) without using Gradle

  • Create Java project to emulate a very basic Sensor Event

    1. File -> New -> Project
    2. Select Java
    3. Give a name GradleLab GradleLab_NoGradle
    4. Create a Package lab.java.gradle.kaushik under the created project

    Project Structure

    GradleLab_NoGradle
    β”œβ”€β”€ .idea
    β”œβ”€β”€ src
        └── lab.java.gradle.kaushik
            └── SensorEvent.java 
    
  • This project is a simple Java project that does not use any external dependency - it just prints the SensorEvent object using its toString method:

      ...
      @Override
      public String toString() {
          return "SensorEvent{" +
                  "id='" + id + '\'' +
                  ", capability='" + capability + '\'' +
                  ", value='" + value + '\'' +
                  '}';
      }
    
      public static void main(String[] args) {
          SensorEvent sensorEvent = new SensorEvent();
          sensorEvent.setId("100-101-102-103");
          sensorEvent.setCapability("motionSensor");
          sensorEvent.setValue("active");
    
          System.out.println(sensorEvent.toString());
      }
  • Now let us introduce an external dependency (to Google GSON library) to convert this simple sensor event to a JSON string

    1. Create a directory libs under the root folder
    2. Download Google GSON library from Maven repository on the Internet and place the downloaded jar to libs directory

      (Important) Right click the libs folder in IntelliJ and select "Add as Library..."

    Project Structure

    GradleLab_NoGradle
    β”œβ”€β”€ .idea
    β”œβ”€β”€ libs
    β”‚   └── gson-2.8.0.jar
    └── src
        └── lab.java.gradle.kaushik
            └── SensorEvent.java 
    
  • With this done we can now use the GSON library in our Java project

    • We need to import some GSON specific code, ex.:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
...
    public static void main(String[] args) {
        ...
        System.out.println(sensorEvent.toString());

        Gson json = new GsonBuilder().create();
        String jsonEventString = json.toJson(sensorEvent);
        System.out.println(jsonEventString);
    }

[Ref. https://github.com/kaushikdas/InterestingCodes/tree/master/Java/GradleLab_NoGradle]

2.1. Problems with this approach

  • Need to manually find and download the jar
  • Manage updates if the version of the GSON library changes

3. Java Plugin for Gradle

  • Makes Gradle "Java aware"
  • Defines a set of tasks for Java builds (many kinds of plugins available)
  • Applying a plugin
    • Write apply plugin: '<plugin name>' in build.gradle β€” apply plugin: 'java'
  • Main tasks added
    • Clean
    • Compile
    • Assemble
    • Test

3.1. Add Gradle to Simple Java Project (SensorEvent)

  1. Create a directory structure as expected the java plugin

    $ mkdir -p GradleLab_GsonGradle/src/main/java
    
    $ mkdir -p GradleLab_GsonGradle/src/main/resources
  2. In IntelliJ open the directory GradleLab_GsonGradle using menu File -> Open

  3. Important Right click the java folder -> Mark Directory as -> Sources Root

  4. Right click the java directory -> New -> Java Class -> lab.java.gradle.kaushik.SeonsorEvent and add code to SensorEvent.java

  5. Add build.gradle file at the root of project and add java plugin to that as below:

    plugins {
      id 'java'
    }

    Directory structure at this point will be like:

    $ tree GradleLab_GsonGradle
    GradleLab_GsonGradle
    β”œβ”€β”€ build.gradle
    └── src
        └── main
            β”œβ”€β”€ java
            β”‚   └── lab
            β”‚       └── java
            β”‚           └── gradle
            β”‚               └── kaushik
            β”‚                   └── SensorEvent.java
            └── resources
    
    8 directories, 2 files
  6. Generate gradle wrapper:

    $ gradle wrapper
    
    BUILD SUCCESSFUL in 1s       
    1 actionable task: 1 executed

    We will have new files and directories like below:

    GradleLab_GsonGradle
    β”œβ”€β”€ build.gradle
    β”œβ”€β”€ gradle                            # new directory
    β”‚   └── wrapper
    β”‚       β”œβ”€β”€ gradle-wrapper.jar
    β”‚       └── gradle-wrapper.properties
    β”œβ”€β”€ gradlew                           # new file
    β”œβ”€β”€ gradlew.bat                       # new file
    └── src
        └── main
            β”œβ”€β”€ java
            β”‚   └── lab
            β”‚       └── java
            β”‚           └── gradle
            β”‚               └── kaushik
            β”‚                   └── SensorEvent.java
            └── resources
    
    10 directories, 6 files

    We can now see the tasks that the java plugin brings using gradlew tasks:

    $ ./gradlew tasks--all
    
    > Task :tasks
    
    ------------------------------------------------------------
    Tasks runnable from root project
    ------------------------------------------------------------
    
    Build tasks
    -----------
    assemble - Assembles the outputs of this project.
    build - Assembles and tests this project.
    buildDependents - Assembles and tests this project and all projects that depend on it.
    buildNeeded - Assembles and tests this project and all projects it depends on.
    classes - Assembles main classes.
    clean - Deletes the build directory.
    jar - Assembles a jar archive containing the main classes.
    testClasses - Assembles test classes.
    
    Build Setup tasks
    -----------------
    init - Initializes a new Gradle build.
    wrapper - Generates Gradle wrapper files.
    
    Documentation tasks
    -------------------
    javadoc - Generates Javadoc API documentation for the main source code.
    
    Help tasks
    ----------
    buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleLab_GsonGradle'.
    components - Displays the components produced by root project 'GradleLab_GsonGradle'. [incubating]
    dependencies - Displays all dependencies declared in root project 'GradleLab_GsonGradle'.
    dependencyInsight - Displays the insight into a specific dependency in root project 'GradleLab_GsonGradle'.
    dependentComponents - Displays the dependent components of components in root project 'GradleLab_GsonGradle'. [incubating]
    help - Displays a help message.
    model - Displays the configuration model of root project 'GradleLab_GsonGradle'. [incubating]
    outgoingVariants - Displays the outgoing variants of root project 'GradleLab_GsonGradle'.
    projects - Displays the sub-projects of root project 'GradleLab_GsonGradle'.
    properties - Displays the properties of root project 'GradleLab_GsonGradle'.
    tasks - Displays the tasks runnable from root project 'GradleLab_GsonGradle'.
    
    Verification tasks
    ------------------
    check - Runs all checks.
    test - Runs the unit tests.
    
    Other tasks
    -----------
    compileJava - Compiles main Java source.
    compileTestJava - Compiles test Java source.
    prepareKotlinBuildScriptModel
    processResources - Processes main resources.
    processTestResources - Processes test resources.
    
    Rules
    -----
    Pattern: clean<TaskName>: Cleans the output files of a task.
    Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
    Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 executed
  7. Compiling Java source code. There are 2 tasks, compileJava and processResources that we can use.

    • compileJava: This task will compile any .java file inside src/main/java directory and compiled class files will be generated under build/classes directory.

      $ ./gradlew compileJava --console=verbose
      > Task :compileJava
      
      BUILD SUCCESSFUL in 3s
      1 actionable task: 1 executed
    • processResources: This task copies the resource files to build directory. For our thos basic project we do not have any resources so far:

      $ ./gradlew processResources --console=verbose
      > Task :processResources NO-SOURCE
      
      BUILD SUCCESSFUL in 1s
    • Instead of using compileJava and processResources individually everytime, we can use the aggregator task classes that perform both the tasks:

      $ ./gradlew classes --console=verbose
      > Task :compileJava
      > Task :processResources NO-SOURCE
      > Task :classes
      
      BUILD SUCCESSFUL in 1s
      1 actionable task: 1 executed
    • We can set more compiler options inside build.gradle. For example we can set source nad target compatibility of our project using the java extension created by the java plugin. We can also set the compiler option to stop compilation in case of any warning

      java {
          sourceCompatibility = JavaVersion.VERSION_11
          targetCompatibility = JavaVersion.VERSION_11
      }
      
      compileJava {
          options.compilerArgs << '-Werror'
      }
  8. Packaging a JAR file using the jar task of java plugin. The jar task as such does not need any special properties to be set. But we can configire some useful properties of the jar task, (say) to give a meaningful name to the jar, to make the jar executable.

  • java plugin's jar task generates the jar under build/libs

    plugins {
        id 'java'
    }
    
    version = '1.0-SNAPSHOT'
    
    java {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    
    compileJava {
        options.compilerArgs << '-Werror'
    }
    
    jar {
        // Give a meaningful name to our jar, else it will be the root
        // project name, by default. In case the version property is
        // defined, the jar will also have the version included in the
        // jar name
        archiveBaseName = 'SensorEvent'
    
        // Make the jar executable
        manifest {
            attributes "Main-Class": 'lab.java.gradle.kaushik.SensorEvent'
        }
    }

    Build the jar

    $ ./gradlew clean --console=verbose
    > Task :clean
    
    BUILD SUCCESSFUL in 2s
    1 actionable task: 1 executed
    
    $ ./gradlew jar --console=verbose  
    > Task :compileJava
    > Task :processResources NO-SOURCE
    > Task :classes
    > Task :jar
    
    BUILD SUCCESSFUL in 2s
    2 actionable tasks: 2 executed

    Run the jar

    $ java -jar .\build\libs\SensorEvent-1.0-SNAPSHOT.jar
    SensorEvent{id='100-101-102-103', capability='motionSensor', value='active'}

    The Java plugin can produce both library and executable application type of artifacts

  1. The Java Application plugin
  • It helps to run the application directly from the build (using the run task that it provides) or to produce an executable distribution. The important tasks that ot provides are:

    • the run task that executes the main class of the program
    • the installDist task that generates operating system specific scripts suitable for starting the program
    • the distZip or distTar task that bundle the distribution as zip or tar file
  • Let us apply the application plugin

    STEPS

    1. Change the build.gradle file to apply the application plugin
    plugins {
      id 'java'
      id 'application'    // apply application plugin
                          // IMP: It needs mainClass property to be set
    }
    
    version = '1.0-SNAPSHOT'
    
    java {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    
    compileJava {
        options.compilerArgs << '-Werror'
    }
    
    application {
        // Set mandatory mainClass property for application plugin
        mainClassName = 'lab.java.gradle.kaushik.SensorEvent'
    }
    
    jar {
        // Give a meaningful name to our jar, else it will be the root
        // project name, by default. In case the version property is
        // defined, the jar will also have the version included in the
        // jar name
        archiveBaseName = 'SensorEvent'
    
        // Make the jar executable
        manifest {
            attributes "Main-Class": 'lab.java.gradle.kaushik.SensorEvent'
        }
    }
    
    1. Run the run task
    $ ./gradlew run --console=verbose
    > Task :compileJava
    > Task :processResources NO-SOURCE
    > Task :classes
    
    > Task :run
    SensorEvent{id='100-101-102-103', capability='motionSensor', value='active'}
    
    BUILD SUCCESSFUL in 2s
    2 actionable tasks: 2 executed
    1. Run the installDist task
    $ ./gradlew installDist --console=verbose
    > Task :compileJava
    > Task :processResources NO-SOURCE
    > Task :classes
    > Task :jar
    > Task :startScripts
    > Task :installDist
    
    BUILD SUCCESSFUL in 2s
    4 actionable tasks: 4 executed
    • This will generate OS specific executables to run the project under build/install/bin
      $ tree build/install
      build/install
      └── GradleLab_GsonGradle
          β”œβ”€β”€ bin
          β”‚   β”œβ”€β”€ GradleLab_GsonGradle      # x-Nix specific executable
          β”‚   └── GradleLab_GsonGradle.bat  # For Windows
          └── lib
              └── SensorEvent-1.0-SNAPSHOT.jar
      Run the executable
      $ ./build/install/SensorEvent/bin/GradleLab_GsonGradle
      SensorEvent{id='100-101-102-103', capability='motionSensor', value='active'}
      
      • Note SensorEvent-1.0-SNAPSHOT.jar is also generated because this task also runs the jar task as indiciated in the output

      • To change the output file and directory names (by default as per the root directory ,i.e., GradleLab_GsonGradle in this case) we need to update the rootProject.name in seetings.gradle file

        # Create settings.gradle at the root level
        $ cat ./settings.gradle
        rootProject.name = 'SensorEvent'

        Now the name will reflect the root project name from settings.gradle file:

        $ tree build/install
        build/install
        └── SensorEvent               # from rootProject.name
            β”œβ”€β”€ bin
            β”‚   β”œβ”€β”€ SensorEvent       # from rootProject.name
            β”‚   └── SensorEvent.bat   # from rootProject.name
            └── lib
                └── SensorEvent-1.0-SNAPSHOT.jar
    1. Run distTar and distZip tasks which will create the distributable packages under build/distributions
    $ ./gradlew distTar distZip --console=verbose
    > Task :compileJava UP-TO-DATE
    > Task :processResources NO-SOURCE
    > Task :classes UP-TO-DATE
    > Task :jar UP-TO-DATE
    > Task :startScripts UP-TO-DATE
    > Task :distTar
    > Task :distZip
    
    BUILD SUCCESSFUL in 1s
    5 actionable tasks: 2 executed, 3 up-to-date
    $ tree build/distributions/
    build/distributions/
    β”œβ”€β”€ SensorEvent-1.0-SNAPSHOT.tar  # Use proerties for jar task
    └── SensorEvent-1.0-SNAPSHOT.zip  # Use proerties for jar task

3.2. Add Gradle to Simple Java Project (SensorEvent)

This approach does NOT follow directory structure convention expected by the java plugin

  1. Create the Java as described Section 2

  2. Create a new file build.gradle under the root directory

    Project Structure

    GradleLab_GsonGradle
    β”œβ”€β”€ .gradle
    β”œβ”€β”€ .idea
    └── src
        └── lab.java.gradle.kaushik
            └── SensorEvent.java 
    
  3. Apply Java plugin:

    apply plugin: 'java'

    This steps enables the simple project with gradle. To verify, we can go to Terminal in IntelliJ (or open command prompt and go to this project root path) and run below command and see that it shows various steps under the build task (notice Task :compileJava NO-SOURCE indicating there are no sources added to compile yet:

    ❯ gradle build --console plain
    > Task :compileJava NO-SOURCE
    > Task :processResources NO-SOURCE
    > Task :classes UP-TO-DATE
    > Task :jar UP-TO-DATE
    > Task :assemble UP-TO-DATE
    > Task :compileTestJava NO-SOURCE
    > Task :processTestResources NO-SOURCE
    > Task :testClasses UP-TO-DATE
    > Task :test NO-SOURCE
    > Task :check UP-TO-DATE
    > Task :build UP-TO-DATE
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 up-to-date
    ❯ 
    

    In older version of gradle we could just run gradle build to get the output like above. But with new gradle version (my version is 6.4.1) it gives very short output:

    ❯ gradle build
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 up-to-date
    <-------------> 0% WAITING
    ❯ 
  4. Add source code declaration to build.gradle

    sourceSets {                   // Add source set 
       main {                      // For main Java code (and not Test code, we don't have any yet)
           java.srcDirs = ['src']  // Add directory containing source files...
                                   // ... add multiple dirs (if any) separated by commas 
       }
    }
  5. Add external library dependency

    dependencies {
        compile 'com.google.code.gson:gson:2.8.6' // compile config
                                                  // '<co_name.artifact_name.artifact_version>
    }

    This compile configuration for dependencies is deprecated now (with my gradle version 5.4.1) and instead implementation is the recommended configuration. The compile configuration adds the dependecies as shown below with the command gradle dependencies.

    ❯ gradle dependencies
    
    > Task :dependencies
    
    ------------------------------------------------------------
    Root project
    ------------------------------------------------------------
    
    annotationProcessor - Annotation processors and their dependencies for source set 'main'.
    No dependencies
    
    apiElements - API elements for main. (n)
    No dependencies
    
    archives - Configuration for archive artifacts. (n)
    No dependencies
    
    compileClasspath - Compile classpath for source set 'main'.
    \--- com.google.code.gson:gson:2.8.6
    
    compileOnly - Compile only dependencies for source set 'main'. (n)
    No dependencies
    
    default - Configuration for default artifacts. (n)
    No dependencies
    
    implementation - Implementation only dependencies for source set 'main'. (n)
    No dependencies
    
    runtimeClasspath - Runtime classpath of source set 'main'.
    \--- com.google.code.gson:gson:2.8.6
    
    runtimeElements - Elements of runtime for main. (n)
    No dependencies
    
    runtimeOnly - Runtime only dependencies for source set 'main'. (n)
    No dependencies
    
    testAnnotationProcessor - Annotation processors and their dependencies for source set 'test'.
    No dependencies
    
    testCompileClasspath - Compile classpath for source set 'test'.
    \--- com.google.code.gson:gson:2.8.6
    
    testCompileOnly - Compile only dependencies for source set 'test'. (n)
    No dependencies
    
    testImplementation - Implementation only dependencies for source set 'test'. (n)
    No dependencies
    
    testRuntimeClasspath - Runtime classpath of source set 'test'.
    \--- com.google.code.gson:gson:2.8.6
    
    testRuntimeOnly - Runtime only dependencies for source set 'test'. (n)
    No dependencies
    
    A web-based, searchable dependency report is available by adding the --scan option.
    
    Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
    Use '--warning-mode all' to show the individual deprecation warnings.
    See https://docs.gradle.org/6.4.1/userguide/command_line_interface.html#sec:command_line_warnings
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 executed
    ❯
  6. Add repository declaration

    repositories {
        mavenCentral()
    }

    With this we can run the command gradle build to build our project (and create build\libs\GradleLab_GsonGradle.jar) but when we try to do "Run SensorEvent.Main()" by right clicking on the SensorEvent.java file inside IntelliJ it fails to execute. We need to follow below steps to run the project (from terminal using gradle run).

  7. Add java plugin in the build.gradle (which applies the java and distribution plugins implicitly so we may even retain that - no problem)

    apply plugin: 'java'
    apply plugin: 'application'    // This provides 'run' task
  8. Specify the main class name for the jar in the build.gradle

    mainClassName = 'lab.java.gradle.kaushik.SensorEvent'
  9. Build and run the project from Terminal:

    ❯ gradle clean
    
    Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
    Use '--warning-mode all' to show the individual deprecation warnings.
    See https://docs.gradle.org/6.4.1/userguide/command_line_interface.html#sec:command_line_warnings
    
    BUILD SUCCESSFUL in 1s
    1 actionable task: 1 executed
    ❯ gradle build
    ...
    BUILD SUCCESSFUL in 1s
    5 actionable tasks: 5 executed
    ❯ gradle run
    
    > Task :run
    SensorEvent{id='100-101-102-103', capability='motionSensor', value='active'}
    {"id":"100-101-102-103","capability":"motionSensor","value":"active"}
    
    ...
    BUILD SUCCESSFUL in 1s
    2 actionable tasks: 1 executed, 1 up-to-date
    ❯ 

4. Creating Gradle Project in IntelliJ (Community 2020.1)

  1. File -> New -> Project
  2. Select Gradle and Java (under "Additional Libraries and Frameworks:")
  3. (Optional Select Project SDK (1.8 - java version)
  4. Next
  5. Give Name and Location
  6. (Optional) Change Artifact Coordinates
  7. Click Finish

Sample build.gradle for a Gradle project created using above steps:

plugins {
    id 'java'
}

group 'kaushikd.gradleexps'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

4.1. Using SLF4J

  1. Add imports:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
  1. Add Gradle dependency (without this the project will show error):
dependencies {
    ...
    compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'
    ...
}

and do "Load Gradle Changes" to apply the dependency change (this will add Gradle:org.slf4j-api:1.7.30 to External Libraries). 3. This dependency will just allow to use slf4j

    ...
    Logger logger = LoggerFactory.getLogger(GaussianRandom.class);
    ...
    logger.info("GaussianRandom");

but no logs will come because and we will get below warnings:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation

SLF4J's plug-in mechanism looks for the StaticLoggerBinder class and ends up with a no operation implementation that discards all log messages. Just to avoid the above warnings we can add below dependency to build.gradle to bind slf4j to slf4j-nop [http://www.slf4j.org/manual.html] (note that we will still not get any logs)

compile group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.30'
  1. To get logs using logback-classic as underlying logger we can use below dependency to build.gradle :
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'

Using log4j2

  1. (Remove any previously added dependency for logback-classic or slf4j-nop and) Add below dependency to build.gradle to bind slf4j to log4j2:
compile group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.13.3'
  1. Add log4j2 configuration properties file under src/main/resources. A sample log4j2.properties file is shown as below:
status = error
name = PropertiesConfig

filters = threshold

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug

appenders = console

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

rootLogger.level = debug
rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = STDOUT

With these changes we should see the logs to appear.

4.2. Creating Executable JAR

Add below code to build.gradle to modify the jar task to create an executable JAR to add Main-Class attribute to the manifest file:

jar {
    manifest {
        attributes "Main-Class": "kaushikd.java.exps.GaussianRandom"
    }
}

But this will generate an executable JAR without an dependencies. Therefore, if we run the executable jar we shall get error. For example, if we are using slf4j we shall get below error:

❯ java -jar D:\GitHub\InterestingCodes\Java\Gradle\build\libs\GradleExps-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
        at kaushikd.java.exps.GaussianRandom.main(GaussianRandom.java:8)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
        at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 1 more

To solve this problem we need to create a Uber JAR that will also include its dependencies.

4.3. Creating Uber JAR

Modify jar task in build.gradle to include below code that will ensure that dependencies are also included in the created JAR:

jar {
    manifest {
        attributes "Main-Class": "kaushikd.java.exps.GaussianRandom"
    }

    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

Now if we see the JAR content, we shall see that the JAR now includes all required dependencies:

❯ jar tf D:\GitHub\InterestingCodes\Java\Gradle\build\libs\GradleExps-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
kaushikd/
kaushikd/java/
kaushikd/java/exps/
kaushikd/java/exps/GaussianRandom.class
log4j2.properties
META-INF/DEPENDENCIES
META-INF/LICENSE
META-INF/NOTICE
META-INF/maven/
META-INF/maven/org.apache.logging.log4j/
META-INF/maven/org.apache.logging.log4j/log4j-slf4j-impl/
META-INF/maven/org.apache.logging.log4j/log4j-slf4j-impl/pom.properties
META-INF/maven/org.apache.logging.log4j/log4j-slf4j-impl/pom.xml
org/
org/apache/
org/apache/logging/
org/apache/logging/slf4j/
...

and we should be able to run the JAR.

5. A Super Crash Course on Groovy

  • Groovy is also a JVM language that gets compiled to byte code

5.1. Groovy on IntelliJ

  • Go to Tools -> Groovy Console
// We can also write...
// ...   System.out.println("Hello world");
// ... but that is not required
// ... 1. System.out is automatically available
// ... 2. No need of ; at the end no
// ... 3. Even () is not needed for a method call with 1 arg
println "Hello world!"
  • Using classes

Code

class DemoClass {
    void doSomework(Closure closure) {
        /*
        closure can be considered as piece of code being passed
        (similar to Java lambda) we we can invoke that code as below
         */
        closure.call()
    }
}

obj = new DemoClass()  // again no need of any ending ;

/* Now we invoke doSomework using obj.doSomework method by passing
   some piece of code inside {} */
obj.doSomework {
    println new Date() // no need to import anything to use Date
}

Output

Fri Jul 24 20:52:05 IST 202

6. Gradle Project Object Model

  • Project Object Model
    • A Java object built by Gradle
    • Represents different things in our project
    • Used by Gradle to build projects
    • Not a static model: Accessible to developers for modfication
    • Project object
      • 1-to-1 mapped to a build.gradle
      • preject: corresponding object reference
    • Task object
      • Project is a collection of tasks (compile, create jar file etc.)
      • Task is a collection of (smaller) actions performed by gradle
        • Usually 1-to-1 mapping between task and action
        • We can list the tasks for a project

6. Gradle Project Object Model

  • Project Object Model
    • A Java object built by Gradle
    • Represents different things in our project
    • Used by Gradle to build projects
    • Not a static model: Accessible to developers for modfication
    • Project object
      • 1-to-1 mapped to a build.gradle
      • preject: corresponding object reference
    • Task object
      • Project is a collection of tasks (compile, create jar file etc.)
      • Task is a collection of (smaller) actions performed by gradle
        • Usually 1-to-1 mapping between task and action
        • We can list the tasks for a project

6.1. project object in `build.gradle

project.depedencies {  // depedencies ≑ project.depedencies

}

project.buildDir = "..." // this will change the build directory...
                         // ...from the default value of build folder
                         // ...under the project root

List of attaributes under project object can be found by typing the command gradle properties in the terminal. More details can be found at Gradle official documentation website.

6.2. task object

  • gradle tasks
    • Shows all the tasks available
    • Tasks are grouped logically into different categories

7. Dependency Mangement

  • gradle dependencies: Shows all dependencies of a project
    • gradle dependencies --configuration compile: Shows dependencies for a requested configuration (compile in this case)
    • Shows trasitive dependencies
    \--- org.apache.logging.log4j:log4j-slf4j-impl:2.13.3
     +--- org.slf4j:slf4j-api:1.7.25
     +--- org.apache.logging.log4j:log4j-api:2.13.3
     \--- org.apache.logging.log4j:log4j-core:2.13.3
          \--- org.apache.logging.log4j:log4j-api:2.13.3
    • Gradle automatically resolves conflicting depedencies w.r.t. versions of libraries requied by different dependencies (say, by upgrading dependency of one to a higher version as included by another depedency)
  • HTML report for dependency
    • Requires another plugin project-report in build.gradle
    apply plugin: 'project-report'
    • Run gradle command gradle htmlDependencyReport
      • This is generate an HTML report under a specific path as indicated in its execution output

7.1. Creating a library module

  • WIP
⚠️ **GitHub.com Fallback** ⚠️