Gradle Java Plugin

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

The Java plugin adds Java compilation, testing and packaging capabilities to a project.

plugins {
  id 'java'
}

The Java plugin also serves as the basis for many of the other JVM language Gradle plugins. The Java plugin builds upon a Java Base plugin.

However, it is the Java Library plugin that should be used by default with Java projects instead of Java plugin. Java Library plugin extends the Java plugin, so it offers all the features of the Java plugin, plus a set of additional ones:

Java Library Plugin

The Java plugin instantiates by default two source sets ("main" and "test"). Each of these source sets has an associated compile task: compileJava (which is the abbreviation of "compileMainJava") for the "main" source set and compileTestJava for the "test" source set. To compile the production and test code:

The Java plugin also adds a jar task that assembles a JAR file with the classes produced from the "main" source set code.

The default configuration of the source sets is convention-based, borrowing the directory layout from Maven projects. As such, if those conventions are followed, generally not much needs to be added to the build script to get a useful build.

Concepts

Source Set

https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html

A source set is a logical grouping of source files and resources, typically associated on the base of their purpose: application production code, test code, etc. Each source set contains sources and resources from typically one, but possibly more directories, which are listed as part of the source set. The files that form a source set don't have to be located in the same directory. The source set concept was introduced by the Java plugin. A source set directory has subdirectories for java sources, and other types of sources. The Java plugin instantiates by default two source sets: main and test. They correspond to the conventional directories from "src":

.
└─ src
   ├─ main                  ← source set "main"
   │    ├─ java             ← the java subtree of the "main" source set
   │    │    └─ playground
   │    │        └─ Sample.java
   │    └─ resources        ← the resource subtree of the "main" source set
   │
   └─ test                  ← source set "test"
        ├─ java             ← the java subtree of the "test" source set
        │    └─ playground
        │        └─ SampleTest.java
        └─ resources        ← the resource subtree of the "test" source set

Aside from the source files and resources, the source set also keeps track of various classpaths, including any dependencies, require the compile or run the code. It does that by maintaining a set of configurations corresponding to the compilation classpath, annotation processor classpath and runtime classpath. Each source set has associated an "<source-set-name>implementation" configuration. In case of the "main" source set, main is omitted and the configuration is named simply "implementation". Also see:

Configuration

Finally, the source set keeps track of where the compiled class files are placed, as a source set output.

The Java plugin automatically creates compilation tasks for each source set, naming them "compile<source-set-name>Java". In case of the "main" source set, the <source-set-name> is omitted and the task is called "compileJava".


SourceSet.png


Each source set is internally maintained in the runtime project hierarchy as child of the Java plugin. However, it can be configured via a top-level script blocks:

plugins {
  id 'java'
}
sourceSets {
  main {
    java {
      srcDirs = ['src/main/java']
    }
    resources {
      srcDirs = ['src/main/resources']
      excludes = ['**/something/**']
    }
  }
  test {
    java {
      srcDirs = ['src/test/java']
    }
    resources {
      srcDirs = ['src/test/resources']
      excludes = ['**/something/**']
    }
  }
}

More details on how to reconfigure existing source sets is available below in the section Source Set Customization.

Source Set Lifecycle

How is a source set configured during the project lifecycle? How are configurations injected?

Resource Files

Java projects typically include resource other than source files, such as property files, that may need processing - for example by replacing tokens within the files - and packaging them within the final JAR. Typical use cases for resource files are images, configuration files, localization data. The Java plugin handles this automatically by creating a dedicated task for each source set, called "process<source-set-name>Resources", or "processResources" for the main source set. The processing consists in copying files from src/<source-set>/resources to a directory that will be included in the project's JAR. This target directory will also be included in the runtime classpath of the tests.

Default Source Sets

The "main" Source Set

The main source set is conventionally associated with all source files found under src/main/java and all resource files from src/main/resources. The sources managed by the main source set are compiled by the compileJava task. The resources are processed by the processResources task. The main source set is associated with the "compileOnly", "runtimeOnly" and "implementation" configurations. The jar task packages the main source set compiled classes and resources.

└─ src
    └─ main
         ├─ java
         └─ resources

The "test" Source Set

The "test" source set is conventionally associated with all the source file under src/test/java. It contains test source code, which is compiled and executed using JUnit or TestNG. These are typically unit tests, but any test can be include in this source set as long as they all share the same compilation and runtime classpaths. The sources managed by the "test" source set are compiled by the compileTestJava task, and the tests are executed by the test task. The resources are processed by the processTestResources task. The test source set is associated with the "testCompileOnly", "testRuntimeOnly" and "testImplementation" configurations.

└─ src
    └─ test
         ├─ java
         └─ resources

Custom Source Set

New source sets can be defined, typically for one or both of the following reasons:

  • The source code should be keep separate from the existing source sets for aesthetics and manageability.
  • The source code requires a different, unique compilation or runtime classpaths.
  • The generated code should be handled differently than the code from main and test source set.

Java Toolchain

The toolchain options protect against problems with the project being built with different Java versions.

TODO: https://docs.gradle.org/current/userguide/toolchains.html.

Component

The plugin adds a "java" software component accessible via components.java. The JAR file produced by the jar task is the artifact associated with the Java component, and its dependencies are those of the runtime configuration.

Dependency Configurations

Default Configurations added by the Java Plugin

Tasks

compileJava

The "compileJava" task compiles the sources managed by the "main" source set. The task is backed by the JavaCompile type.

To change compiler options, configure the task as follows:

compileJava {
  options.incremental = true
  options.fork = true
  options.failOnError = false
}

For more details, see:

JavaCompile type

compileTestJava

The "compileTestJava" task compiles the sources managed by the "test" source set. The task is backed by the JavaCompile type, and can be used to change compilation options for test compilation. The syntax is similar to the one shown above, just applied inside the "compileTestJava" script block.

processResources

The "processResources" task processes the resources managed by the main source set. The task is an implementation of the Copy type. The task will automatically copy any files in src/main/resources to a directory that will be included in the project's JAR. This target directory will also be included in the runtime classpath of the tests. For more details see:

Copy task

processTestResources

The "processTestResources" task processes the resources managed by the test source set. The task is an implementation of the Copy type. For more details see:

Copy task

jar

Bytecode JAR

Assembles the JAR file from the classes produced by the "main" source set source files and "main" source set resources. No configuration is needed for a standard Java project. The task produces a single JAR conventionally named:

<archiveBaseName>[-archiveAppendix]<-archiveVersion>[-archiveClassifier].<archiveExtension>

By default, that translates to:

<project>-<version>.jar

The JAR is placed in the build/libs project directory. It depends on the classes task.

No task configuration is necessary by default, but the task can be configured in build.gradle using a jar {...} configuration block:

jar {
  manifest.attributes 'Main-Class': 'playground.Main'
}

For example, the name of the JAR can be changed through configuration, as explained in the Custom JAR Name section below. The task is backed by the Jar type, and detailed configuration documentation is available in

Jar Task Type

The plugin creates by default a "thin" JAR, which only includes the project classes. A "fat" JAR, or an uber-JAR, is a self-sufficient archive which contains both classes and dependencies needed to run an application. To create a "fat" JAR, see:

Create a Fat JAR with Gradle

Note that Java Library plugin integrates the "jar" task with the standard lifecycle task assemble.

Also see:

Gradle Artifacts

JAR Customizations

Custom JAR Name
FAT JAR
Create a Fat JAR with Gradle

Source JAR

TODO.

javadoc

Generates Javadoc for the "main" source set classes.

test

The "test" task runs all the tests from the "test" source set (src/test/java). Note that Java Library plugin integrates the "test" task with the standard lifecycle task check. For more details, see:

Testing with Gradle Java Plugin

testClasses

classes

Lifecycle Tasks

Are these introduced by the Java plugin or they're also inherited?

buildDependents

buildNeeded

Inherited Tasks

The following tasks are lifecycle tasks inherited from the Base plugin.

assemble

Standard lifecycle task, inherited from the Base plugin. For the Java plugin, the 'assemble' task compiles the code and builds the JAR but does not run the tests. Other plugins, such as the War Plugin, add more artifacts to this task. More details:

assemble

build

Standard lifecycle task:

build

check

Standard lifecycle task:

check

clean

clean

Java Plugin Predefined Task Types

JavaCompile

JavaCompile exposed as "compileJava" task

Jar

Jar exposed as "jar" task

JavaExec

JavaExec

Properties

The Java plugin adds a number of properties to your project. These properties have default values which are usually sufficient to get started.

sourceCompatibility

'sourceCompatibility' defines the language version of the source code. See Use a Specific Java Version below.

targetCompatibility

'targetCompatibility' defines the minimum JVM version the code should be run on (the version of the bytecode the compiler generates). See Use a Specific Java Version below.

Customization

Various aspects related to Java compilation are set in the "java" script block:

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

Use a Specific Java Version

By default, Gradle will compile Java code to the language level of the JVM running Gradle. Since version 8, the Java compiler can be configured to produce bytecode for an older Java version, while making sure the code does not use any APIs from a more recent version.

This can be changed with the usage of Java toolchains.

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

The level can be changed at task level:

compileJava {
  options.release = 11
}

The legacy way to configure this is to set the "sourceCompatibility" and "targetCompatibility" properties at the top level of build.gradle file:

sourceCompatibility = 15
targetCompatibility = 11

If the toolchain is used, that makes it illegal to use sourceCompatibility and targetCompatibility. The target compatibility level reflects in the generated classes bytecode version. For more details, see:

Java Bytecode Version

Compiler Options Customization

Compiler options can be configured in the compileJava and compileTestJava tasks, as shown above.

Source Set Customization

Custom Source Directories

The source directories for both main and test source sets can be reconfigured as follows (the example only shows main, test is similar):

sourceSets {
  main {
    java {
      srcDirs = ['src/somethingelse/java']
    }
  }
}

To add directories, use this syntax:

sourceSets {
  main {
    java {
      srcDir 'src/somethingelse/java'
    }
  }
}

or

sourceSets.main.java.srcDirs += ['src/somethingelse/java']

It is equivalent with:

sourceSets {
  main {
    java {
      srcDirs = ['src/main/java', 'src/somethingelse/java']
    }
  }
}

Custom Resource Directories

sourceSets {
  main {
    resources {
      srcDirs = ["src/main/resources", "src/main/configs"]
      excludes = ['**/something/**']
    }
  }
}

Testing with Gradle Java Plugin

Testing with Gradle Java Plugin