The Gradle team is excited to announce Gradle 6.8.
This release significantly improves the performance of Kotlin DSL build scripts compilation, adds several improvements to Java toolchain support, including vendor selection, and makes it easy to execute any tasks in a composite build directly from the command line. This release also introduces new dependency management APIs for consistent resolution.
The experimental configuration cache has added support for composite builds and more core plugins shipped with Gradle.
Several other improvements were added in this release.
We don't expect many builds to be affected, but this release disables outdated TLS v1.0 and v1.1 protocols to improve the security of builds resolving dependencies from external repositories.
We would like to thank the following community contributors for their contributions to this release of Gradle:
Marcono1234, Björn Sundahl, Roberto Perez Alcolea, Danny Thomas, Jeff, Mattia Tommasone, jdai8, David Burström, Björn Kautler, Stefan Oehme, Thad House, knittl, hywelbennett, Gregorios Leach.
Switch your build to use Gradle 6.8 by updating your wrapper:
./gradlew wrapper --gradle-version=6.8
See the Gradle 6.x upgrade guide to learn about deprecations, breaking changes and other considerations when upgrading to Gradle 6.8.
For Java, Groovy, Kotlin and Android compatibility, see the full compatibility notes.
This release makes compilation of Gradle Kotlin DSL scripts (*.gradle.kts
) faster, reduces the amount of memory consumed, and introduces compilation avoidance that can eliminate the need to recompile Kotlin build scripts altogether.
On a sample build with 100 subprojects, the cumulative script compilation time goes from ~50 seconds down to ~21 seconds with cold caches and cold daemons. Garbage collection time goes from 2.6 seconds down to 1.3 seconds. This improvement also reduces memory pressure. On top of that, a non-ABI change can eliminate build script recompilation altogether now, saving those 21 seconds.
Until now, any change to build logic in buildSrc required all of the build scripts to be recompiled. This release introduces compilation avoidance for Gradle Kotlin DSL scripts.
Compilation avoidance will cause Gradle to only recompile build scripts when a change to shared build logic impacts the ABI (application binary interface) of the classpath of the build script. Changes to private implementation details of build logic, such as private methods or classes, bodies of non-private methods or classes, as well as internal changes to precompiled script plugins, will no longer trigger recompilation of the project's build scripts.
Compilation avoidance also applies to changes in any JAR on the build script's classpath. That includes JARs added by plugins define in included builds and JARs added directly via the buildscript {}
block.
While the impact on your build may vary, most builds can expect a noticeably shorter feedback loop when editing Kotlin DSL build logic.
Note: Kotlin's public inline functions are not supported with compilation avoidance. If such functions appear in the public API of a JAR on the buildscript's classpath, changes to classes in that JAR will cause Gradle to fallback to its old behavior. For example, if buildSrc
contains a class with a public inline function, then any change to a class in buildSrc
will cause all build scripts to be recompiled.
For up-to-date checks and the build cache, Gradle needs to determine if two task input properties have the same value. In order to do so, Gradle first normalizes both inputs and then compares the result.
Runtime classpath analysis now smartly inspects all properties files, ignoring changes to comments, whitespace, and differences in property order. Moreover, you can selectively ignore properties that don't impact the runtime classpath.
normalization {
properties('**/build-info.properties') {
ignoreProperty('timestamp')
}
}
This improves the likelihood of up-to-date and build cache hits when a properties file on the classpath is regenerated or only differs by unimportant values.
See the user manual for further information.
For up-to-date checks and the build cache, Gradle needs to determine if two directory structures contain the same contents. When a directory contains an empty directory, it is considered to have different contents than an identical directory where the empty directory does not exist.
This may not always be desirable. There are many cases where only the files in a directory structure may be significant, and an empty directory will have no impact on the outputs of a task. In such cases, re-executing the task because an empty directory exists is unnecessary as it will only produce the same outputs.
A new annotation has been introduced to address this scenario. Inputs annotated with @InputFiles or @InputDirectory can additionally be annotated with @IgnoreEmptyDirectories to specify that directories should not be considered during build cache and up-to-date checks. For inputs annotated in this way, only changes to files (including the file path) will be treated as differences in the input values.
class MyTask extends DefaultTask {
@InputFiles
@PathSensitive(@PathSensitivity.RELATIVE)
@IgnoreEmptyDirectories
FileCollection inputFiles;
}
Similarly, there is a corresponding runtime API equivalent:
tasks.register("myTask") {
ext.inputFiles = files()
inputs.files(inputFiles)
.withPropertyName('inputFiles')
.withPathSensitivity(PathSensitivity.RELATIVE)
.ignoreEmptyDirectories()
}
SourceTask, JavaCompile, GroovyCompile, and AntlrTask have all been updated to now ignore empty directories when doing up-to-date checks and build cache key calculations.
See the user manual for more information.
The configuration cache improves build performance by caching the result of the configuration phase. Using the configuration cache, Gradle can skip the configuration phase entirely when nothing that affects the build configuration has changed.
Read about this feature and its impact on the Gradle blog. You can also track progress of configuration cache support in core plugins and community plugins.
Starting with this release, composite builds are fully supported with the configuration cache.
In this release all core code analysis plugins received full for support the configuration cache:
See the matrix of supported core plugins in the user manual.
Java toolchain support provides an easy way to declare what Java version the project should be built with. By default, Gradle will auto-detect installed JDKs that can be used as toolchain.
With this release, toolchain support has been added to the Groovy compile task along with the following improvements.
In case your build has specific requirements from the used JRE/JDK, you may want to define the vendor for the toolchain as well. JvmVendorSpec
has a list of well-known JVM vendors recognized by Gradle.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
vendor = JvmVendorSpec.ADOPTOPENJDK
// alternativly, use custom matching
// vendor = JvmVendorSpec.matching("customString")
}
}
If the vendor is not enough to select the appropriate toolchain, you may as well filter by the implementation of the virtual machine. For example, to use an Open J9 JVM, distributed via AdoptOpenJDK, you can filter by the implementation as shown in the example below.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
vendor = JvmVendorSpec.ADOPTOPENJDK
implementation = JvmImplementation.J9
}
}
Please refer to the documentation for more detailed information.
In order to see which toolchains got detected and their corresponding metadata, Gradle 6.8 now provides some insight with the javaToolchains
task.
Output of gradle -q javaToolchains
:
+ Options
| Auto-detection: Enabled
| Auto-download: Enabled
+ AdoptOpenJDK 1.8.0_242
| Location: /path/to/8.0.242.hs-adpt/jre
| Language Version: 8
| Vendor: AdoptOpenJDK
| Is JDK: true
| Detected by: SDKMAN!
+ OpenJDK 15-ea
| Location: /path/to/java/15.ea.21-open
| Language Version: 15
| Vendor: AdoptOpenJDK
| Is JDK: true
| Detected by: SDKMAN!
+ Oracle JDK 1.7.0_80
| Location: /Library/Java/jdk1.7.0_80.jdk/jre
| Language Version: 7
| Vendor: Oracle
| Is JDK: true
| Detected by: macOS java_home
This can help to debug which toolchains are available to the build and if the expected toolchain got detected or requires manual setup. See the toolchain documentation for more in-depth information on toolchain detection and usage.
Composite builds are a way of combining separate Gradle builds into a single build. Each build can have a separate purpose (build logic, backend code, frontend code) and can be worked on independently.
Gradle now allows users to execute tasks from included builds directly from the command line. For example, if your build includes my-other-project
as an included build and it has a subproject sub
with a task foo
, then you can execute foo
with the following command:
gradle :my-other-project:sub:foo
Note, unlike a multi-project build, running gradle build
will not run the build
task in all of the included builds. You could introduce task dependencies to lifecycle tasks in included builds if you wanted to recreate this behavior for included builds.
IDE support for executing tasks from included builds may not yet fully work depending on the IDE. Updates for IntelliJ IDEA and Eclipse Buildship are planned to support this fully. Today, in IntelliJ IDEA, you can create a Gradle run configuration to execute a task directly (like you would on the command line).
There are cases, where a cycle between included builds are desired. For example, if two builds contain end-to-end tests that require the production code of both builds. Such setups are possible with subprojects of a single build, but were not fully supported between projects of different builds. With this release, this is possible and Gradle will only fail if there is a cycle between tasks. Issues with importing such builds in IDEs are also fixed.
Gradle's documentation now contains a sample for structuring software projects with composite builds and a new a chapter on structuring software projects using composite builds.
Dependency resolution in Gradle happens a lot during a build. From the classpath to compile code or run tests, to the tools used for static analysis, they all resolve a configuration to a set of dependencies at some point.
However, these resolutions happen in isolation. Sometimes, the dependencies resolved for the runtime classpath have different versions than the dependencies resolved for the compile classpath. This typically happens when a transitive dependency that is only present at runtime brings in a higher version of a first level dependency. Similarly, the runtime classpath of tests could use different versions than the compile classpath of production code.
To mitigate this problem, Gradle now lets you declare consistency between dependency configurations. For example, in the Java ecosystem, you can write:
java {
consistentResolution {
useCompileClasspathVersions()
}
}
This tells Gradle that the common dependencies between the runtime classpath and the compile classpath should be aligned to the versions used at compile time.
There are many options to configure this feature, including using it outside of the Java ecosystem, which are described in the user manual.
In previous Gradle versions, repositories used for dependency resolution had to be declared for every (sub)project individually. However, in most cases, the same repositories should be used in every project.
In Gradle 6.8, repositories can now conveniently be defined for the whole build in settings.gradle(.kts)
:
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
This allows Gradle to ensure that you use the same repositories for resolving dependencies in all projects of the build. Learn more by reading how to declare repositories for the whole build.
Component metadata rules are a powerful tool to fix bad metadata published on remote repositories. However, similarly to repositories, rules traditionally had to be applied on each project. Starting from this release, it is possible to declare component metadata rules in a central place in settings.gradle(.kts)
:
dependencyResolutionManagement {
components {
withModule('com.google.guava:guava', GuavaRule)
}
}
You can learn more about declaring rules globally in the user manual.
Dependency locking makes builds using dynamic versions deterministic.
So far you could lock dependency configurations from your project or from the project buildscript classpath. This release adds support to lock configurations from the Gradle settings.gradle(.kts)
:
buildscript {
configurations.classpath {
resolutionStrategy.activateDependencyLocking()
}
}
See the documentation for more details on locking settings configurations.
The Test
task, used for executing JVM tests, reports test results as HTML and as a set of XML files in the “JUnit XML” pseudo standard. It is common for CI servers and other tooling to observe test results via the XML files. A new mergeReruns
option has been added that changes how tests that are executed more than once are reported in the XML files.
test {
reports.junitXml.mergeReruns = true
}
When this new option is enabled, if a test fails but is then retried and succeeds, its failures will be recorded as <flakyFailure>
instead of <failure>
, within one <testcase>
. This is the same as the reporting produced by the surefire plugin of Apache Maven™, when enabling reruns. If your CI server understands this format, it will indicate that the test was flaky.
This option is disabled by default, causing each test execution to be listed as a separate <testcase>
in the XML. This means that when a test is executed multiple times, due to a retry-on-failure mechanism for example, it is listed multiple times. This is also the behavior for all previous Gradle versions.
If you are using build scans or Gradle Enterprise, flaky tests will be detected regardless of this setting.
Learn more about this new feature in the Java testing documentation.
@Inject
is an implicit importWhen using dependency injection when developing plugins, tasks or project extensions, it is now possible to use the @Inject
annotation without explicitly importing it into your build scripts the same way it works for other Gradle API classes.
This version of Gradle fixes problems with projects that use custom source sets, like additional functional test source sets.
Custom source sets are now imported into Eclipse automatically and no longer require manual configuration in the build.
This does not require a separate upgrade to Eclipse Buildship.
This version of Gradle removes TLS protocols v1.0 and v1.1 from the default list of allowed protocols. Gradle will no longer fallback to TLS v1.0 or v1.1 by default when resolving dependencies. Only TLS v1.2 or TLS v1.3 are allowed by default.
These TLS versions can be re-enabled by manually specifying the system property https.protocols
with a comma separated list of protocols required by your build.
The vast majority of builds should not need to change in any way. Maven Central and JCenter/Bintray dropped support for TLS v1.0 and TLS v1.1 in 2018. Java has had TLS v1.2 available since Java 7. Disabling these protocols in Gradle protects builds from downgrade attacks.
Depending on the version of Java you use, Gradle will negotiate TLS v1.2 or TLS v1.3 when communicating with remote repositories.
Note: Early versions of JDK 11 & JDK 12 contained race condition bug in the TLSv1.3
handling logic which causes the exception javax.net.ssl.SSLException: No PSK available. Unable to resume
. If you run into this issue, we recommend updating to the latest minor JDK version.
Known issues are problems that were discovered post release that are directly related to changes made in this release.
We love getting contributions from the Gradle community. For information on contributing, please see gradle.org/contribute.
If you find a problem with this release, please file a bug on GitHub Issues adhering to our issue guidelines. If you're not sure you're encountering a bug, please use the forum.
We hope you will build happiness with Gradle, and we look forward to your feedback via Twitter or on GitHub.