What's new in Gradle 8.0

Gradle 8.0 banner

Gradle 8.0 marks the next major milestone in Gradle’s ongoing mission to revolutionize build automation and enhance developer productivity. Following the release of Gradle 7.0 in May 2021, we’ve dedicated ourselves to streamlining usability so that performing common tasks is effortless, enabling you to concentrate on maximizing productivity.

The major improvements since Gradle 7.0 are organized into the following categories:

Upgrade now to try them with your build or start a new project with Gradle.

If you want to stay up-to-date with new Gradle features and other developments, subscribe to our newsletter and follow us on Twitter or Mastodon.

Performance

Slow builds can have detrimental effects and that shipping faster and more frequently is important, especially for larger companies like Square. With every Gradle release, the goal is to improve performance, ensuring that each iteration surpasses its predecessor.

Configuration cache

Faster configuration time

Every Gradle build has a lifecycle: initialization, configuration, and execution. Many performance features focus on the execution phase; however, the configuration phase can be time-consuming. Without the configuration cache the configuration phase needs to be repeated even if there is no change to the configuration.

To improve this situation, the experimental feature called configuration cache was introduced in version 6.6. This reduces the cost of the configuration phase by caching the result and reusing this for subsequent builds. The configuration phase only needs to run if there is a change in how the project is configured. This is an opt-in feature in Gradle 8.0 that saves people significant time. In Gradle 8.1 the configuration cache will be stable.

Faster execution via parallelism

Running tasks in parallel using the –parallel flag already improves build time. Still, tasks in the same project are not allowed to execute in parallel unless they are specifically written to do so. With the configuration cache enabled, Gradle can perform even better parallelism.

Gradle runs tasks in parallel when the configuration cache is enabled without the –parallel flag. All tasks can run in parallel, even those within the same project because the configuration cache prevents the tasks from interfering with each other’s configuration. Tasks that are configuration cache compatible do not need to be changed to be parallel-safe.

Faster Java compilation

Faster incremental compilation

By default, Gradle uses a Java incremental compiler to make builds faster by only compiling Java source files that need to be compiled. Incremental compilation has been around for over 5 years, but some users were starting to reach a limit with large source sets.

Incremental compilation works by analyzing the relationship between classes. Previously, this analysis was only saved locally and prevented Gradle from performing incremental compilation immediately after a cache hit from the build cache. In projects with large source sets, the cost of recompiling all source files after a cache hit negated the time saved by the cache hit.

The incremental analysis is now saved in the build cache. As a result, compilation after a cache hit will be incremental.

Additionally, the incremental compile analysis has been made faster and uses less memory than Gradle 7.0.

The impact of these changes will vary by project but can be extremely large. On the Gradle project itself, these changes made incremental compilation up to twice as fast!

Changes to constants no longer trigger a full recompilation

Because of how the Java compiler works, previous Gradle releases were forced to perform a full recompilation as soon as any constant was changed in an upstream dependency.

Gradle now performs constant usage tracking and only recompiles the classes that use a constant that has changed.

This can speed up incremental builds for projects with classes that contain many constants, such as generated code from template engines.

More cache hits between operating systems for Java compilation

Gradle calculates the cache key for a task by looking at its inputs. When checking out source code on different operating systems, the line endings can make the inputs of a task appear different, even though the task produces identical outputs.

Gradle now normalizes line endings in source files when compiling Java code to get better cache hits between operating systems.

More responsive continuous build

Continuous Build automatically re-executes the build with the requested tasks when inputs change. This allows for continuous feedback during development.

Due to the implementation details of some recent JDKs, Continuous Build often did not work well on Windows and macOS on Java 9+. It could take up to 10 seconds to detect a change and trigger a build.

Continuous Build responds quickly to changes on Windows and macOS when Gradle is run using new Java versions. Gradle now uses its own robust and natively implemented file system watching system instead of relying on the generic API in the JDK.

Usability

Java toolchains

You’re probably familiar with the pain of using the wrong version of Java and experiencing build failures, but it’s just as important to use the correct vendor. Java Toolchain support was introduced in Gradle 6.7, allowing you to specify a project’s JDK. Toolchains have since become the best way to enforce this, with support continuing through the 7.x line and 8.0.

    java {
      toolchain {
        languageVersion.set(JavaLanguageVersion.of(11))
        vendor.set(JvmVendorSpec.ADOPTIUM)
      }
    }

Gradle can still automatically detect the installed toolchains, but we provide increased flexibility in provisioning through a Toolchain Resolver Plugin. This gives you much more flexibility and control over where you obtain your JDK so you can use the specific one to match your needs.

Build Scan™ now shows the Java toolchain usage

build scan image

Test suites

A common use case when writing tests is to group test classes to organize them into manageable chunks so that you can run them with different frequencies or at distinct points in your build pipeline. For example, you may want to define groups of unit tests, integration tests, and functional tests. Doing this correctly required a thorough knowledge of how to modify and connect various domain objects in Gradle, like SourceSets, configurations, and tasks.

You’ll be glad to know that the process has been vastly simplified by Introducing Test Suites. The JVM Test Suite Plugin simplifies the creation of such groups of tests referred to as Test Suites. Note that this is not to be confused with testing framework suites, like JUnit4’s Suite or JUnit Jupiter’s Suite Engine.

Test Suites are a high-level declarative concept that can be referred to directly within build scripts. You can configure dependencies, sources, and the testing framework the tests use without worrying about low-level details.

For example, you can create an integration testing test suite by adding the following snippet to a Java project:

testing {
  suites {
    // Add a new test suite
    integrationTest(JvmTestSuite) {
      // Use JUnit Jupiter as a testing framework
      useJUnitJupiter('5.7.1')

      // depend on the production code for tests
      dependencies {
        implementation project
      }
    }
  }
}

// Run integration tests as part of check
tasks.named('check') {
  dependsOn(testing.suites.integrationTest)
}

Version catalog

Version catalog code

Gradle introduced version catalogs as an experimental feature in 7.0. If you haven’t tried version catalogs yet to manage your dependencies, now is the time to make the switch. It is used to declare the versions of all direct dependencies used in your build in one central location, the file gradle/libs.versions.toml. Some of the advantages include type-safe accessing, common versioning across dependencies, and bundling dependencies together.

Through the 7.x line and 8.0, version catalogs have improved in many ways. Below are some specific examples.

Declaring plugin versions

Version catalogs already support declaring versions of your libraries. However, these declarations were not accessible to the plugins and buildscript blocks. This limitation is lifted, and it’s possible to declare plugins, for example, in the TOML file:

Version catalog code

Using them in the plugins block like this:

Plugin block code

Version catalog type unsafe API changes

When using the type unsafe API, all methods accepting alias references can now use the same string as the alias definition. This means that you can declare and reference groovy-json instead of being forced to use groovy.json in the type unsafe API.

Note that access to the type unsafe API has changed; please see the upgrade guide.

Kotlin DSL

Gradle’s Kotlin DSL provides an alternative syntax to the traditional Groovy DSL with an enhanced editing experience in supported IDEs, superior content assistance, refactoring, documentation, and more. A lot of effort has been devoted to making sure the Kotlin experience is exceptional.

Improved script compilation performance

The Kotlin script compilation has traditionally been slower than Groovy script compilation in many situations. Gradle 8.0 can make Kotlin script compilation up to 20% faster by introducing an interpreter for the declarative plugins {} blocks in .gradle.kts scripts. Calling the Kotlin compiler for declarative plugins {} blocks is avoided by default.

To benefit from this performance improvement, use the supported formats in the plugins {} blocks. For more information on plugin syntax, read the documentation on constrained syntax.

Type-safe accessors for extensions of repositories {} in Kotlin DSL

Kotlin DSL generates type-safe model accessors for custom extensions added to the repositories {} block. Custom extensions have full content assistance in the IDE.

For instance, the [asciidoctorj-gems-plugin](https://asciidoctor.github.io/asciidoctor-gradle-plugin/master/user-guide/#asciidoctorj-gems-plugin) plugin adds a custom extension. You no longer need withGroovyBuilder and instead have this succinct syntax:

Kotlin code

Build configuration

Build configuration has seen many changes over the years. The current best practice is sometimes using buildSrc and sometimes included builds. At scale, sometimes included builds are more performant, and sometimes buildSrc are. The performance depends on how exactly things are structured. However, many projects still use apply scripts (which lack IDE auto-completion) or even allprojects/subprojects (making it difficult to determine where shared configuration comes from, and which are inflexible and not performant).

In working towards the unification of buildSrc and included builds, buildSrc is changing to behave more like included builds. Note that this means buildSrc tests are not automatically run, and you can address buildSrc tasks like regular included build tasks via the command line.

BuildSrc

If you’re using buildSrc already, keep using it and enjoy the new features. If you’re using included builds already, keep using them. If your build performance needs improvement, experiment with both solutions and pick the best combination for your project.

Ecosystem support upgrades

Support for Java 17 through 19

Gradle supports compiling, testing, and running on Java 17, 18 and 19.

Support for Groovy 4

Gradle supports building software with Groovy 4.0. Note that Groovy DSL build scripts still use Groovy 3.

Newer Scala compiler

The default Scala Zinc version was updated to 1.6.1.

Zinc is the Scala incremental compiler that allows Gradle to always compile the minimal set of files needed by the current file changes. It considers which methods are being used and which have changed, which means it’s much more granular than interfile dependencies.

New Kotlin features in Kotlin DSL

Build scripts written with the Kotlin DSL use Kotlin API level 1.8. Previously, build scripts were limited to Kotlin API level 1.4. This change brings all the improvements made to the Kotlin language and standard library since Kotlin 1.4.0.

For information about breaking and non-breaking changes in this upgrade, visit the upgrading guide.

How to upgrade

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

Before upgrading, you should:

  • upgrade to Gradle 7.6.1 using the Gradle wrapper. gradle wrapper --gradle-version=7.6.1
  • 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™
  • upgrade to Gradle 8.0.2 using the Gradle wrapper. gradle wrapper --gradle-version=8.0.2
  • see the troubleshooting guide or reach out on the community forums if you get stuck

You can share feedback with the Gradle team via Twitter or Mastodon. Go forth and Build Happiness!