| Previous: Anatomy of a Gradle Plugin | Table of Contents | Next: Lifecycle of a Gradle Build |
Further Reading:
Gradle provides us with some tools to make building plugins easier. Particularly around creating types with less boilerplate. We'll cover how to use these and what it does behind the scenes.
Gradle has a concept of a "managed type". This is an abstract class that contains "managed properties" and should be the preferred way to make types in Gradle.
An example managed type is the extension we saw in the previous section:
public abstract class HelloExtension {
public abstract Property<String> getName();
public abstract RegularFileProperty getOutputFile();
}Here, we have an abstract class with no implementation that defines two properties, name and outputFile. When we instantiate it as an extension:
project.getExtension().create("hello", HelloExtension.class);Gradle makes an implementation class with all the boilerplate like this:
public final class HelloExtension_Generated extends HelloExtension {
private final Property<String> name;
private final RegularFileProperty outputFile;
@Inject
public HelloExtension_Generated(ObjectFactory objects) {
this.name = objects.property(String.class);
this.outputFile = objects.fileProperty();
}
@Override
public Property<String> getName() {
return name;
}
@Override
public RegularFileProperty getOutputFile() {
return outputFile;
}
}This is very similar to the commonly used immutables library. However, it is aware of Gradle types so will automatically create instances of properties (and other Gradle services) for us.
It is possible to write extensions and tasks directly as in the HelloExtension_Generated class above. If you look into the depths of older Palantir Gradle code you will find many instances of this, but this is no longer recommended. All new code should use Managed Types.
Prefer register an extension via
project.getExtensions().create("name", MyExtension.class);rather than
project.getExtensions().add("name", new MyExtension(/* … */));create asks Gradle to
- build a managed instance (using Gradle’s code-generation),
- wire that instance into Gradle’s lifecycle and dependency-injection system
This is mostly stylistic but is important as our Errorprones are built to detect extensions instantiated via create
Similarly, for tasks, they should look like below rather than being manually specified:
public abstract class SomeTask extends DefaultTask {
@Input
public abstract Property<String> getName();
@Output
public abstract RegularFileProperty getOutputFile();
@TaskAction
public final void action() {
// ...
}
} Note
This repository ships with the NonAbstractGradleType errorprone, which automatically fixes your Gradle tasks to be abstract.
| Previous: Anatomy of a Gradle Plugin | Table of Contents | Next: Lifecycle of a Gradle Build |