Hi, folks! When defining dependencies between task...
# community-support
j
Hi, folks! When defining dependencies between tasks, I use
project.tasks.named<Type>("taskName")
, like:
Copy code
project.tasks.register("myTask") {
  val anotherTaskProvider = project.tasks.named<Type>("anotherTask")

  foo.convention(anotherTaskProvider.map { ... })

  dependsOn(anotherTaskProvider)
}
This works only when
anotherTask
is defined before
myTask
. Is there a way to retrieve a named task provider even if it doesn’t exist?
1
a
a good workaround:
Copy code
dependsOn(tasks.matching { it.name == "anotherTask" })
j
Yeah, I was hoping to avoid
matching
🙂
v
tasks.matching
is a really bad idea here, as it totally breaks task configuration avoidance for each and every task if used like shown by Adam. Only when combined with
configureEach
, it does not break task configuration avoidance and is fine to use. Exactly to solve this problem
tasks.named { ... }
was introduced in latest version. Unfortunately, the implementation is totally broken which makes it behave exactly like
matching
. But once that is fixed, it is the way to go if you have recent enough Gradle version. To minimize the task-configuration breakage while using non-stringy API, you could do
tasks.withType<Type>().matching { ... }
, so that it is only broken for tasks of type
Type
. A probably better way in this situation would indeed be to use string-y API:
Copy code
project.tasks.register("myTask") {
   val anotherTask = project.tasks.withType<Type>().matching { it.name == "anotherTask" }.configureEach {
       foo.convention(it.map { ... })
    }
    dependsOn("anotherTask")
}
But actually, all this is just work-around, even with
tasks.named { ... }
. The real question is, why do you need an explicit
dependsOn
? Every explicit
dependsOn
that does not have a lifecycle task on the left-hand side is a code smell. If you properly wire inputs to outputs, you get the necessary task dependencies automatically. There might be situations where you have old tasks that are not property-enabled where you need it, but in your case, you use the task provider to configure a
Property
, so as long as you do not break the chain with something like
flatMap
to something that is not an output property, you get the task dependency automatically when necessary. For example if someone sets a custom value for
foo
, with your explicit depending, "anotherTask" would still be executed while it is not needed anymore. If it is still needed even though
foo
has a different value, then the question is why, and whether then not some other wiring is missing.
j
An explicit
dependsOn
isn’t necessary, indeed. I was unaware that dependencies are set even if you use the task’s output.
The trick with
.matching
did the job, thanks!
Copy code
inline fun <reified T : Task> Project.resolveTask(name: String) = provider {
    tasks.withType<T>().matching { it.name == name }.single()
}

val nameTaskProvider = project.resolveTask<Type>("name")
v
That's generally a bad idea still. By using
single()
you make it eager and break task-configuration avoidance. So when using that snippet, you still break task-configuration avoidance for all tasks of type
T
that are already registered and also do not get the task if it is not registered already. If that construct works in your case, you should just use
tasks.named<Type>("name")
which has the same behavior that it only works if the task is already registered while not breaking task-configuration avoidance even for that single task.
j
My problem is that some tasks are not yet registered when calling
tasks.named()
This could be solved, with
Copy code
inline fun <reified T : Task> Project.resolveTask(name: String) = provider {
   tasks.named<T>(name).get()
}
🥲
v
Oh, I missed the
provider { ... }
But what do you do with the
nameTaskProvider
?
With both of the last two variants, you will loose the implicit task dependency that the task provider would have had.
j
I’m using
nameTaskProvider
to feed the other task with produced data, simple case.
Copy code
val patchPluginXmlTaskProvider = project.resolveTask<PatchPluginXmlTask>(Tasks.PATCH_PLUGIN_XML)
pluginXml.convention(patchPluginXmlTaskProvider.flatMap { it.outputFile })
I’d love to stick to the initial
project.tasks.named<Type>("anotherTask")
but the
anotherTask
gets registered after the one I currently configure.
v
Ah, you flatMap to an output property to which the task dependency got forwarded, so you get it back. That might indeed work then.
Then I guess the named with get in provider should work fine
j
Anyway, I’ll try to rearrange the code to avoid that in the first place.
Thanks for checking on that!
👌 1