The Gradle team is pleased to bring you Gradle 2.10. This release delivers significant performance improvements for large Native software builds, together with a number of bug fixes and smaller improvements.
Major progress has been made on the Rule-based configuration and Software Model infrastructure. We are excited about the opportunities this work will bring, and the progress that has been made. With full support for dependency management, an improved DSL and better support for developing plugins, the Gradle 2.10 release brings this revolutionary way of building software closer to the mainstream.
buildScript
dependenciesHere are the new features introduced in this Gradle release.
Gradle needs to know all input properties, input files and output files of a task to perform incremental build checks. When no input or output files have changed, Gradle can skip executing a task.
For native compilation tasks, Gradle used to consider the contents of all include directories as inputs to the task. This had performance problems when there were many include directories or when the project root directory was used as an include directory. To speed up up-to-date checks, Gradle now treats the include path as an input property and only considers those header files that are actually referenced by a source file as a inputs. Our performance benchmarks have demonstrated this to have a large positive impact on incremental builds for very large native projects.
For C++ and related languages, Gradle uses source file parsing to determine the set of included headers for a source file. If Gradle cannot determine the set of included headers, it will fall back to the old mechanism of including all files in all include directories as inputs. This means that using a macro to define an #include
file is not recommended, as it can prevent Gradle from taking advantage of this optimization.
Declaring a dependency on gradleTestKit()
includes all of the Gradle TestKit runtime classpath. In previous versions of Gradle this TestKit dependency also included transitive dependencies on some Gradle core classes and external libraries. This could lead to version conflicts between the TestKit runtime classpath and user-defined dependency declarations.
Gradle TestKit now avoids polluting the runtime classpath by using a fat and shaded JAR file for any required Gradle core classes and external dependencies.
buildScript
dependenciesThe new buildEnvironment
task can be used to obtain better insight into the buildscript
dependencies of a Gradle project. This task is implicitly available for all projects, much like the existing dependencies
task.
Use the buildEnvironment
task to gain an understanding of how the declared dependencies of project's build script are actually resolved, and to debug issues with plugin loading and classloading.
The feature was kindly contributed by Ethan Hall.
The Checkstyle
task now produces a HTML report on failure in addition to the existing XML report. This more human friendly HTML report is generated by default, and when available will be advertised in preference to the XML report.
This feature was kindly contributed by Sebastian Schuberth.
The Tooling API now exposes the Java source language level that should be used for an Eclipse project via the javaSourceSettings
property.
This allows Buildship
and other IDE integrations to automatically configure the source language level in Eclipse, so that users no longer need to configure this themselves.
This release adds some important new features for dependency management for Java libraries in the incubating Java software model.
In most cases it is more natural and convenient to define dependencies on a component rather than on each of its source sets and it is now possible to do so when defining a Java library in the software model.
Example:
apply plugin: "jvm-component"
model {
components {
main(JvmLibrarySpec) {
dependencies {
library "core"
}
}
core(JvmLibrarySpec) {
}
}
}
Dependencies declared this way will apply to all source sets for the component.
It is now possible to declare dependencies on external modules for a Java library in the software model:
repositories {
jcenter()
}
model {
components {
main(JvmLibrarySpec) {
dependencies {
// external module dependency can start with either group or module
group 'com.acme' module 'artifact' version '1.0'
module 'artifact' group 'com.acme' version '1.0'
// shorthand module notation also works, version number is optional
module 'com.acme:artifact:1.42'
}
}
}
}
Module dependencies declared this way will be resolved against the configured repositories as usual. External dependencies can be declared for a Java library, Java source set or Java library API specification.
This release includes a number of improvements to the model DSL, which is the DSL you use to define and configure the software model from a build script.
The ModelMap
creation and configuration DSL syntax now defines nested rules, each with its own inputs. For example, this means that an element of a ModelMap
can now be configured using the configuration of a sibling as input:
model {
components {
mylib { ... }
test {
// Use `mylib` as input. When this code runs, it has been fully configured and will not change any further
// Previously, this would have been treated as an input of the `components` rule, resulting in a dependency cycle
targetPlatform = $.components.mylib.targetPlatform
}
}
}
And because tasks
is a ModelMap
, this means that a task can be configured using another task as input using the same syntax:
model {
tasks {
jar { ... }
dist(Zip) {
// Use the `jar` task as input. It has been fully configured and will not change any further
def jar = $.tasks.jar
from jar.output
into someDir
}
}
}
This is also available for the various methods of ModelMap
, such as all
or withType
:
model {
components {
all {
// Adds a rule for each component
...
}
withType(JvmLibrarySpec) {
// Adds a rule for each JvmLibrarySpec component
...
}
}
}
@Managed
typeThe properties of a @Managed
type can now be configured using nested configure methods:
model {
components {
mylib {
sources {
// Adds a rule to configure `mylib.sources`
...
}
binaries {
// Adds a rule to configure `mylib.binaries`
...
}
}
}
}
This is automatically added for any property whose type is @Managed
, or a ModelMap<T>
or ModelSet<T>
. Note that for this release, these nested closures do not define a nested rule, and the closure is executed as soon as it is encountered in the containing closure. This will be improved in the next Gradle release.
See the model DSL user guide section for more details and examples.
The model DSL now supports automatic conversions between various scalar types, making it very easy to use one type for another. In particular, you can use a String
wherever a scalar type is expected. For example:
enum FailType {
FAIL_BUILD,
WARNING
}
@Managed
interface CoverageConfiguration {
double getMinClassCoverage()
void setMinClassCoverage(double minCoverage)
double getMinPackageCoverage()
void setMinPackageCoverage(double minCoverage)
FailType getFailType()
void setFailType(FailType failType)
File getReportTemplateDir()
void setReportTemplateDir(File templateDir)
}
model {
coverage {
minClassCoverage = '0.7' // can use a `String` where a `double` was expected
minPackageCoverage = 1L // can use a `long` where a `double` was expected
failType = 'WARNING' // can use a `String` instead of an `Enum`
templateReportDir = 'src/templates/coverage' // creates a `File` which path is relative to the current project directory
}
}
This release includes some major capabilities to allow plugin authors to extend the software model
LanguageSourceSet
model elementsThis release allows source sets (subtypes of LanguageSourceSet
) to be added to arbitrary locations in the managed model. A LanguageSourceSet
can be attached to any @Managed
type as a property, or used for the elements of a ModelSet
or ModelMap
, or as a top level model element.
The BinarySpec
and ComponentSpec
types can now be extended via @Managed
subtypes, allowing for declaration of @Managed
components and binaries without having to provide a default implementation. LibrarySpec
and ApplicationSpec
can also be extended in this manner.
Example:
@Managed
interface SampleLibrarySpec extends LibrarySpec {
String getPublicData()
void setPublicData(String publicData)
}
class RegisterComponentRules extends RuleSource {
@ComponentType
void register(ComponentTypeBuilder<SampleLibrarySpec> builder) {
}
}
apply plugin: RegisterComponentRules
model {
components {
sampleLib(SampleLibrarySpec) {
publicData = "public"
}
}
}
Now it is possible to attach a @Managed
internal view to any BinarySpec
or ComponentSpec
type. This allows plugin authors to attach extra properties to already registered binary and component types like JarBinarySpec
.
Example:
@Managed
interface MyJarBinarySpecInternal extends JarBinarySpec {
String getInternal()
void setInternal(String internal)
}
class CustomPlugin extends RuleSource {
@BinaryType
public void register(BinaryTypeBuilder<JarBinarySpec> builder) {
builder.internalView(MyJarBinarySpecInternal)
}
@Mutate
void mutateInternal(ModelMap<MyJarBinarySpecInternal> binaries) {
// ...
}
}
apply plugin: "jvm-component"
model {
components {
myComponent(JvmLibrarySpec) {
binaries.withType(MyJarBinarySpecInternal) { binary ->
binary.internal = "..."
}
}
}
}
Note: @Managed
internal views registered on unmanaged types (like JarBinarySpec
) are not yet visible in the top-level binaries
container, and thus it's impossible to do things like:
// This won't work:
model {
binaries.withType(MyJarBinarySpecInternal) {
// ...
}
}
This feature is available for subtypes of BinarySpec
and ComponentSpec
.
It is now possible to declare a default implementation for a base component or a binary type, and extend it via further managed subtypes.
interface MyBaseBinarySpec extends BinarySpec {}
class MyBaseBinarySpecImpl extends BaseBinarySpec implements MyBaseBinarySpec {}
class BasePlugin extends RuleSource {
@ComponentType
public void registerMyBaseBinarySpec(ComponentTypeBuilder<MyBaseBinarySpec> builder) {
builder.defaultImplementation(MyBaseBinarySpecImpl.class);
}
}
@Managed
interface MyCustomBinarySpec extends BaseBinarySpec {
// Add some further managed properties
}
class CustomPlugin extends RuleSource {
@ComponentType
public void registerMyCustomBinarySpec(ComponentTypeBuilder<MyCustomBinarySpec> builder) {
// No default implementation required
}
}
This functionality is available for unmanaged types extending ComponentSpec
and BinarySpec
.
The goal of the new internal views feature is for plugin authors to be able to draw a clear line between public and internal APIs of their plugins regarding model elements. By declaring some functionality in internal views (as opposed to exposing it on a public type), the plugin author can let users know that the given functionality is intended for the plugin's internal bookkeeping, and should not be considered part of the public API of the plugin.
Internal views must be interfaces, but they don't need to extend the public type they are registered for.
Example: A plugin could introduce a new binary type like this:
/**
* Documented public type exposed by the plugin
*/
interface MyBinarySpec extends BinarySpec {
// Functionality exposed to the public
}
// Undocumented internal type used by the plugin itself only
interface MyBinarySpecInternal extends MyBinarySpec {
String getInternalData();
void setInternalData(String internalData);
}
class MyBinarySpecImpl implements MyBinarySpecInternal {
private String internalData;
String getInternalData() { return internalData; }
void setInternalData(String internalData) { this.internalData = internalData; }
}
class MyBinarySpecPlugin extends RuleSource {
@BinaryType
public void registerMyBinarySpec(BinaryTypeBuilder<MyBinarySpec> builder) {
builder.defaultImplementation(MyBinarySpecImpl.class);
builder.internalView(MyBinarySpecInternal.class);
}
}
With this setup the plugin can expose MyBinarySpec
to the user as the public API, while it can attach some additional information to each of those binaries internally.
Internal views registered for an unmanaged public type must be unmanaged themselves, and the default implementation of the public type must implement the internal view (as MyBinarySpecImpl
implements MyBinarySpecInternal
in the example above).
It is also possible to attach internal views to @Managed
types as well:
@Managed
interface MyManagedBinarySpec extends MyBinarySpec {}
@Managed
interface MyManagedBinarySpecInternal extends MyManagedBinarySpec {}
class MyManagedBinarySpecPlugin extends RuleSource {
@BinaryType
public void registerMyManagedBinarySpec(BinaryTypeBuilder<MyManagedBinarySpec> builder) {
builder.internalView(MyManagedBinarySpecInternal.class);
}
}
Internal views registered for a @Managed
public type must themselves be @Managed
.
This functionality is available for types extending ComponentSpec
and BinarySpec
.
Binary names are now scoped to the component they belong to. This means multiple components can have binaries with a given name. For example, several library components might have a jar
binary. This allows binaries to have names that reflect their relationship to the component, rather than their absolute location in the software model.
Previous versions of Gradle TestKit included a number of additional dependencies in the test runtime classpath. These dependencies, such as Google Guava, are no longer automatically usable by functional test code. If an external dependency is required by test code, it must be explicitly declared in order to be available on the test classpath.
Previously, Gradle considered all files in all include path directories as inputs to a compile task. This had performance problems and could cause tasks to be out of date when they should not be. This has been fixed but may cause some subtle differences to the way changes are detected for compilation tasks.
Gradle now considers a compile task to be out-of-date (and require full recompilation) when the include path is changed. In older releases, Gradle would only recompile source files if the resolved set of headers changed. This meant that you could reorder the include path without necessarily causing any files to be recompiled.
Once Gradle has compiled a file once, it will only be recompiled if it or one of the included headers has changed. This means that Gradle will not detect a new header file added to the include path, where that file that should be used in preference to an existing header file. If a compilation task has an include path of [ first/, second/ ]
and a source file includes header.h
from second/
, if a new file called header.h
is added to first/
, Gradle will not detect the change, and will consider the source file compilation to be up to date.
We plan to address this limitation in a future version of Gradle.
RuleSource
must not be private, and all other methods must be private.BinarySpec.name
should no longer be considered a unique identifier for the binary within a project.jar
in mylib
will have a build task called mylibJar
binaries
container is now a ModelMap
instead of a DomainObjectContainer
. It is still accessible as BinaryContainer
.ComponentSpec.sources
and BinarySpec.sources
now have true ModelMap
semantics. Elements are created and configured on demand, and appear in the model report.BinarySpec.sources
from the top-level binaries
container: this functionality will be re-added in a subsequent release.FunctionalSourceSet
is now a subtype of ModelMap
, and no longer extends Named
ComponentSpec
, BinarySpec
or LanguageSourceSet
, if defined, is no longer visible. These elements can only be accessed using their public types or internal view types.DependentSpec
API is now polymorphic. For dependencies declared by project and library, use ProjectDependencySpec
.jar
rather than one qualified with the library name.JarBinarySpec
has been changed: what was previously createMyLibApiJar
is now simply myLibApiJar
.org.gradle.language.PreprocessingTool
has moved to org.gradle.nativeplatform.PreprocessingTool
We would like to thank the following community members for making contributions to this release of Gradle.
buildEnvironment
task.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.