What's new in Gradle 6.0

Gradle 6.0 is the culmination of several years of innovative improvements. Watch a recording from our What’s New in Gradle 6.0 webinar.

Dependency management in Gradle 6.0 embraces the idea that there is more to software composition and reuse than just putting a set of jar files on the classpath. Gradle now offers a new metadata format to richly define software components that are often made up of multiple files, different variants and specific constraints on their dependencies.

Gradle 6.0 will help improve your overall Developer Productivity with updates to the Java, Groovy and Scala ecosystem plugins. The APIs used by plugin authors continue to evolve to make it easier to create plugins and tasks that are parallelizable, incremental and easy to use.

The com.gradle.build-scan plugin received a major update for Gradle 6.0. Users of scans.gradle.com and Develocity users will need to switch to the com.gradle.enterprise plugin. This isn’t just the same plugin with a new ID and a new way to apply it. The com.gradle.enterprise plugin reliably captures buildSrc build events and failures.

If you’re interested in following the development of new Gradle features, we post updates to our blog when we have new features to preview like Swift support or advice on avoiding dependency hell. We also post videos from past webinars for top Android build issues, dependency management techniques and release strategies.

Here are the major improvements from Gradle 5.0 to 6.0:

Dependency Management with Gradle Module Metadata

The publication of Gradle Module Metadata is now the default when using the maven-publish or ivy-publish plugins. This enables many innovative dependency management features, described below, to work across project boundaries.

Sharing dependency versions between projects with platforms

Gradle offers an easy way to recommend and share versions between projects called platforms.

With Gradle platforms, more context around version declaration are available, versions can be recommended and strict versions are enforced. For interoperability, builds can also leverage integration with Maven BOMs.

Handling mutually exclusive dependencies

Gradle uses capabilities to allow plugins and builds to detect and resolve implementation conflicts between mutually exclusive dependencies. A well-known example in the JVM world is competing logging implementations. Capabilities let builds configure which dependency to select instead of adding competing ones to the classpath.

Upgrading versions of transitive dependencies

Issues with dependency management are often about dealing with transitive dependencies. Often, transitive dependency issues are incorrectly fixed by adding direct dependencies. To avoid this, Gradle provides the concept of dependency constraints to influence the version of transitive dependencies.

Aligning versions across multiple dependencies

Dependency version alignment allows builds to express that different modules belong to the same logical group (like a platform) and need to have identical (a.k.a aligned) versions in a dependency graph. A well-known example in the JVM world is the Jackson libraries.

Expressing intent with context

When declaring a dependency, a build can provide more context to Gradle about its version, including version preferences within a range, strict version requirements or rejected versions. Developers can also provide human readable descriptions for why a dependency is used or needed.

Tweaking published metadata

Gradle allows builds to fix or enrich traditional metadata with information that could not be published before, such as dependency constraints, rich versions, capabilities and variants. These are called component metadata rules. Component metadata rules also make it possible to map additional published artifacts to new Gradle variants.

Modeling feature variants and optional dependencies

Gradle provides the ability to model optional features of a library. Each feature variant can have its own set of dependencies and can be consumed separately.

Better builds for Java, Groovy and Scala developers

You can bring improvements to your Java, Groovy or Scala builds by upgrading to Gradle 6.0.

Faster Java incremental compilation

When analyzing the impact of a changed class, the incremental compiler can now exclude classes that are an implementation detail of another class. This limits the number of classes that need to be recompiled. For deep dependency chains, this greatly reduces the number of files that require recompilation.

As an example, compiling a project with 5000 source files after changing a single source file:

Gradle 5.6 ( 21s)
Gradle 5.6 takes 21s to compile.
Gradle 6.0 (0.5s)
Gradle 6.0 only takes 0.5s to compile the same change.

Faster Groovy Compilation

Gradle 6.0 speeds up compilation for Groovy with two strategies.

Gradle uses compilation avoidance to skip re-compiling dependent projects if there are no changes that would affect the output of their compilation.

Gradle also uses incremental Groovy compilation. If only a small set of Groovy source files have been changed, only the affected source files will be recompiled. For example, if you only change one Groovy test class, Gradle doesn’t need to recompile all Groovy test source files. Gradle will recompile only the changed classes and the classes that are affected by them.

The same analysis improvements that made Java incremental compilation faster also apply to Groovy compilation.

This feature requires changes to your build to use. See Groovy compilation avoidance and Groovy incremental compilation for more details.

Latest Zinc compiler integration

Gradle 6.0 uses the latest Zinc incremental Scala compiler. The Zinc compiler saw major performance improvements in Zinc 1.0.

By default, Gradle uses Zinc 1.3.0.

Support for a wide range of JDKs

Gradle 6.0 now supports Java 13.

Gradle supports running on JDK8 through JDK13. JDK6 and 7 can still be used for compilation and testing.

Add Javadoc and sources jars

You can now activate Javadoc and sources publishing for a Java Library or Java project:

java {
    withJavadocJar()
    withSourcesJar()
}

Using the maven-publish or ivy-publish plugin, this will not only automatically create and publish a -javadoc.jar and -sources.jar but also publish the information that these exist as variants in Gradle Module Metadata. This means that you can query for the Javadoc or sources variant of a module and also retrieve the Javadoc (or sources) of its dependencies.

If activated, a Java and Java Library project automatically provides the javadocJar and sourcesJar tasks.

Sharing test fixtures between projects

Gradle 6.0 allows Java test fixtures from one project to be shared with other projects in the build. Gradle will automatically perform the necessary wiring so that test compilation depends on test fixtures. The java-test-fixtures plugin needs to be applied with the java-library plugin. It can also be combined with other JVM language plugins, like Groovy or Scala, to write test fixtures in these languages.

For example, this will add the test fixtures of “my-lib” to the compile classpath of the tests in this project.

dependencies {
   testImplementation(testFixtures(project(":my-lib")))
}

Organizing your build logic

Gradle 6.0 helps you get your build under control.

Organization-wide Gradle properties via custom distribution gradle.properties

Gradle now looks for a gradle.properties file in the Gradle distribution used by the build. This file has the lowest precedence of any gradle.properties and properties defined in other locations will override values defined here.

By placing a gradle.properties file in a custom Gradle distribution, an organization can add default properties for the entire organization or tweak the default Gradle daemon memory parameters with org.gradle.jvmargs.

Central management of plugin versions from settings.gradle

Gradle 6.0 makes it easier to manage the versions of plugins used by your build. By configuring all plugin versions in the settings script within the new pluginManagement.plugins {} block, build scripts can apply plugins via the plugins {} block without specifying a version.

pluginManagement {
    plugins {
        id 'org.my.plugin' version '1.1'
    }
}

One benefit of managing plugin versions in this way is that plugin versions may be loaded from gradle.properties or defined programmatically.

Plugin development with Composite Builds

The plugins { } block in build scripts can now be used to refer to plugins defined in included builds. In previous versions of Gradle, this was possible but required some additional boiler-plate code in the settings file. This boiler-plate is now no longer required.

This change makes it super easy to add a test build for a Gradle plugin and streamlines the process of implementing a Gradle plugin. You can also use this feature to conveniently work on changes to a plugin and builds that use that plugin at the same time, to implement a plugin that is both published and used by projects in the same source repository, or to structure a complex build into a number of plugins.

Using the plugins { } block also makes the Gradle Kotlin DSL much more convenient to use.

You can find out more about composite builds in the user manual.

APIs for better plugins

Gradle 6.0 makes it easier for you to build fast and correctly behaving plugins.

Minimizing the amount of work a task needs to do

With Gradle, it’s very simple to implement a task that is skipped when all of its inputs and outputs are up to date. This allows Gradle to perform incremental builds where only tasks that need to execute are executed.

For some tasks, less work can be done even when the task needs to execute. If only a few input files have changed since the last execution, the task only needs to reprocess files that have changed. Gradle calls these types of tasks incremental tasks. To implement an incremental task, use the InputChanges API.

Making work parallel-safe

Gradle 6.0 has a new API tasks can use for submitting units of work to be executed in parallel. Tasks that opt-in to this API give Gradle more flexibility to start other tasks in parallel.

Plugin authors using this API can move a task’s action into a separate classloader or even a separate process. Separate processes are reused when possible and expired if they consume too many resources. Separate classloaders allow the plugin a lot of flexiblity to control the classpath used by the task’s action.

The parameters into a unit of work are type-safe and isolated, so they can’t be modified concurrently to the unit of work’s action. Parameters can be optional (null) or be made up of complex types (e.g., file collections). There are several services that can be injected into an action to perform common functions like copying and deleting files or forking other processes.

Validating task configuration

Tasks that define their inputs or outputs incorrectly can cause problems when running incremental builds or when using the build cache. As part of an ongoing effort to bring these problems to light, Gradle 6.0 now reports these problems as deprecation warnings during the build.

When issues are detected, Gradle will show warnings when run with --warning-mode=all:

> Task :myTask
Property 'inputDirectory' is declared without normalization specified. 
Properties of cacheable work must declare their normalization via @PathSensitive, @Classpath or @CompileClasspath. 
Defaulting to PathSensitivity.ABSOLUTE. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.

Property 'outputFile' is not annotated with an input or output annotation. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.

Deprecation warnings will always show up in a Build Scan™ regardless of the command-line arguments used.

Transforming artifacts from dependencies

A dependency’s artifacts can take many forms–JARs, AARs, ZIPs and other formats. You may even need a format that isn’t published by the library you’re using.

As an example, take a Java module that publishes a normal JAR file. As a consumer, you want to use the normal JAR for development, but you need to use an obfuscated JAR for production builds instead.

Since the obfuscated JAR is not available in any repository, you must use the normal JAR and obfuscate it yourself.

Gradle provides an API for registering artifact transforms that hook into the dependency management resolution engine. An artifact transform can specify that whenever an obfuscated JAR is requested but can’t be found, Gradle should run a transform to create the obfuscated JAR and make it appear as an available artifact.

Other things you may have missed

There have been lots of changes since Gradle 5.0. Here are some other things you can enjoy in Gradle 6.0.

Bootstrap a new project with gradle init

Gradle 6.0 can bootstrap a project with gradle init and a few parameters.

You can choose one of these projects:

  • An application written in Java, Groovy, Kotlin, C++ or Swift
  • A library written in Java, Groovy, Scala, Kotlin, C++ or Swift
  • A Gradle plugin written in Java, Groovy or Kotlin
  • A basic, empty project

Quickly find the task you want to run by filtering by its group

Gradle 6.0 lets you quickly find tasks by showing only the tasks belonging to a particular group. This makes it easier to find tasks in builds with many available tasks.

Try it out with gradle tasks --group="Build Setup".

Stay up to date by failing on deprecation warnings

The warning-mode command line option has a fail option that will show all deprecation warnings and will fail the build if any deprecation warning is found during the build.

Incremental PMD analysis

Gradle 6.0’s PMD plugin supports using PMD’s incremental analysis cache to improve performance when files have not changed in between builds. To enable incremental analysis, add the following to your PMD configuration:

pmd {
    incrementalAnalysis = true
}

Executing Java applications with long classpaths

When Gradle detects that a Java process command-line will exceed Windows’s 32,768 character limit, Gradle will attempt to shorten the command-line by passing the classpath of the Java application via a “classpath jar”.

The classpath jar contains a manifest with the full classpath of the application. Gradle will only pass the generated jar on the command-line to the application. If the command-line is still too long, the Java process will fail to start as before.

If the command-line does not require shortening, Gradle will not change the command-line arguments for the Java process.

Artifact Signing

Gradle 6.0 has several signing related improvements:

  • The signing plugin uses SHA512 instead of SHA1
  • In-memory subkeys are supported

If you use the maven-publish or ivy-publish plugins, Gradle will automatically upload SHA256 and SHA512 signatures, in addition to the traditional but unsecure MD5 and SHA1 signatures.

Publication of SHA256 and SHA512 files is not supported by the deprecated maven plugin but works with the legacy uploadArchives task for Ivy repositories.

How to upgrade

We’ve provided a document to help you upgrade from Gradle 5.x to Gradle 6.0. If you’re using something older than Gradle 5.0, you may want to see all the new things in Gradle 5.0 first.

Before upgrading, we recommend you:

  • Upgrade to Gradle 5.6.4 using the Gradle wrapper. gradle wrapper --gradle-version=5.6.4
  • Run gradle help --scan to list all uses of deprecated Gradle APIs with their locations.
  • Update your Gradle plugins, especially those listed in the deprecations report from the Build Scan™.
  • See the troubleshooting guide or reach out on the community forums if you get stuck.

You can share feedback with the Gradle team via @gradle on Twitter. Go forth and Build Happiness!