My team has written dozens of gradle plugins and have tried out different ways to do so. Recently we have been discussing what the best way is to create default tasks if a user does not manually create one them selves.
The pattern I have come to use is as follows:
// pseduo code
apply(project) {
// some code ...
project.afterEvaluate { setupDefaults(it) }
}
setupDefaults(project) {
// Pattern for a single task. Create a default MyTask if the user doesn't declare one
if (project.tasks.withType(MyTask).isEmpty()) {
// create MyTask with reasonable defaults
}
// Pattern for creating a default task for each parent task
if (project.tasks.withType(MyTask).isEmpty()) {
// withType passing in a closure will evaluate as objects are added to the collection
project.tasks.withType(MyParentTask) {
// create MyTask depending on MyParentTask with reasonable defaults
}
}
// Other patterns exist like generating tasks based on config found in an extension created by the plugin
}
Note that our situation is unique:
- We have a need to support java 6, 7, and 8 build environments.
- We support Gradle 2.8 - 4.10.3 (2.8-2.14.1 for java 6).
- Users always run with the latest version of a plugin
In order to work within these parameters we very careful as to which gradle APIs we use so we don’t have to maintain separate forks of a plugin for different gradle versions. Occasionally we replicate behavior instead of using inheritance to prevent runtime problems when running with different versions. I have tried using a rule based annotation mechanism to dynamically generate tasks but found that it was constantly changing and people found it to be harder to follow due to a lack of technical documentation on it. It also appears to be deprecated now.
The mechanism above works but has a drawback when there are many plugins in play all using afterEvaulate. Any default setup needs to be carefully crafted to work regardless of the order that plugins are applied (one way to do this is using the withType(type, closure) method on project asks).
Some coworkers of mine have expressed concern about issues that have arose because of using project afterEvaluate in plugins. I believe this mechanism is the best solution that I am aware of and order dependency issues would appear no matter what mechanism was used.
We are looking for input from gradle users or developers involved in plugin development. Is this the proper approach to take to generate default tasks dynamically or is there a better way? Is this an appropriate use for afterEvaluate in a plugin or are we abusing it?