The Java Plugin
The Java plugin adds Java compilation along with testing and bundling capabilities to a project. It serves as the basis for many of the other JVM language Gradle plugins. You can find a comprehensive introduction and overview to the Java Plugin in the Building Java Projects chapter.
Usage
To use the Java plugin, include the following in your build script:
Groovy
Kotlin
plugins {
id 'java'
}
Tasks
The Java plugin adds a number of tasks to your project, as shown below.
compileJava
— JavaCompile-
Depends on: All tasks which contribute to the compilation classpath, including
jar
tasks from projects that are on the classpath via project dependenciesCompiles production Java source files using the JDK compiler.
processResources
— Copy-
Copies production resources into the production resources directory.
classes
-
Depends on:
compileJava
,processResources
This is an aggregate task that just depends on other tasks. Other plugins may attach additional compilation tasks to it.
compileTestJava
— JavaCompile-
Depends on:
classes
, and all tasks that contribute to the test compilation classpathCompiles test Java source files using the JDK compiler.
processTestResources
— Copy-
Copies test resources into the test resources directory.
testClasses
-
Depends on:
compileTestJava
,processTestResources
This is an aggregate task that just depends on other tasks. Other plugins may attach additional test compilation tasks to it.
jar
— Jar-
Depends on:
classes
Assembles the production JAR file, based on the classes and resources attached to the
main
source set. javadoc
— Javadoc-
Depends on:
classes
Generates API documentation for the production Java source using Javadoc.
test
— Test-
Depends on:
testClasses
, and all tasks which produce the test runtime classpathRuns the unit tests using JUnit or TestNG.
uploadArchives
— Upload-
Depends on:
jar
, and any other task that produces an artifact attached to thearchives
configurationUploads artifacts in the
archives
configuration — including the production JAR file — to the configured repositories. clean
— Delete-
Deletes the project build directory.
cleanTaskName
— Delete-
Deletes files created by the specified task. For example,
cleanJar
will delete the JAR file created by thejar
task andcleanTest
will delete the test results created by thetest
task.
SourceSet Tasks
For each source set you add to the project, the Java plugin adds the following tasks:
compileSourceSetJava
— JavaCompile-
Depends on: All tasks which contribute to the source set’s compilation classpath
Compiles the given source set’s Java source files using the JDK compiler.
processSourceSetResources
— Copy-
Copies the given source set’s resources into the resources directory.
sourceSetClasses
— Task-
Depends on:
compileSourceSetJava
,processSourceSetResources
Prepares the given source set’s classes and resources for packaging and execution. Some plugins may add additional compilation tasks for the source set.
Lifecycle Tasks
The Java plugin attaches some of its tasks to the lifecycle tasks defined by the Base Plugin — which the Java Plugin applies automatically — and it also adds a few other lifecycle tasks:
assemble
-
Depends on:
jar
, and all other tasks that create artifacts attached to thearchives
configurationAggregate task that assembles all the archives in the project. This task is added by the Base Plugin.
check
-
Depends on:
test
Aggregate task that performs verification tasks, such as running the tests. Some plugins add their own verification tasks to
check
. You should also attach any customTest
tasks to this lifecycle task if you want them to execute for a full build. This task is added by the Base Plugin. build
-
Depends on:
check
,assemble
Aggregate tasks that performs a full build of the project. This task is added by the Base Plugin.
buildNeeded
-
Depends on:
build
, andbuildNeeded
tasks in all projects that are dependencies in thetestRuntimeClasspath
configuration.Performs a full build of the project and all projects it depends on.
buildDependents
-
Depends on:
build
, andbuildDependents
tasks in all projects that have this project as a dependency in theeirtestRuntimeClasspath
configurationsPerforms a full build of the project and all projects which depend upon it.
buildConfigName
— task rule-
Depends on: all tasks that generate the artifacts attached to the named — ConfigName — configuration
Assembles the artifacts for the specified configuration. This rule is added by the Base Plugin.
uploadConfigName
— task rule, type: Upload-
Depends on: all tasks that generate the artifacts attached to the named — ConfigName — configuration
Assembles and uploads the artifacts in the specified configuration. This rule is added by the Base Plugin.
The following diagram shows the relationships between these tasks.
Project layout
The Java plugin assumes the project layout shown below. None of these directories need to exist or have anything in them. The Java plugin will compile whatever it finds, and handles anything which is missing.
src/main/java
-
Production Java source.
src/main/resources
-
Production resources, such as XML and properties files.
src/test/java
-
Test Java source.
src/test/resources
-
Test resources.
src/sourceSet/java
-
Java source for the source set named sourceSet.
src/sourceSet/resources
-
Resources for the source set named sourceSet.
Changing the project layout
You configure the project layout by configuring the appropriate source set. This is discussed in more detail in the following sections. Here is a brief example which changes the main Java and resource source directories.
Groovy
Kotlin
sourceSets {
main {
java {
srcDirs = ['src/java']
}
resources {
srcDirs = ['src/resources']
}
}
}
Source sets
The plugin adds the following source sets:
main
-
Contains the production source code of the project, which is compiled and assembled into a JAR.
test
-
Contains your test source code, which is compiled and executed using JUnit or TestNG. These are typically unit tests, but you can include any test in this source set as long as they all share the same compilation and runtime classpaths.
Source set properties
The following table lists some of the important properties of a source set. You can find more details in the API documentation for SourceSet.
name
— (read-only)String
-
The name of the source set, used to identify it.
output
— (read-only) SourceSetOutput-
The output files of the source set, containing its compiled classes and resources.
output.classesDirs
— (read-only) FileCollection-
Default value:
$buildDir/classes/java/$name
, e.g. build/classes/java/mainThe directories to generate the classes of this source set into. May contain directories for other JVM languages, e.g. build/classes/kotlin/main.
output.resourcesDir
—File
-
Default value:
$buildDir/resources/$name
, e.g. build/resources/mainThe directory to generate the resources of this source set into.
compileClasspath
— FileCollection-
Default value:
${name}CompileClasspath
configurationThe classpath to use when compiling the source files of this source set.
annotationProcessorPath
— FileCollection-
Default value:
${name}AnnotationProcessor
configurationThe processor path to use when compiling the source files of this source set.
runtimeClasspath
— FileCollection-
Default value:
$output
,${name}RuntimeClasspath
configurationThe classpath to use when executing the classes of this source set.
java
— (read-only) SourceDirectorySet-
The Java source files of this source set. Contains only
.java
files found in the Java source directories, and excludes all other files. java.srcDirs
—Set<File>
-
Default value:
src/$name/java
, e.g. src/main/javaThe source directories containing the Java source files of this source set. You can set this to any value that is described in sec:specifying_multiple_filesthis section.
java.outputDir
—File
-
Default value:
$buildDir/classes/java/$name
, e.g. build/classes/java/mainThe directory to generate compiled Java sources into. You can set this to any value that is described in this section.
resources
— (read-only) SourceDirectorySet-
The resources of this source set. Contains only resources, and excludes any
.java
files found in the resource directories. Other plugins, such as the Groovy Plugin, exclude additional types of files from this collection. resources.srcDirs
—Set<File>
-
Default value:
[src/$name/resources]
The directories containing the resources of this source set. You can set this to any type of value that is described in this section.
allJava
— (read-only) SourceDirectorySet-
Default value: Same as
java
propertyAll Java files of this source set. Some plugins, such as the Groovy Plugin, add additional Java source files to this collection.
allSource
— (read-only) SourceDirectorySet-
Default value: Sum of everything in the
resources
andjava
propertiesAll source files of this source set of any language. This includes all resource files and all Java source files. Some plugins, such as the Groovy Plugin, add additional source files to this collection.
Defining new source sets
See the integration test example in the Testing in Java & JVM projects chapter.
Some other simple source set examples
Adding a JAR containing the classes of a source set:
Groovy
Kotlin
task intTestJar(type: Jar) {
from sourceSets.intTest.output
}
Generating Javadoc for a source set:
Groovy
Kotlin
task intTestJavadoc(type: Javadoc) {
source sourceSets.intTest.allJava
}
Adding a test suite to run the tests in a source set:
Groovy
Kotlin
task intTest(type: Test) {
testClassesDirs = sourceSets.intTest.output.classesDirs
classpath = sourceSets.intTest.runtimeClasspath
}
Dependency management
The Java plugin adds a number of dependency configurations to your project, as shown below. Tasks such as compileJava
and test
then use one or more of those configurations to get the corresponding files and use them, for example by placing them on a compilation or runtime classpath.
Dependency configurations
✨
|
To find information on the |
compile
(Deprecated)-
Compile time dependencies. Superseded by
implementation
. implementation
extendscompile
-
Implementation only dependencies.
compileOnly
-
Compile time only dependencies, not used at runtime.
compileClasspath
extendscompile, compileOnly, implementation
-
Compile classpath, used when compiling source. Used by task
compileJava
. annotationProcessor
-
Annotation processors used during compilation.
runtime
(Deprecated) extendscompile
-
Runtime dependencies. Superseded by
runtimeOnly
. runtimeOnly
-
Runtime only dependencies.
runtimeClasspath
extendsruntimeOnly, runtime, implementation
-
Runtime classpath contains elements of the implementation, as well as runtime only elements.
testCompile
(Deprecated) extendscompile
-
Additional dependencies for compiling tests. Superseded by
testImplementation
. testImplementation
extendstestCompile, implementation
-
Implementation only dependencies for tests.
testCompileOnly
-
Additional dependencies only for compiling tests, not used at runtime.
testCompileClasspath
extendstestCompile, testCompileOnly, testImplementation
-
Test compile classpath, used when compiling test sources. Used by task
compileTestJava
. testRuntime
(Deprecated) extendsruntime, testCompile
-
Additional dependencies for running tests only. Superseded by
testRuntimeOnly
. testRuntimeOnly
extendsruntimeOnly
-
Runtime only dependencies for running tests.
testRuntimeClasspath
extendstestRuntimeOnly, testRuntime, testImplementation
-
Runtime classpath for running tests. Used by task
test
. archives
-
Artifacts (e.g. jars) produced by this project. Used by task
uploadArchives
. default
extendsruntime
-
The default configuration used by a project dependency on this project. Contains the artifacts and dependencies required by this project at runtime.
The following diagrams show the dependency configurations for the main and test source sets respectively. You can use this legend to interpret the colors:
-
Gray text — the configuration is deprecated.
-
Green background — you can declare dependencies against the configuration.
-
Blue-gray background — the configuration is for consumption by tasks, not for you to declare dependencies.
-
Light blue background with monospace text — a task.
For each source set you add to the project, the Java plugins adds the following dependency configurations:
SourceSet dependency configurations
sourceSetCompile
(Deprecated)-
Compile time dependencies for the given source set. Superseded by
sourceSetImplementation
. sourceSetImplementation
extendssourceSetCompile
-
Compile time dependencies for the given source set. Used by
sourceSetCompileClasspath, sourceSetRuntimeClasspath
. sourceSetCompileOnly
-
Compile time only dependencies for the given source set, not used at runtime.
sourceSetCompileClasspath
extendscompileSourceSetJava
-
Compile classpath, used when compiling source. Used by
sourceSetCompile, sourceSetCompileOnly, sourceSetImplementation
. sourceSetAnnotationProcessor
-
Annotation processors used during compilation of this source set.
sourceSetRuntime
(Deprecated)-
Runtime dependencies for the given source set. Used by
sourceSetCompile
. Superseded bysourceSetRuntimeOnly
. sourceSetRuntimeOnly
-
Runtime only dependencies for the given source set.
sourceSetRuntimeClasspath
extendssourceSetRuntimeOnly, sourceSetRuntime, sourceSetImplementation
-
Runtime classpath contains elements of the implementation, as well as runtime only elements.
Convention properties
The Java Plugin adds a number of convention properties to the project, shown below. You can use these properties in your build script as though they were properties of the project object.
Directory properties
String reporting.baseDir
-
The name of the directory to generate reports into, relative to the build directory. Default value:
reports
(read-only) File reportsDir
-
The directory to generate reports into. Default value:
buildDir/reporting.baseDir
String testResultsDirName
-
The name of the directory to generate test result .xml files into, relative to the build directory. Default value:
test-results
(read-only) File testResultsDir
-
The directory to generate test result .xml files into. Default value:
buildDir/testResultsDirName
String testReportDirName
-
The name of the directory to generate the test report into, relative to the reports directory. Default value:
tests
(read-only) File testReportDir
-
The directory to generate the test report into. Default value:
reportsDir/testReportDirName
String libsDirName
-
The name of the directory to generate libraries into, relative to the build directory. Default value:
libs
(read-only) File libsDir
-
The directory to generate libraries into. Default value:
buildDir/libsDirName
String distsDirName
-
The name of the directory to generate distributions into, relative to the build directory. Default value:
distributions
(read-only) File distsDir
-
The directory to generate distributions into. Default value:
buildDir/distsDirName
String docsDirName
-
The name of the directory to generate documentation into, relative to the build directory. Default value:
docs
(read-only) File docsDir
-
The directory to generate documentation into. Default value:
buildDir/docsDirName
String dependencyCacheDirName
-
The name of the directory to use to cache source dependency information, relative to the build directory. Default value:
dependency-cache
Other convention properties
(read-only) SourceSetContainer sourceSets
-
Contains the project’s source sets. Default value: Not null SourceSetContainer
JavaVersion sourceCompatibility
-
Java version compatibility to use when compiling Java source. Default value: version of the current JVM in use JavaVersion. Can also set using a String or a Number, e.g.
'1.5'
or1.5
. JavaVersion targetCompatibility
-
Java version to generate classes for. Default value:
sourceCompatibility
. Can also set using a String or Number, e.g.'1.5'
or1.5
. String archivesBaseName
-
The basename to use for archives, such as JAR or ZIP files. Default value:
projectName
Manifest manifest
-
The manifest to include in all JAR files. Default value: an empty manifest.
These properties are provided by convention objects of type JavaPluginConvention, and BasePluginConvention.
Publishing
components.java
-
A SoftwareComponent for publishing the production JAR created by the
jar
task. This component includes the runtime dependency information for the JAR.
Incremental Java compilation
Gradle comes with a sophisticated incremental Java compiler that is active by default.
This gives you the following benefits
-
Incremental builds are much faster.
-
The smallest possible number of class files are changed. Classes that don’t need to be recompiled remain unchanged in the output directory. An example scenario when this is really useful is using JRebel - the fewer output classes are changed the quicker the JVM can use refreshed classes.
To help you understand how incremental compilation works, the following provides a high-level overview:
-
Gradle will recompile all classes affected by a change.
-
A class is affected if it has been changed or if it depends on another affected class. This works no matter if the other class is defined in the same project, another project or even an external library.
-
A class’s dependencies are determined from type references in its bytecode.
-
Since constants can be inlined, any change to a constant will result in Gradle recompiling all source files. For that reason, you should try to minimize the use of constants in your source code and replace them with static methods where possible.
-
Since source-retention annotations are not visible in bytecode, changes to a source-retention annotation will result in full recompilation.
-
You can improve incremental compilation performance by applying good software design principles like loose coupling. For instance, if you put an interface between a concrete class and its dependents, the dependent classes are only recompiled when the interface changes, but not when the implementation changes.
-
The class analysis is cached in the project directory, so the first build after a clean checkout can be slower. Consider turning off the incremental compiler on your build server.
Known issues
-
If a compile task fails due to a compile error, it will do a full compilation again the next time it is invoked.
-
If you are using an annotation processor that reads resources (e.g. a configuration file), you need to declare those resources as an input of the compile task.
-
If a resource file is changed, Gradle will trigger a full recompilation.
-
If there is a mismatch in the package declaration and the directory structure of source files (e.g.
package foo
vs locationbar/MyClass.java
), then incremental compilation can produce broken output. Wrong classes might be recompiled and there might be leftover class files in the output.
Incremental annotation processing
Starting with Gradle 4.7, the incremental compiler also supports incremental annotation processing. All annotation processors need to opt in to this feature, otherwise they will trigger a full recompilation.
As a user you can see which annotation processors are triggering full recompilations in the --info
log.
Incremental annotation processing will be deactivated if a custom executable
or javaHome
is configured on the compile task.
Making an annotation processor incremental
Please first have a look at incremental Java compilation, as incremental annotation processing builds on top of it.
Gradle supports incremental compilation for two common categories of annotation processors: "isolating" and "aggregating". Please consult the information below to decide which category fits your processor.
You can then register your processor for incremental compilation using a file in the processor’s META-INF directory. The format is one line per processor, with the fully qualified name of the processor class and its category separated by a comma.
Example: Registering incremental annotation processors
EntityProcessor,isolating ServiceRegistryProcessor,dynamic
If your processor can only decide at runtime whether it is incremental or not, you can declare it as "dynamic" in the META-INF descriptor and return its true type at runtime using the Processor#getSupportedOptions() method.
Example: Registering incremental annotation processors dynamically
@Override
public Set<String> getSupportedOptions() {
return Collections.singleton("org.gradle.annotation.processing.aggregating");
}
Both categories have the following limitations:
-
They must generate their files using the Filer API. Writing files any other way will result in silent failures later on, as these files won’t be cleaned up correctly. If your processor does this, it cannot be incremental.
-
They must not depend on compiler-specific APIs like
com.sun.source.util.Trees
. Gradle wraps the processing APIs, so attempts to cast to compiler-specific types will fail. If your processor does this, it cannot be incremental, unless you have some fallback mechanism. -
If they use Filer#createResource, the
location
argument must be one of these values from StandardLocation:CLASS_OUTPUT
,SOURCE_OUTPUT
, orNATIVE_HEADER_OUTPUT
. Any other argument will disable incremental processing.
"Isolating" annotation processors
The fastest category, these look at each annotated element in isolation, creating generated files or validation messages for it.
For instance an EntityProcessor
could create a <TypeName>Repository
for each type annotated with @Entity
.
Example: An isolated annotation processor
Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(entityAnnotation);
for (Element entity : entities) {
createRepository((TypeElement) entity);
}
"Isolating" processors have the following limitations:
-
They must make all decisions (code generation, validation messages) for an annotated type based on information reachable from its AST. This means you can analyze the types' super-class, method return types, annotations etc., even transitively. But you cannot make decisions based on unrelated elements in the RoundEnvironment. Doing so will result in silent failures because too few files will be recompiled later. If your processor needs to make decisions based on a combination of otherwise unrelated elements, mark it as "aggregating" instead.
-
They must provide exactly one originating element for each file generated with the
Filer
API. If zero or many originating elements are provided, Gradle will recompile all source files.
When a source file is recompiled, Gradle will recompile all files generated from it. When a source file is deleted, the files generated from it are deleted.
"Aggregating" annotation processors
These can aggregate several source files into one ore more output files or validation messages.
For instance, a ServiceRegistryProcessor
could create a single ServiceRegistry
with one method for each type annotated with @Service
Example: An aggregating annotation processor
JavaFileObject serviceRegistry = filer.createSourceFile("ServiceRegistry");
Writer writer = serviceRegistry.openWriter();
writer.write("public class ServiceRegistry {");
for (Element service : roundEnv.getElementsAnnotatedWith(serviceAnnotation)) {
addServiceCreationMethod(writer, (TypeElement) service);
}
writer.write("}");
writer.close();
"Aggregating" processors have the following limitations:
-
They can only read
CLASS
orRUNTIME
retention annotations -
They can only read parameter names if the user passes the
-parameters
compiler argument.
Gradle will always reprocess (but not recompile) all annotated files that the processor was registered for. Gradle will always recompile any files the processor generates.
State of support in popular annotation processors
✨
|
Many popular annotation processors support incremental annotation processing (see the table below). Check with the annotation processor project directly for the most up-to-date information and documentation. |
Annotation Processor | Supported since | Details |
---|---|---|
N/A |
||
N/A |
||
Partly supported. |
||
Unsupported. |
||
N/A |
||
DataBinding |
Hidden behind a feature toggle |
|
Dagger |
Hidden behind a feature toggle |
|
kapt |
Hidden behind a feature toggle |
|
Toothpick |
N/A |
|
Glide |
N/A |
|
Toothpick |
N/A |
|
Android-State |
N/A |
|
Parceler |
N/A |
|
Dart and Henson |
N/A |
|
MapStruct |
N/A |
|
Realm |
N/A |
|
Requery |
N/A |
|
EventBus |
N/A |
|
EclipseLink |
N/A |
|
PermissionsDispatcher |
N/A |
|
Immutables |
N/A |
|
N/A |
||
N/A |
||
Android Annotations |
N/A |
|
DBFlow |
N/A |
|
AndServer |
N/A |
|
Litho |
N/A |
|
Moxy |
N/A |
|
Epoxy |
N/A |
Compilation avoidance
If a dependent project has changed in an ABI-compatible way (only its private API has changed), then Java compilation tasks will be up-to-date. This means that if project A
depends on project B
and a class in B
is changed in an ABI-compatible way (typically, changing only the body of a method), then Gradle won’t recompile A
.
Some of the types of changes that do not affect the public API and are ignored:
-
Changing a method body
-
Changing a comment
-
Adding, removing or changing private methods, fields, or inner classes
-
Adding, removing or changing a resource
-
Changing the name of jars or directories in the classpath
-
Renaming a parameter
Since implementation details matter for annotation processors, they must be declared separately on the annotation processor path. Gradle ignores annotation processors on the compile classpath.
Groovy
Kotlin
dependencies {
// The dagger compiler and its transitive dependencies will only be found on annotation processing classpath
annotationProcessor 'com.google.dagger:dagger-compiler:2.8'
// And we still need the Dagger library on the compile classpath itself
implementation 'com.google.dagger:dagger:2.8'
}