Gradle

Gradle is an automation tool that can be used to build 🏭, test πŸ§ͺ, and deploy πŸš€ your project. It's an alternative created to address problems with other tools such as Maven or Ant.

Gradle uses a domain-specific language (DSL) based on the Groovy programming language for defining build scripts. It's possible to use Kotlin instead of Groovy.

It's commonly used with Java and Kotlin projects.

You can download Gradle here. It's only needed to initialize Gradle wrapper which is recommended to use. It allows you to use separate Gradle versions per project and with it, others don't need to install Gradle to build/test/... the project.

$ cd my_project
# generate the "gradle" folder (config + downloader)
# generate gradlew (Unix) gradlew.bat (Windows)
# generate .gradle (gradle binaries)
$ gradle wrapper
$ ./gradlew wrapper --gradle-version 7.2 # change version

⚠️ With Version Control, commit all files aside from .gradle folder.


Gradle components

Project

For Gradle, a project is an application or a library that we are building. A project may be composed of multiple subprojects.

See also: settings.gradle.

Build file

The build file is where we define how a project is built. It contains tasks, plugin, and dependencies.

See also: build.gradle and println "$buildDir".

Tasks

A task is a simple action such as "compiling the code".

➑️ Use ./gradlew task to run the task "task".

Plugins

Gradle plugins contain pre-defined tasks and additional features simplifying build file and task creation.

Dependencies

These are the other projects that we need to import to build ours.


Groovy Build file

Add plugins

You can import plugins in the plugins block at the top.

// idea, java, java-library, maven-publish
// application...
plugins {
    id 'xxx'
    id 'xxx' version 'xxx'
}

Project metadata

group = 'org.example'
version = '1.0-SNAPSHOT'
description = 'Some description [...]' // optional

Project repositories

repositories {
    mavenCentral()
}

Add dependencies

dependencies {
    implementation 'xxx'     // needed to compile + run
    testImplementation 'xxx' // needed for tests
    // import 'xxx', but do not import its dependency
    implementation ('xxx') {
        exclude group: 'org.json', module: 'json'
    }
}

Custom tasks

tasks.register('hello_world') {
    doFirst {
        println 'Hello, World!'
    }
    doLast {
        println 'Bye, World!'
    }
}

Gradle for Java

Java plugins

plugins {
    id 'java'
}

Java dependencies

You can find dependencies at mvnrepository.

dependencies {
    // use JUnit 5
    testImplementation platform('org.junit:junit-bom:5.9.1')
    testImplementation 'org.junit.jupiter:junit-jupiter'
}

Java Compile Options

Add the block below to set Java compiler and its options.

tasks.withType(JavaCompile).configureEach {
    // Compiler options
    options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
    options.encoding = "UTF-8"
    // Ask the compiler to target a SDK
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

The xxxCompatibility attributes do not enforce that the compiler has the target version, e.g., JDK 18 can compile code targeting JDK 17. To enforce the use of JDK 17 when compiling, use:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

➑️ If you only need to set the encoding, it's simpler to use compileJava.options.encoding = 'UTF-8'.

Java Tests

test {
    useJUnitPlatform()  // JUnit5
}

Java folder structure

Gradle separate source, and tests. Both have a resources folder.

  • src/main/java: your java files
  • src/resources/java: your resources (images/...)
  • src/test/java: your tests
  • src/resources/java: resources only for your tests

You can actually edit them however you want:

sourceSets {
    main.java.srcDirs = []
    main.java.srcDirs += []
    main.resources.srcDirs = []

    test.java.srcDirs = []
    test.resources.srcDirs = []
}

⚠️ From the code, to access a file that is inside the resources folder, you must use the ClassLoader Utilities, as we would in JAR files.

Java run

You can create a task run to use ./gradlew run and ./gradlew run --args="arg1 arg2 arg3".

tasks.register('run', JavaExec) {
    mainClass = 'org.example.Main' // set yours
    classpath = sourceSets.main.runtimeClasspath
}

// optional, set run options
tasks.withType(JavaExec).configureEach {
    systemProperty 'file.encoding', 'UTF-8'
}

Generate a jar

Add this to generate a jar with ./gradlew jar. Replace the org.example.Main with your Main class.

jar {
    from sourceSets.main.output
    manifest {
        attributes 'Main-Class': 'org.example.Main'
    }
}

➑️ The output is usually at build/libs/xxx-version.jar.

πŸš€ See also: shadow plugin.


gradle.properties

It will allow you to load some variables inside your build.gradle that are defined in .properties files.

This is useful for projects that need different versions of plugins or dependencies based on the target. We simply edit gradle.property.

# gradle.properties
myVariable=value
// build.gradle
print "$myVariable" // will print "value"

You may need to learn a bit about Groovy/Kotlin to write modular build files that use your variables.

Saliman plugin

If we often need to edit the configuration, we can use saliman plugin to easily swap from one .properties to another.

id("net.saliman.properties") version "1.5.2"

We will declare myVariable as the one determining which .property file we will load. You can easily change which file is loaded by changing the variable's value.

# gradle.properties
# load gradle-$myVariable.properties
propertiesPluginEnvironmentNameProperty=myVariable
myVariable=11

Currently, myVariable=11, so we will load gradle-11.properties.


Local dependencies

Local project

Assuming the root folder has a folder mylib with a build.gradle.

  • settings.gradle
include 'mylib' // load
  • build.gradle
dependencies {
    implementation project(':mylib')
}

Local jar

This code can be used to include a local jar:

repositories {
    // ...
    flatDir { dirs 'libs' }
}

dependencies {
    // libs/mylib-1.02.jar
    implementation 'com.example.mylib:mylib-1.02'
}

πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.

  • jitpack
  • sourceSets, resourcesSets, access to resources
application {
    // if you have a module
    mainModule.set('com.module.name')
    mainClassName = "com.a.package.Main"
}