Lazy configuration strategy with dependent tasks

There are some answers to this referring to taskGraph.whenReady and I’ve tested that approach and verified that it works. The answers are all pretty old however, and I know a lot has been done to improve lazy configuration in the last couple of years, so I’d like to verify if that’s still the preferred approach.

The setup is something like this:

  • Project A applies plugins P, Q, and S
  • P creates a task T1, which is configured based on passed in properties, -PsomeSetting=value.
  • Q then creates a task T2 to take the output from T1.
  • S also creates a task T3, which takes the output from T1, but it requires that T1 has been configured in a certain way, or it won’t work.

The “old” solution seem to be to use the following construct:

project.gradle.taskGraph.whenReady {
    if (it.hasTask(T3)) {
        // modify config of T1

This works, but at the same time feels a bit like a kludge.

I suppose T3 could verify that T1 has the right config and fail if it doesn’t, forcing the user to pass the right settings in this scenario, and that’s an acceptable solutions since most of this is automated anyway, but it also feels like a workaround.

Another approach would be to generate individual tasks for all permutations of T1 input properties, similar to what the native plugin does, and make sure that T3 depends on the output from T1WithConfigXYZ instead of a plain T1, and T2 would depend on T1WithConfigABC. What I worry is that I’ll end up with an explosion of permutations of task T1 that are rarely, if ever, used, and just pollute the tasks view.

I can filter out the permutations to some extent, as some don’t make any sense in my context, but there would still be a lot, and one would have to learn the naming of these tasks instead of just calling T1 to get the defaults, or T1 with -PsomeProperty=abc to override that single property.

While I understand that there are benefits to caching and incremental building if I use the latter approach, as one task ends up having more fixed inputs/outputs, the nature of T1 is that some of its dependencies are colliding anyway, and that’s nothing I can do anything about. In essence, T1 and any permutation I would create depends on potentially unrelated changes to the same files, so T1A and T1B would still affect each other.

To sum up, I have a working solution, I just wonder which one would be the most “gradle” one, either of the above, or something completely different.