Together with the usual set of bug fixes and new features, this release comes with some major performance improvements. Expect to see faster builds that use less heap space with Gradle 1.4.
If you work with Scala and/or Groovy projects, you'll find Gradle more convenient with the auto-detection of Scala and Groovy libraries from the classpath. A Gradle build no longer needs to specifically configure a 'groovy' or 'scalaTools' configuration to use these tools. Automatic configuration makes it easier to build multiple variants of your software targeting different Scala/Groovy versions, or to use Scala/Groovy only for selected sourceSets.
Dependency management continues to be an important focus and this release brings some significant improvements, better reporting, and a new, powerful mechanism providing fine-grained control over dependency resolution.
Keeping Gradle at the forefront of software automation are some exciting new incubating features. Configuration-on-demand can reduce startup time for large multi-project builds, the java-library-distribution plugin makes it easy to bundle your java project into a distribution, and the new TestReport task makes it trivial to aggregate all of your test results into a single report.
TestNG users haven't been forgotten, either. They now enjoy the same high-quality reports that JUnit users have long had access to.
And that's just the highlights. Read on for more details on why you should upgrade to Gradle 1.4. As always, please share your feedback and experiences with Gradle 1.4 via the Gradle Forums.
Gradle 1.4 introduces some important performance improvements, resulting in faster builds that use less heap space. These improvements affect dependency resolution, task up-to-date checks and test execution. In other words, everything that a typical Java based project uses in its build.
A typical Java project might see a 10-20% improvement in build time. For builds with many small projects and many unit tests, in combination with the Gradle daemon, we are seeing a 50% improvement in build time.
As mentioned below, resolution of Maven SNAPSHOT versions is now faster, due to fewer network requests and various other internal changes.
Artifact meta-data is now cached more efficiently, making it much faster to read and write to disk. This means that resolution of all dependencies can be performed more quickly. In particular, reading of cached meta-data is much faster, so that resolution of dependencies that have been cached is much faster.
This in turn means that task up-to-date checks are faster, as typically a large portion of the time to perform an up-to-date check is made up of the time it takes to resolve the dependencies that form the task inputs.
Test execution typically has a significant effect on build time. While your actual test code won't run any faster in Gradle 1.4, Gradle is now much more efficient in managing test execution and generating result and report files.
Gradle runs tests in a separate worker JVM, to keep the build and tests isolated from each other. The mechanism for controlling these workers and dispatching tests to them is now much more efficient, meaning that tests will start to execute in the worker processes more quickly, and that the CPU cores can spend more of their time executing tests. This mechanism is now much more robust and can handle a very large number of test classes, fixing a number of deadlock conditions.
Previously, Gradle collected the test results as XML files and generated the HTML report from these XML files. Test results are now written in an efficient binary format, and the XML files and HTML report generated from this. The work of generating the results has moved from the test worker processes to the parent build process, meaning that the workers can spend more of their time executing tests, while the results are generated asynchronously by the parent. In addition, all result and report file generation streams the content to file, keeping heap usage at a minimum.
As with every release, the dependency resolution engine has been improved with bug fixes and performance optimizations.
These improvements are in addition to the new incubating support for Dependency Resolve Rules, which give you more control over dependency resolution.
When checking whether a Maven SNAPSHOT dependency has been updated remotely, fewer network requests are now made to the repository. Previously, multiple requests were made to the maven-metadata.xml
file where now only one request is made (GRADLE-2585).
This results in faster dependency resolution when using Maven SNAPSHOT dependencies, in particular when importing into an IDE.
Before Gradle downloads a dependency artifact from a remote repository, it will selectively search the local file system for that exact same file (i.e. a file with the exact same checksum). For example, Gradle will search the user's “local maven” repository. If the file is found, it will be copied from this location which is much faster than downloading the file (which would be exactly the same) over the network. This is completely transparent and safe.
The algorithm used to search for “local candidates” has been improved and is now faster. This affects all builds using dependency management, especially when building for the first time (GRADLE-2546).
For dependencies originating in Maven repositories, Gradle follows Maven semantics and treats any dependency artifact whose version number ends in '-SNAPSHOT
' as “changing”, which means that it can change over time and Gradle should periodically check for updates instead of caching indefinitely (see “controlling caching”). Previously, artifacts with classifiers (e.g sources
or javadoc
) were not being checked for changes. This has been fixed in this release (GRADLE-2175).
--offline
modePreviously, Gradle discarded cached artifacts just prior to attempting to fetch an updated version from the remote source. If the fetch of the remote artifact failed (e.g. network disruption), there was no longer a cached version available to be used in --offline
mode. This resulted in some situations where trying to use --offline
mode in response to unexpected network issues would not work well.
Gradle now only discards old artifacts after a newer version has been cached, making --offline
mode more reliable and useful (GRADLE-2364).
By default, an Ivy repository will store the module "org.my.group:module:version
" with a directory pattern like baseurl/org.my.group/module
, while a maven repository would store the same module under baseurl/org/my/group/module
. It is now possible to configure an ivy
repository that uses the maven directory layout, using the new m2compatible
flag with the pattern
layout.
With earlier versions of Gradle, it was not possible to generate a dependency resolution report if one of the dependencies could not be resolved. This has been rectified. Not only will the dependency resolution reports run successfully if a dependency cannot be resolved, the reports will now include details of those dependencies that couldn't be resolved.
Here is an example for the dependencies
task:
compile - Classpath for compiling the sources. \--- foo:bar:1.0 \--- foo:baz:2.0 FAILED
The FAILED
marker indicates that foo:baz:2.0
, which is depended upon by foo:bar:1.0
, couldn't be resolved.
A similar improvement has been made to the dependencyInsight
task:
foo:baz:2.0 (forced) FAILED foo:baz:1.0 -> 2.0 FAILED \--- foo:bar:1.0 \--- compile
In this example, foo:baz
was forced to version 2.0
, and that version couldn't be resolved.
The dependencies
task now accepts an optional --configuration
parameter that restricts its output to a particular configuration:
$ gradle dependencies --configuration compile
This command will display the dependency tree rooted at the compile
configuration, and (assuming a standard Java project) omit the dependency trees for the runtime
, testCompile
, and testRuntime
configurations.
GroovyCompile
and Groovydoc
tasksThe groovy-base
plugin now automatically detects the Groovy dependency used on the compile class path of any GroovyCompile
or Groovydoc
task, and appropriately configures the task's groovyClasspath
. As a consequence, the Groovy dependency can now be configured directly for the configuration(s) that need it, and it is no longer necessary to use the groovy
configuration.
Old (and still supported):
dependencies {
groovy "org.codehaus.groovy:groovy-all:2.0.5"
}
New (and now preferred):
dependencies {
compile "org.codehaus.groovy:groovy-all:2.0.5"
}
Automatic configuration makes it easier to build multiple artifact variants targeting different Groovy versions, or to only use Groovy for selected source sets:
dependencies {
testCompile "org.codehaus.groovy:groovy-all:2.0.5"
}
Apart from the groovy-all
Jar, Gradle also detects usages of the groovy
Jar and -indy
variants. Automatic configuration is disabled if a task's groovyClasspath
is non-empty (for example because the groovy
configuration is used) or no repositories are declared in the project.
ScalaCompile
and Scaladoc
tasksThe scala-base
plugin now automatically detects the scala-library
dependency used on the compile class path of any ScalaCompile
or ScalaDoc
task, and appropriately configures for the task's scalaClasspath
. As a consequence, it is no longer necessary to use the scalaTools
configuration.
Old (and still supported):
dependencies {
scalaTools "org.scala-lang:scala-compiler:2.9.2"
compile "org.scala-lang:scala-library:2.9.2"
}
New (and now preferred):
dependencies {
compile "org.scala-lang:scala-library:2.9.2"
}
Automatic configuration makes it easier to build multiple artifact variants targeting different Scala versions. Here is one way to do it:
apply plugin: "scala-base"
sourceSets {
scala2_8
scala2_9
scala2_10
}
sourceSets.all { sourceSet ->
scala.srcDirs = ["src/main/scala"]
resources.srcDirs = ["src/main/resources"]
def jarTask = task(sourceSet.getTaskName(null, "jar"), type: Jar) {
baseName = sourceSet.name
from sourceSet.output
}
artifacts {
archives jarTask
}
}
repositories {
mavenCentral()
}
dependencies {
scala2_8Compile "org.scala-lang:scala-library:2.8.2"
scala2_9Compile "org.scala-lang:scala-library:2.9.2"
scala2_10Compile "org.scala-lang:scala-library:2.10.0-RC5"
}
Note that we didn't have to declare the different scala-compiler
dependencies, nor did we have to assign them to the corresponding ScalaCompile
and ScalaDoc
tasks. Nevertheless, running gradle assemble
produces:
$ ls build/libs scala2_10.jar scala2_8.jar scala2_9.jar
With build variants becoming a first-class Gradle feature, building multiple artifact variants targeting different Scala versions will only get easier.
Automatic configuration isn't used if a task's scalaClasspath
is non-empty (for example because the scalaTools
configuration is used) or no repositories are declared in the project.
Gradle 1.3 introduced several incubating improvements to TestNG reports. In Gradle 1.4 the improved reports are turned on by default. The TestNG users will be delighted to learn that:
The implementation of the new reports is now a part of Gradle. Previously, the report generation was delegated to TestNG's default listeners that are shipped with TestNG library. You can switch off the HTML report generation by configuring the test.testReport property. If you prefer the old TestNG reports please refer to the documentation.
The Tooling API, the standard way to embed Gradle, is now more convenient to use. As of Gradle 1.4 it ships as a single jar with the only external dependency being an SLF4J implementation. All other dependencies are packaged inside the Jar and shaded to avoid conflicts with the embedder's classpath.
Incubating features are intended to be used, but not yet guaranteed to be backwards compatible. By giving early access to new features, real world feedback can be incorporated into their design. See the User guide section on the “Feature Lifecycle” for more information.
The following are the new incubating features or changes to existing incubating features in this Gradle release.
A “dependency resolve rule” is a user specified algorithm that can influence the resolution of a particular dependency. Dependency resolve rules can be used to solve many challenging dependency management problems.
For example, a dependency resolve rule can be used to force all versions with a particular “group” to be of the same version:
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.gradle') {
details.useVersion '1.4'
}
}
}
The rule (i.e. the closure given to the eachDependency
method above) is called for each dependency that is to be resolved. The DependencyResolveDetails
object passed to the rule implementation represents the originally requested and the finally selected version (after conflict resolution has been applied). The rule can make a programmatic choice to change how the dependency should be resolved.
This is an “incubating” feature. In Gradle 1.4, it is only possible to affect the version of the dependency that will be resolved. In future versions, more control will be allowed via the DependencyResolveDetails
type.
Dependency resolve rules are a powerful feature that allow you to do much more than just enforcing a certain version of a dependency in advance (which you can also do with Gradle). Many interesting use cases can be implemented with the dependency resolve rules:
For more information, including more code samples, please refer to this user guide section.
In Gradle, all projects are configured before any task gets executed (see the build lifecycle). Huge multi-project builds may have a noticeable configuration time for that reason. To improve the experience of working with large multi-project builds "configuration on demand" mode is introduced, where only those projects required by the build are configured. This mode is incubating and currently it is not guaranteed to work with every multi-project build. It should work very well with builds that have decoupled projects (e.g. avoiding having a subproject accessing the model of another project). Before you start configuring on demand, please read the section in the user guide. Then update your gradle.properties file:
#gradle.properties file
systemProp.org.gradle.configuration.ondemand=true
The new incubating 'java-library-distribution
' plugin, contributed by Sébastien Cogneau, makes it is much easier to create a standalone distribution for a JVM library.
Let's walk through a small example. Assume a project with the following layout:
MyLibrary |____build.gradle |____libs // a directory containing third party libraries | |____a.jar |____src | |____main | | |____java | | | |____SomeLibrary.java | |____dist // additional files that should go into the distribution | | |____dir2 | | | |____file2.txt | | |____file1.txt
In the past, it was necessary to declare a custom zip
task that assembles the distribution. Now, the 'java-library-distribution' will do the job for you:
apply plugin: 'java-library-distribution'
dependencies {
runtime files('libs/a.jar')
}
distribution {
name = 'MyLibraryDistribution'
}
Given this configuration, running gradle distZip
will produce a zip file named MyLibraryDistribution.zip
that contains the library itself, its runtime dependencies, and everything in the src/dist
directory.
To add further files to the distribution, configure the distZip
task accordingly:
distZip {
from('aFile')
from('anotherFile') {
into('dist')
}
}
A new, incubating TestReport
task type is now available. This task takes the test results generated by one or more Test
tasks and generates a combined HTML test report from them. For example, you can use this task to generate a single test report for all the projects in the build:
task testReport(type: TestReport) {
destinationDir = file("$buildDir/reports/all-tests")
reportOn subprojects*.test
}
The test report task currently combines test results, but does not aggregate the test results for a given class. So, if a given class is run by multiple Test
tasks, only one execution of the class will be included in the report and the other executions of that class will be discarded. This will be addressed in a later Gradle version.
For more details, see the user guide
ivy.xml
without publishingThe incubating 'ivy-publish
' plugin introduces a new GenerateIvyDescriptor
task generates the Ivy metadata file (a.k.a. ivy.xml
) for publication. The task name for the default Ivy publication is 'generateIvyModuleDescriptor
'.
This function used to be performed internally by the PublishToIvyRepository
task. By having this function be performed by a separate task you can generate the ivy.xml
metadata file without having to publish your module to an Ivy repository, which makes it easier to test/check the descriptor.
The GenerateIvyDescriptor
task also allows the location of the generated Ivy descriptor file to changed from its default location at ‘build/publications/ivy/ivy.xml
’. This is done by setting the destination
property of the task:
apply plugin: 'ivy-publish'
group = 'group'
version = '1.0'
// … declare dependencies and other config on how to build
generateIvyModuleDescriptor {
destination = 'generated-ivy.xml'
}
Executing gradle generateIvyModuleDescriptor
will result in the Ivy module descriptor being written to the file specified. This task is automatically wired into the respective PublishToIvyRepository
tasks, so you do not need to explicitly call this task to publish your module.
The new incubating ‘maven-publish’ plugin is an alternative to the existing ‘maven’ plugin, and will eventually replace it. This plugin builds on the new publishing model that was introduced in Gradle 1.3 with the ‘ivy-publish’ plugin. The new publication mechanism (which is currently “incubating”, including this plugin) will expand and improve over the subsequent Gradle releases to provide more convenience and flexibility than the existing publication mechanism plus very powerful features to wire your components across builds & teams.
In the simplest case, publishing to a Maven repository looks like:
apply plugin: 'java'
apply plugin: 'maven-publish'
group = 'group'
version = '1.0'
// … declare dependencies and other config on how to build
publishing {
repositories {
maven {
url 'http://mycompany.org/repo'
}
}
}
To publish, you simply run the publish
task. The POM file will be generated and the main artifact uploaded to the declared repository. To publish to your local Maven repository (ie 'mvn install') simply run the publishToMavenLocal
task. You do not need to have mavenLocal
in your publishing.repositories
section.
To modify the generated POM file, you can use a programmatic hook that modifies the descriptor content as XML.
publications {
maven {
pom.withXml {
asNode().appendNode('description', 'A demonstration of maven POM customisation')
}
}
}
In this example we are adding a ‘description
’ element for the generated POM. With this hook, you can modify any aspect of the POM. For example, you could replace the version range for a dependency with the actual version used to produce the build. This allows the POM file to describe how the module should be consumed, rather than be a description of how the module was built.
For more information on the new publishing mechanism, see the new User Guide chapter.
Much of a task's configuration influences how, or even if, a task should execute. After the task has executed, changing the configuration has no useful effect. For example, it does not make sense to add an action via the doFirst()
method to a task that is executing or has already executed. Changing such configuration has been deprecated and this will become an error condition in Gradle 2.0.
Once a task has started executing, its action list should no longer be changed. This includes calling the following methods on Task
objects:
setActions()
doLast()
- including using the synonymous <<
operatordoFirst()
Mutating the collection returned by getActions()
is also deprecated after the task has started executing.
Once a task has started executing, its dependencies should no longer be changed. This includes calling the following methods on Task
objects:
dependsOn()
setDependsOn()
Once a task has started executing, its configuration controlling whether it will be execution should no longer be changed. This includes calling the following methods on Task
objects:
onlyIf()
setOnlyIf()
setEnabled()
Once a task has started executing, its “inputs” configuration should no longer be changed. This includes calling the following methods on TaskInputs
objects:
files()
file()
dir()
property()
properties()
source()
sourceDir()
Once a task has started executing, its “outputs” configuration should no longer be changed. This includes calling the following methods on TaskOutputs
objects:
upToDateWhen()
files()
file()
dir()
DependencyReportTask
and DependencyInsightReportTask
no longer fail when dependencies cannot be resolvedPreviously, these tasks types would fail if one or more dependencies could not be resolved. Now, they no longer fail and instead display the failed dependencies in the appropriate place in the output.
DependencyInsightReportTask
throws better exception on bad configurationPreviously, when the task's configuration was invalid a ReportException
would be thrown when the task started to execute. For consistency with other tasks, it now throws a InvalidUserDataException
.
Configuration
also copies its resolution strategyPreviously, a copied Configuration
object shared the same resolutionStrategy
object as the Configuration
that it was copied from. This meant that changes to resolutionStrategy
of the source or the copy effected both instances and resulted in undesirable side affects. Copying a Configuration
now also creates a discrete copy of the resolutionStrategy
.
Jvm.getSupportsAppleScript()
In the deprecated internal class org.gradle.util.Jvm
the method getSupportsAppleScript()
has been removed.
If you need to check if the running JVM supports AppleScript, you can use the following code:
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("AppleScript");
boolean isAppleScriptAvailable = engine != null;
Breaking changes have been made to the new, incubating, Ivy publishing support.
Previously, it was possible to set the descriptorFile
property on an IvyPublication object. This property has been removed with the introduction of the new GenerateIvyDescriptor
task. To specify where the ivy.xml
file should be generated, set the destination
property of the GenerateIvyDescriptor
task.
Previously all configurations of the project were published. Now, only the ‘archives
’ configuration together with the ‘default
’ configuration and its ancestors will be published. In practice, this means that a Java project's testCompile
and testRuntime
configurations will no longer be published by default.
TestNGOptions.useDefaultListeners
The default value for TestNGOptions.useDefaultListeners has changed from true
to false
so that Gradle can take over generation of the reports. This way Gradle can provide invaluable improvements to the reporting - for more information read the earlier section on TestNG reports.
The default version of Checkstyle used for the 'checkstyle
' plugin has been updated from 5.5
to 5.6
.
The default version of CodeNarc used for the 'codenarc
' plugin has been updated from 0.16.1
to 0.18
.
eclipseWtpComponent
task overrides dependent modulesPreviously, the eclipse-wtp
plugin's eclipseWtpComponent
task would add generated dependent-module
entries to those already contained in the .settings/org.eclipse.wst.common.component
file. This could lead to stale and duplicated entries (see GRADLE-2526
). Now, existing entries are overridden with generated entries, just like it's done for classpathentry
elements in .classpath
files.
We would like to thank the following community members for making contributions to this release of Gradle.
java-library-distribution
pluginshowViolations
flag for Checkstyle
task (GRADLE-1656)m2compatible
flag on PatternRepositoryLayout
(GRADLE-1919)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.