The Gradle team is pleased to announce Gradle 3.4.
We're excited to bring you 3 incredible performance features in this release.
First up: Compile Avoidance. We've introduced a new mechanism for up-to-date checking of Java compilation that is sensitive to public API changes only. This means that if you change a comment or even a private API in a downstream project, Java compilation for upstream projects will be UP-TO-DATE
.
Next: A stable incremental Java compiler. We've smartened the handling of constants, backed it with in-memory caches, and fixed many bugs. It is now production-ready for your build and has been promoted out of @Incubating
.
Finally: A brand new Java Library Plugin. Use this when building a component intended to be used as a dependency from another project. It provides a strong separation between public (exported) and private code which not only gives great performance benefits (because consumers' compile classpaths are smaller) but also enforces architectural boundaries on the build level.
Put these together on our perf-enterprise-large benchmark Java project, and compilation time after a method body change is reduced from 2.5 minutes to 9 seconds! Let's put that in perspective:
It is not just large projects that reap the benefits, for example the same use case for Apache Polygene was reduced from 14 seconds to 7 seconds.
Now, with your help and guidance we've been able to made a couple of highly-requested code-quality plugins improvements:
A special thank you to those who voted and contributed to these issues.
Last but not least, we've made it more convenient to let Gradle know when you want a build scan — just use --scan
(or --no-scan
if not). No need for the "magic" system property -Dscan
anymore.
We hope you're able to build happiness with Gradle 3.4, and we look forward to your feedback via Twitter or on GitHub. Happy new year from the Gradle team!
Here are the new features introduced in this Gradle release.
This version of Gradle introduces a new mechanism for up-to-date checking of Java compilation that is sensitive to public API changes only.
If a dependent project has changed in an ABI-compatible way (only its private API has changed), then Java compilation tasks will now 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
. Even finer-grained compile-avoidance can be achieved by enabling incremental Java compilation, as explained below.
Some of the types of changes that do not affect the public API and are ignored:
Compile-avoidance can greatly improve incremental build time, as Gradle now avoids recompiling source files that will produce the same bytecode as the last time.
Compile-avoidance is deactivated if annotation processors are found on the compile classpath, because for annotation processors the implementation details matter. To better separate these concerns, the CompileOptions
for the JavaCompile
task type now defines a annotationProcessorPath
property.
If you are using annotation processors and want optimal performance, make sure to separate them from your compile classpath.
configurations {
apt
}
dependencies {
apt 'some.cool:annotation.processor:1.0'
}
tasks.withType(JavaCompile) {
options.annotationProcessorPath = configurations.apt
}
The Java incremental compiler has been improved to deal with constants in a smarter way.
Due to the way constants are inlined by the Java compiler, previous Gradle releases have taken a conservative approach and recompiled all sources when a constant has changed. Now Gradle avoids recompiling under the following conditions:
The incremental compiler will recompile only a small subset of the potentially affected classes now.
In addition, the incremental compiler is more efficient and backed by in-memory caches, which avoids a lot of disk I/O that slowed it down in previous versions.
The Java incremental compiler is no longer incubating and is now considered stable. This Gradle release includes many bug fixes and improved performance for incremental Java compilation.
Note that incremental Java compilation is not enabled by default. It needs to be activated explicitly. We encourage all Gradle users to give it a try in their projects.
The new Java Library plugin is the next step towards improved modeling of the Java ecosystem, and should be used whenever you are building a Java component aimed at being consumed by other components. This is typically called a library, and it has many advantages:
We strongly encourage users to migrate to this plugin, instead of the java
plugin, whenever they are building a library. Some of the new configurations of this plugin are available to the java
plugin too, and others are just deprecated.
compile
, you should use one of implementation
or api
runtime
, you should use runtimeOnly
Java compile-avoidance is implemented using a new @CompileClasspath annotation that can be attached to a task property, similar to the @InputFiles
or @Classpath
annotations.
This new annotation is also available for use in your own tasks as well, for those tasks that take a Java compile classpath. For example, you may have a task that performs static analysis using the signatures of classes. You can use the @CompileClasspath
annotation for this task instead of @InputFiles
or @Classpath
, to avoid running the task when the class signatures have not changed.
Gradle introduces a feature for the JaCoCo plugin strongly requested by the community: enforcing code coverage metrics. The JaCoCo plugin now provides a new task of type JacocoCoverageVerification
enabling the user to define and enforce violation rules. Coverage verification does not automatically run as part of the check
task. Please see the relevant user guide section on the “JaCoCo plugin” for more information.
tasks.withType(JacocoCoverageVerification) {
violationRules {
rule {
limit {
minimum = 0.5
}
}
}
}
Gradle keeps information about each task's inputs and outputs in your project's .gradle
directory. If this information is lost or cannot be read, your build directory can be in an inconsistent state with stale files from previous builds. GitHub issue #1018 is an example of the problems this can cause.
Gradle now removes the output directories for source sets when this situation is detected. Most often, this occurs when performing a Gradle upgrade because the information kept in .gradle
is not backwards compatible.
There are other situations where output files are not cleaned up, such as removing a sub project or a task from the build. You can follow the progress on GitHub issue #821.
The JaCoCo plugin has been upgraded to use JaCoCo version 0.7.8 by default.
You can now create a build scan by using the --scan
command line option. To explicitly disable creating a build scan, use the --no-scan
command line option.
For more information about build scans, see https://gradle.com.
It is relatively common to have tasks within a build that can be skipped because they have no input source. For example, the standard java
plugin creates a compileTestJava
task to compile all java source at src/test/java
. If at build time there are no source files in this directory the task can be skipped early, without invoking a Java compiler.
Previously in such scenarios Gradle would emit:
:compileTestJava UP-TO-DATE
This is now communicated as:
:compileTestJava NO-SOURCE
A task is said to have no source if all of its input file properties that are annotated with @SkipWhenEmpty
are empty (i.e. no value or an empty set of files).
APIs that communicate that outcome of a task have been updated to accommodate this new outcome.
The TaskSkippedResult.getSkipMessage()
of the Tooling API now returns NO-SOURCE
for such tasks, where it previously returned UP-TO-DATE
.
The TaskOutcome.NO_SOURCE
enum value of TestKit is now returned for such tasks, where it previously returned TaskOutcome.UP_TO_DATE
.
The WriteProperties
task that was introduced in Gradle 3.3 now supports deferred evaluation for properties:
WriteProperties.property(String, Object)
can be used to add a property with a Callable
or Object
that can be coerced into a String
.WriteProperties.properties(Map<String, Object>)
can be used to add multiple properties as above.When Gradle creates an archive, the order of the files in the archive is based on the order that Gradle visits each file. This means that even archive tasks with identical inputs can produce archives with different checksums. We have added initial support for reproducible archives, which tries to create an archive in a byte-for-byte equivalent manner.
For more information visit the section in the user guide about reproducible archives.
We would love to get feedback from you about this incubating feature!
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.
With the improvements made to the incremental Java compiler in this release, this is great time to promote this feature. If you want to make use of it, please keep in mind that it needs to be activated explicitly.
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 4.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.
Javadoc.setOptions(MinimalJavadocOptions)
is now deprecated. Use Javadoc.options()
to configure JavaDoc options.
JacocoTaskExtension.classDumpFile
is now called classDumpDir
and the old property is deprecated.
WriteProperties.getProperties()
returns an immutable collectionWriteProperties.setProperties()
generics have been added.NO-SOURCE
skip message when observing task execution via Tooling APINO_SOURCE
task outcome when testing with GradleRunnerPlease see Improved feedback when skipping tasks with no source input.
When the deprecated Javadoc.setOptions(MinimalJavadocOptions)
method is called with a StandardJavadocDocletOptions
, it replaces the task's own options
value. When calling the method with a parameter that is not a StandardJavadocDocletOptions
, only values declared by MinimalJavadocOptions
will be copied.
The fact that compileOnly
extends the compile
configuration was an oversight. It made it very hard for users to query for the dependencies that were actually "only used for compilation". With this release of Gradle, compileOnly
no longer extends the compile
configuration.
The mapping from Gradle's configurations to IntelliJ IDEA's scopes has been drastically simplified. There used to be a lot of hardcoded mappings and pseudo-scopes in order to reduce the number of dependency declarations in the .iml files. These hardcoded mappings were intransparent to the user and added a lot of complexity to the codebase. Thus we decided to reimplement IDEA mapping with a very simple scheme:
idea.module.scopes.SCOPENAME.plus/minus
just like a user wouldCOMPILE
, PROVIDED
, RUNTIME
and TEST
are valid scope names. The (undocumented) pseudo-scopes like RUNTIME_TEST
no longer have any effectjava
plugin
COMPILE
scope is emptyPROVIDED
scope contains the compileClasspath
configurationRUNTIME
scope contains the runtimeClasspath
configurationTEST
scope contains the testCompileClasspath
and testRuntimeClasspath
configurationsThis means that some runtime
dependencies might be visible when using auto-completion in test classes. This felt like a small price to pay, since the same was already true for testRuntime
dependencies.
We have thoroughly tested these new mappings and found them to work well. Nevertheless, if you encounter any problems importing projects into IDEA, please let us know.
When resolving the runtime classpath for Java applications, Gradle will now use the runtimeClasspath
configuration instead of the runtime
configuration. If you previously attached resolution rules to runtime
, you should apply them to runtimeClasspath
instead or apply them to all configurations.
Tooling providers should try not to depend on configurations directly, but use sourceSet.runtimeClasspath
where applicable. This always contains the correct classpath for the current Gradle version and has been changed to return the runtimeClasspath
configuration in this release. If you are directly resolving the runtime
configuration, your tool will not work with the java-library
plugin.
Since Gradle 3.3, configurations can be marked as not resolvable. If you or a plugin tries to resolve such a configuration, an IllegalStateException
will be thrown. You can check whether a configuration is resolvable by calling Configuration#isCanBeResolved()
. A configuration that cannot be resolved has a special meaning: it's often only there to declare dependencies only.
Although the concept had already been introduced in Gradle 3.3, the first release that comes with unresolvable configurations by default is Gradle 3.4. The Java and Java Library plugins add the following unresolvable configurations: "apiElements", "implementation", "runtimeElements", "runtimeOnly", "testImplementation", "testRuntimeOnly"
. The concept has been introduced in order to support variant aware dependency resolution.
We would like to thank the following community members for making contributions to this release of Gradle.
jdkName
from idea module model (gradle/gradle#989)We love getting contributions from the Gradle community. For information on contributing, please see gradle.org/contribute.
Known issues are problems that were discovered post release that are directly related to changes made in this release.
In the Gradle 3.3 release, we accidentally left out the name of one of our contributors. We would like to recognize Sebastien Requiem for his contribution: - S3 repository can be configured to authenticate using AWS EC2 instance metadata (gradle/gradle#690).