Gradle Release Notes

Version 4.6

The Gradle team is pleased to announce Gradle 4.6.

First and foremost, this release of Gradle includes built-in support for JUnit Platform and the JUnit Jupiter/Vintage Engine, also known as JUnit 5 support. You can use the new filtering and engines functionality in JUnit 5 using the examples provided below and in the documentation.

Thank you to the JUnit team for helping to achieve JUnit Platform support, and a special thank you to Andrew Oberstar for extraordinary contributions toward this effort.

Also regarding testing, you can now improve your testing feedback loop when running JVM-based tests using the new fail-fast option for Test tasks, which stops the build immediately after the first test failure.

// Example JUnit 5 and fail-fast test configuration
test {
    useJUnitPlatform {
        excludeTags 'slow'
        includeEngines 'junit-jupiter', 'junit-vintage'
    }

    failFast = true
}

Moving on to dependency management improvements: you can now declare dependency constraints for transitive dependencies and avoid problems caused by oft-hidden upstream dependency changes.

This release also features enhanced Maven dependency compatibility: support for importing BOMs, optional dependencies, and compile/runtime separation when consuming POMs. For now you must enable these features by adding enableFeaturePreview('IMPROVED_POM_SUPPORT') to your settings.gradle file, as they break backward compatibility in some cases.

This version of Gradle also comes with a couple especially useful new APIs for task development. You can now declare custom command-line flags for your custom tasks, for example: gradle myCustomTask --myfoo=bar. In addition, tasks that extend Test, JavaExec or Exec can declare rich arguments for invoking the underlying executable. This allows for better modeling of tools like annotation processors.

Speaking of annotation processors, it is now more convenient to declare dependencies that are annotation processors through the new annotationProcessor dependency configuration. Using a separate dependency configuration for annotation processors is a best practice for improving performance.

Kotlin DSL v0.15.6 is included in this release of Gradle, and features initialization scripts support, nicer script compilation error reporting, performance improvements, and better IntelliJ IDEA integration. Details are available in the linked release notes.

We hope you will build happiness with Gradle 4.6, and we look forward to your feedback via Twitter or on GitHub.

Table Of Contents

Upgrade instructions

Switch your build to use Gradle 4.6 quickly by updating your wrapper properties:

gradle wrapper --gradle-version=4.6

Standalone downloads are available at gradle.org/releases.

New and noteworthy

Here are the new features introduced in this Gradle release.

JUnit 5 support

JUnit 5 is the latest version of the well-known JUnit test framework. JUnit 5 is composed of several modules:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

The JUnit Platform serves as a foundation for launching testing frameworks on the JVM. JUnit Jupiter is the combination of the new programming model and extension model for writing tests and extensions in JUnit 5. JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform.

Gradle now provides native support for JUnit Jupiter/Vintage Engine on top of JUnit Platform. To enable JUnit Platform support, you just need to add one line to your build.gradle:

test {
    useJUnitPlatform()
}

Moreover, Tagging and Filtering can be enabled via:

test {
    useJUnitPlatform {
        // includeTags 'fast'
        excludeTags 'slow'

        // includeEngines 'junit-jupiter', 'junit-vintage'
        // excludeEngines 'custom-engine'
    }
}

You can find more information on test grouping and filtering in the Java Plugin documentation.

JUnit Jupiter Engine

To enable JUnit Jupiter support, add the following dependencies:

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.0'
}

Put your first Jupiter test into src/test/java/foo/bar:

package foo.bar;

import org.junit.jupiter.api.Test;

public class JUnitJupiterTest {
    @Test
    public void ok() { 
    }
}

Now you can run gradle test to see the results of your JUnit 5 tests!

You can find a sample of test with JUnit Jupiter at samples/testing/junitplatform/jupiter in the '-all' distribution of Gradle.

JUnit Vintage Engine

If you want to run JUnit 3/4 tests on JUnit Platform, you should add extra JUnit Vintage Engine dependency:

test {
    useJUnitPlatform()
}

dependencies {
    testCompileOnly 'junit:junit:4.12' 
    testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.1.0' 
}

You can mix JUnit 3/4 tests with Jupiter tests without the need to rewrite old tests. A sample of mixed tests can be found at samples/testing/junitplatform/engine in the '-all' distribution of Gradle.

Note: Use of JUnit 5 features requires Java 8 or higher.

Fail fast option for Test tasks

Gradle now supports stopping Test tasks after the first failed test. Projects with large test suites can take a long time to execute even though a failure occurred early on leading to unnecessary wait times (especially on CI). To enable this fail fast behavior in your build file, set the failFast property to true:

test {
    failFast = true
}

In addition, this behavior can be enabled from the command line for individual build invocations. An invocation looks like:

gradle integTest --fail-fast

More information is available in the Java Plugin documentation for test execution.

Allow declared reasons for dependency and resolution rules

In complex builds, it can become hard to interpret dependency resolution results and why a dependency declaration or a rule was added to a build script. To improve on this situation, we extended all the corresponding APIs with the capability to define a reason for each declaration or rule.

These reasons are shown in dependency insight reports and error messages if the corresponding declaration or rule influenced the resolution result. In the future, they will also be shown in build scans.

dependencies {
    implementation('org.ow2.asm:asm:6.0') {
        because 'we require a JDK 9 compatible bytecode generator'
    }
}

Dependency constraints for transitive dependencies

With dependency constraints, Gradle adds a mechanism to express constraints over transitive dependencies which are used during dependency resolution.

dependencies {
    implementation 'org.apache.httpcomponents:httpclient'
}

dependencies {
    constraints {
        implementation('org.apache.httpcomponents:httpclient:4.5.3') {
            because 'previous versions have a bug impacting this application'
        }
        implementation('commons-codec:commons-codec:1.11') {
            because 'version 1.9 pulled from httpclient has bugs affecting this application'
        }
    }
}

In the example, the version of commons-codec that is brought in transitively is 1.9. With the constraint, we express that we need at least 1.11 and Gradle will now pick that version during dependency resolution.

Compared to dependency management rules you can define in the resolution strategy, which are applied after dependency resolution to fix up the result, dependency constraints are declared information that participate in the dependency resolution process. This means that all constraints are considered in combination. For instance, if another dependency brings in an even higher version of commons-codec, Gradle will respect that.

Expressing the same in a traditional dependency substitution rule requires you to repeat part of the dependency resolution process manually – which is expensive and error prone. That is, you would have to inspect the version that was selected and implement a decision based on the version:

resolutionStrategy.eachDependency { DependencyResolveDetails details ->
    if (details.requested.group == 'commons-codec'
        && details.requested.name == 'commons-codec'
        && VersionNumber.parse(details.requested.version) < VersionNumber.parse('1.11')) {
            details.useVersion '1.11'
    }
}

Note: Dependency constraints are not yet published, but that will be added in a future release. This means that their use currently only targets builds that do not publish artifacts to maven or ivy repositories.

BOM import

Gradle now provides support for importing bill of materials (BOM) files, which are effectively .pom files that use <dependencyManagement> to control the dependency versions of direct and transitive dependencies. It works by declaring a dependency on a BOM.

dependencies {
    // import a BOM
    implementation 'org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE'

    // define dependencies without versions
    implementation 'com.google.code.gson:gson'
    implementation 'dom4j:dom4j'
}

Here, for example, the versions of gson and dom4j are provided by the Spring Boot BOM.

Note: This is a Gradle 5.0 feature preview, which means it is a potentially breaking change that will be activated by default in Gradle 5.0. It can be turned on in Gradle 4.6+ by adding enableFeaturePreview('IMPROVED_POM_SUPPORT') in settings.gradle.

Support for optional dependencies in POM consumption

Gradle now creates a dependency constraint for each dependency declaration in a POM file with <optional>true</optional>. This constraint will produce the expected result for an optional dependency: if the dependency module is brought in by another, non-optional dependency declaration, then the constraint will apply when choosing the version for that dependency (e.g., if the optional dependency defines a higher version, that one is chosen).

Note: This is a Gradle 5.0 feature preview, which means it is a potentially breaking change that will be activated by default in Gradle 5.0. It can be turned on in Gradle 4.6+ by adding enableFeaturePreview('IMPROVED_POM_SUPPORT') in settings.gradle.

Compile/runtime scope separation in POM consumption

Since Gradle 1.0, runtime scoped dependencies have been included in the Java compile classpath, which has some drawbacks:

Now, if this new behavior is turned on, the Java and Java Library plugins both honor the separation of compile and runtime scopes. Meaning that the compile classpath only includes compile scoped dependencies, while the runtime classpath adds the runtime scoped dependencies as well. This is in particular useful if you develop and publish Java libraries with Gradle where the api/implementation dependencies separation is reflected in the published scopes.

Note: This is a Gradle 5.0 feature preview, which means it is a potentially breaking change that will be activated by default in Gradle 5.0. It can be turned on in Gradle 4.6+ by adding enableFeaturePreview('IMPROVED_POM_SUPPORT') in settings.gradle.

Customizable metadata file resolution

Gradle now allows you to explicitly state for which metadata files it should search in a repository. Use the following to configure Gradle to fail-fast resolving a dependency if a POM file is not found first.

repositories {
     mavenCentral {
         metadataSources {
             mavenPom() // Look for Maven '.pom' files
             // artifact() - Do not look for artifacts without metadata file
         }
     }
}

This avoids a 2nd request for the JAR file when the POM is missing, making dependency resolution from Maven repositories faster in this case.

Convenient declaration of annotation processor dependencies

It is now even easier to add annotation processors to your Java projects. Simply add them to the annotationProcessor configuration:

dependencies {
    annotationProcessor 'com.google.dagger:dagger-compiler:2.8'
    implementation 'com.google.dagger:dagger:2.8'
}

Declaring annotation processors on a separate configuration improves performance by preserving incremental compilation for tasks that don't require annotation processors.

Tasks API allows custom command-line options

Sometimes a user wants to declare the value of an exposed task property on the command line instead of the build script. Being able to pass in property values on the command line is particularly helpful if they change more frequently. With this version of Gradle, the task API now supports a mechanism for marking a property to automatically generate a corresponding command line parameter with a specific name at runtime. All you need to do is to annotate a setter method of a property with Option.

The following examples exposes a command line parameter --url for the custom task type UrlVerify. Let's assume you wanted to pass a URL to a task of this type named verifyUrl. The invocation looks as such: gradle verifyUrl --url=https://gradle.org/. You can find more information about this feature in the documentation on declaring command-line options.

import org.gradle.api.tasks.options.Option;

public class UrlVerify extends DefaultTask {
    private String url;

    @Option(option = "url", description = "Configures the URL to be verified.")
    public void setUrl(String url) {
        this.url = url;
    }

    @Input
    public String getUrl() {
        return url;
    }

    @TaskAction
    public void verify() {
        getLogger().quiet("Verifying URL '{}'", url);

        // verify URL by making a HTTP call
    }
}

Rich command-line arguments for Test, JavaExec or Exec tasks

Gradle 4.5 added the possibility to add CommandLineArgumentProviders to CompileOptions, thus enabling plugin authors to better model tools like annotation processors.

Now we introduce CommandLineArgumentProviders to Exec, JavaExec and Test, both to model command line arguments (Exec and JavaExec) as well as JVM options (JavaExec and Test).

For example, the built-in jacoco plugin uses this new feature to declare the inputs and outputs of the JaCoCo agent added to the test task.

class JacocoAgent implements CommandLineArgumentProvider {

    private final JacocoTaskExtension jacoco;

    public JacocoAgent(JacocoTaskExtension jacoco) {
        this.jacoco = jacoco;
    }

    @Nested
    @Optional
    public JacocoTaskExtension getJacoco() {
        return jacoco.isEnabled() ? jacoco : null;
    }

    @Override
    public Iterable<String> asArguments() {
        return jacoco.isEnabled() ? ImmutableList.of(jacoco.getAsJvmArg()) : Collections.<String>emptyList();
    }

}

task.getJvmArgumentProviders().add(new JacocoAgent(extension));

For this to work, JacocoTaskExtension needs to have the correct input and output annotations.

See the documentation about tasks with nested inputs for information how to leverage this feature in custom plugins.

Logging options for debugging build caching

This version of Gradle introduces a property org.gradle.caching.debug which causes individual input property hashes to be logged on the console. For example, when running gradle compileJava -Dorg.gradle.caching.debug=true --build-cache the output would be:

> Task :compileJava
Appending taskClass to build cache key: org.gradle.api.tasks.compile.JavaCompile_Decorated
Appending classLoaderHash to build cache key: 0e1119759d236086191ff6fbd26c610f
Appending actionType to build cache key: _BuildScript_$_run_closure1_769114ba780efdb8aed68d70a323d160
Appending actionClassLoaderHash to build cache key: c383666cbb0f1bf25d832b944f228c44
Appending actionType to build cache key: org.gradle.api.tasks.compile.JavaCompile_Decorated
Appending actionClassLoaderHash to build cache key: 0e1119759d236086191ff6fbd26c610f
Appending inputPropertyHash for 'options.class' to build cache key: 74824162f3f111308fa9dc95c82b65a6
Appending inputPropertyHash for 'options.compilerArgs' to build cache key: 8222d82255460164427051d7537fa305
...
Appending inputPropertyHash for 'source' to build cache key: 55786645cf0e6dcf6a3d48b1b43bf687
Appending outputPropertyName to build cache key: destinationDir
Build cache key for task ':compileJava' is 2221655c6648a7e9baf61a6234de8658

In earlier versions of Gradle, this output was logged on the INFO log level. This does not happen anymore, and the --info logs should be much less verbose now while the build cache is enabled.

Caching for Scala compilation when using the play plugin

The task PlatformScalaCompile is now cacheable. This means that Play projects written in Scala now also benefit from the build cache!

Improved Visual Studio IDE support for multi-project builds

Previous versions of Gradle would only generate Visual Studio solution files for a given component and its dependencies. This made it difficult to work on multiple components in a build at one time as a developer would potentially need to open multiple Visual Studio solutions to see all components. When the visual-studio plugin is applied, Gradle now has a visualStudio task on the root project that generates a solution for all components in the multi-project build. This means there is only one Visual Studio solution that needs to be opened to be able to work on any or all components in the build.

Improvements in gradle-native plugins

The gradle-native has been working on adding new features for building with native languages (like C++). As mentioned above, Gradle now generates a single Visual Studio solution for a multi-project build. Other unannounced features have continued to be developed, which we hope to detail in a future blog post.

Highlights to the gradle-native plugins will continue to be mentioned in Gradle's release notes, but more information will be provided in the gradle-native release notes.

Documentation updates

Documentation in this release of Gradle has significantly improved in the following areas:

Please reach out to @gradle on Twitter or through GitHub to provide feedback to help ensure improvements are useful for you.

Honour cache-expiry settings in the presence of detached configurations

Gradle allows dependency cache expiry (i.e cacheChangingModulesFor) to be set on a per-configuration basis. However, due to a bug in previous versions of Gradle, if a dependency was first resolved via a configuration using the default (24hr) expiry settings, any other resolve in the same build invocation would get the same result.

Normally this wouldn't be a big deal, since most users set the same expiry everywhere using configurations.all. The catch is that plugins like io.spring.dependency-management use detached configurations, which are excluded from configurations.all. If a build was using one of these plugins, the detached configuration could be resolved first, causing later resolves to obtain the same (possibly stale) result.

This nasty cache-expiry bug has now been fixed. Users can trust that Gradle will return the most up-to-date SNAPSHOT or version available as long as the dependency cache expiry is set correctly.

Default JaCoCo version upgraded to 0.8.0

The JaCoCo plugin has been upgraded to use JaCoCo version 0.8.0 by default.

Promoted features are features that were incubating in previous versions of Gradle but are now supported and subject to backwards compatibility. See the User guide section on the “Feature Lifecycle” for more information.

The following are the features that have been promoted in this Gradle release.

Build cache and task output caching marked stable

The build cache and task output caching were first introduced in Gradle 3.5. They are used in production by different teams, including the Gradle team itself, with great results. As of Gradle 4.6, the build cache and task output caching are no longer incubating and considered public features.

Note that the SPI to implement your own build cache service stays incubating.

TestKit marked stable

TestKit was first introduced in Gradle 2.6 to support developers with writing and executing functional tests for plugins. In the course of the Gradle 2.x releases, a lot of new functionality was added. This version of Gradle removes the incubating status and makes TestKit a stable feature.

CompileOptions.annotationProcessorPath property

The CompileOptions.annotationProcessorPath property has been promoted and is now stable.

Fixed issues

Deprecations

Features that have become superseded or irrelevant due to the natural evolution of Gradle become deprecated, and scheduled to be removed in the next major Gradle version (Gradle 5.0). See the User guide section on the “Feature Lifecycle” for more information.

The following are the newly deprecated items in this Gradle release. If you have concerns about a deprecation, please raise it via the Gradle Forums.

Putting annotation processors on the compile classpath or explicit -processorpath compiler argument

Putting processors on the compile classpath or using an explicit -processorpath compiler argument has been deprecated and will be removed in Gradle 5.0. Annotation processors should be added to the annotationProcessor configuration instead. If you don't want any processing, but your compile classpath contains a processor unintentionally (e.g. as part of some library you use), use the -proc:none compiler argument to ignore it.

Play 2.2 is deprecated in Play plugin

The use of Play 2.2 with the the Play plugin has been deprecated and will be removed with Gradle 5.0. It is highly recommended to upgrade to a newer version of Play.

CompilerArgumentProvider replaced by CommandLineArgumentProvider

The interface CompilerArgumentProvider has been deprecated. Use CommandLineArgumentProvider instead.

Potential breaking changes

Local build cache directory cleanup is now time-based

Previously, Gradle would clean up the local build cache directory only if the size of its contents reached 5 GB, or whatever was configured in targetSizeInMB. From now on Gradle will instead clean up everything older than 7 days, regardless of the size of the cache directory. As a consequence targetSizeInMB is now deprecated, and changing its value has no effect.

The minimum age for entries to be cleaned up can now be configured in settings.gradle via the removeUnusedEntriesAfterDays property:

buildCache {
    local {
        removeUnusedEntriesAfterDays = 30
    }
}

Added annotationProcessor configurations

The java-base plugin will now add an <sourceSetName>AnnotationProcessor configuration for each source set. This might break when the user already defined such a configuration. We recommend removing your own and using the configuration provided by java-base.

Changes to Visual Studio IDE configuration

VisualStudioExtension no longer has a solutions property. There is now only a single solution for a multi-project build that can be accessed through the VisualStudioRootExtension on the root project. For instance:

model {
    visualStudio {
        solution {
            solutionFile.location = "vs/${name}.sln"
        }
    }
}

Removed Visual Studio IDE tasks

There are no longer tasks to generate Visual Studio solution files for each component in the build. There is now only a single task (visualStudio) in the root project that generates a solution containing all components in the build.

Removed StartParameter.taskOutputCacheEnabled

The deprecated property StartParameter.taskOutputCacheEnabled has been removed. Use StartParameter.buildCacheEnabled instead.

HttpClient library upgraded to version 4.5.5

Gradle has been upgraded to embed HttpClient version 4.5.5 over 4.4.1.

Kotlin Standard Library for Java 8 artifact changes

Gradle now bundles the Kotlin Standard Library Java 8 artifact kotlin-stdlib-jdk8 instead of kotlin-stdlib-jre8 as a follow up to the upgrade to Kotlin 1.2. This change might affect your build, please see the Kotlin documentation about this change.

External contributions

We would like to thank the following community members for making contributions to this release of Gradle.

We love getting contributions from the Gradle community. For information on contributing, please see gradle.org/contribute.

Known issues

Known issues are problems that were discovered post release that are directly related to changes made in this release.