Hi,

Something I'd like to try to achieve as part of the work to introduce multiple 
outputs for JVM based projects, is to create fewer task instances at 
configuration time. There are 2 main parts to this:

1. Don't create tasks that will never be required. For example, if there is no 
src/main/resources directory, and nothing generates it, then don't create a 
processResources task.
2. Don't create tasks that are not required for the current build. For example, 
if I'm generating the javadoc, don't create the test task or any of its 
dependencies.

Ignore for the moment how we might do this. Let's just say its possible to 
infer both #1 and #2.

Let's also say that if such as task is directly referenced in the build logic, 
then it's "required". For example, if I execute 
`myTask.dependsOn(processResources)` in my build script, then processResources 
is required and must be created. Possibly also for the command-line, so that if 
I run `gradle processResources`, then it is required.

The question is whether we consider it backwards compatible or not that we 
don't create certain tasks that used to always be created.

At first glance, it looks not too bad. The tasks don't do anything and nothing 
references them from code, so it's fine to leave them out. There are a few 
important benefits to doing this:

- There's less work to do at configuration time.
- There's less heap required to maintain the model through the whole build.
- We wouldn't log the execution of tasks that don't do anything. For example, 
if there is no test source, you wouldn't see 'testClasses', 'compileTestJava', 
'processTestResources' etc.
- It pushes the decoupling of 'what' and 'how', which means the 'how' can vary 
across different executions of the same build. For example, when generated 
source must be downloaded when building on one platform but can be generated 
when building on some other platform. Or when I'm running the tests against a 
distribution built by CI.

Unfortunately, there are a few places where the tasks leak out and are not 
referenced by name or path:

- Via the events on TaskContainer, such as tasks.all { } or tasks.whenAdded { }
- Iterating over the various collections of tasks, such as 
project.getTasks(name), or tasks.withType(type).
- Via the execution events, such as gradle.taskGraph.beforeTask { }

Strictly speaking, we'd still be following the contract for these things. In 
practice, though, I think there's an implicit contract that says that we must 
continue to create all possible tasks.

Some options:

1. Don't create the tasks, and simply document this as a potential breaking 
change (or, more likely, as series of changes).
2. Create alternatives to the above leakage points, but with stronger 
contracts. If you use one of the above, then we always create every possible 
task.
3. Toggle the strategy based on whether you're using the Gradle 2.0 
configuration model or not.

We might also choose a different option for tasks that are never required vs 
task that are required only for this build. I'd rather not do this, as I think 
it's important that we move towards the situation where we guarantee that we 
create only the tasks that are required, and don't care about the distinction 
between 'sometimes' and 'never' for tasks.


Thoughts?


--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com

Reply via email to