Sebastian Schuberth
03/17/2025, 11:33 AMdependsOn()
for task dependencies, but to just consume output from task dependencies in as input in another task? I believe that's what I'm doing with
tasks.register<GeneratePluginDocsTask>("generatePluginDocs") {
val kspKotlinTasks = getTasksByName("kspKotlin", /* recursive = */ true)
val outputFiles = kspKotlinTasks.flatMap { it.outputs.files }
inputFiles = files(outputFiles).asFileTree.matching { include("**/META-INF/plugin/*.json") }
}
but that code is not executing the kspKotolin
tasks whose output I'm consuming... π€·ββοΈVampire
03/17/2025, 11:47 AMIsn't it considered best practice by now to not explicitly useUnless the left-hand side is a lifecycle task, correct. In other cases task output should be wired to task inputs. But you do not properly wire task output to task inputs. To start with,for task dependenciesdepen dsOn()
getTasksByName("kspKotlin", /* recursive = */ true)
is a particularly bad idea due to several reasons.
β’ It gets tasks from other projects, so while not being cross-project configuration you still reach into the model of other projects to get the task instances which is likewise bad and also falls under the "Don't reference other project tasks directly!" warning of https://docs.gradle.org/current/userguide/cross_project_publications.html#considerations_and_possible_solutions which is an unsafe way to share task outputs cross-project.
β’ As it returns a plain Set<Task>
, it also breaks task-configuration avoidance for those tasks
β’ And it also only works with tasks that are already registered at the point where you call that method as it is not a life-collection like a TaskContainer
.
Those tasks you then flatMap
(The Set
-one, not the Provider
-one) to it.outputs.files
which then breaks the task-dependencies that would have been there.
So summarized, you should switch to properly share build output cross-project as documented on the above link and if you do it properly should also get the expected task dependencies then.Sebastian Schuberth
03/17/2025, 11:51 AMVampire
03/17/2025, 11:52 AMSebastian Schuberth
03/17/2025, 11:52 AMVampire
03/17/2025, 11:52 AMVampire
03/17/2025, 11:52 AMSebastian Schuberth
03/17/2025, 11:53 AMVampire
03/17/2025, 11:53 AMVampire
03/17/2025, 11:53 AMVampire
03/17/2025, 11:53 AMSebastian Schuberth
03/17/2025, 11:54 AMSebastian Schuberth
03/17/2025, 11:55 AMVampire
03/17/2025, 11:55 AMVampire
03/17/2025, 11:55 AMSebastian Schuberth
03/17/2025, 11:58 AMgetTasksByName()
in general, is it ok to be used when not depending on task output? Like this use-case:
// Gradle's "dependencies" task selector only executes on a single / the current project [1]. However, sometimes viewing
// all dependencies at once is beneficial, e.g. for debugging version conflict resolution.
// [1]: <https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html#sec:listing_dependencies>
tasks.register("allDependencies") {
group = "Help"
description = "Displays all dependencies declared in all projects."
val dependenciesTasks = getTasksByName("dependencies", /* recursive = */ true)
dependsOn(dependenciesTasks)
// Ensure deterministic output by requiring to run tasks after each other in always the same order.
dependenciesTasks.sorted().zipWithNext().forEach { (a, b) ->
b.mustRunAfter(a)
}
}
Vampire
03/17/2025, 11:58 AMVampire
03/17/2025, 12:00 PMgetTasksByName()
is practically always a bad idea.
β’ If you use true
as second argument you always reach into the subprojects models (unless there are no subprojects)
β’ If you use getTasksByName()
at all, you always only get the tasks already registered
β’ If you use getTasksByName()
, you always break task-configuration avoidanceVampire
03/17/2025, 12:01 PMdependencies
where you know it is present in all projects, you can use a different way though, one minuteVampire
03/17/2025, 12:02 PMallprojects.map { "${it.path}:dependencies" }.forEach {
dependsOn(it)
}
Vampire
03/17/2025, 12:02 PMallprojects { ... }
and subprojects { ... }
is bad, or getting things from their modelVampire
03/17/2025, 12:03 PMVampire
03/17/2025, 12:03 PMSebastian Schuberth
03/17/2025, 12:04 PMallprojects
and subprojects
from our project. I'm not going to introduce it now again π
Vampire
03/17/2025, 12:05 PMallprojects { ... }
/ subprojects { ... }
is different from using the information directly available from allprojects
/ subprojects
πSebastian Schuberth
03/17/2025, 12:06 PMSebastian Schuberth
03/17/2025, 12:09 PMVampire
03/17/2025, 12:27 PMSebastian Schuberth
03/17/2025, 12:30 PMVampire
03/17/2025, 12:33 PMpluginManager.withPlugin(...) { ... }
to react to the 3rd-party plugin being applied and then add that task as artifact to the configuration, then on consumer side just consume all projects for example.Sebastian Schuberth
03/17/2025, 12:34 PMval kspProducerConfiguration by configurations.creating {
isCanBeResolved = false
}
tasks.named("kspKotlin") {
artifacts {
add(kspProducerConfiguration.name, this)
}
}
also work for the produces side?Philip W
03/17/2025, 12:55 PMPhilip W
03/17/2025, 12:56 PMVampire
03/17/2025, 1:11 PMVampire
03/17/2025, 1:26 PMartifacts {
add(kspProducerConfiguration.name, tasks.named("kspKotlin"))
}
Sebastian Schuberth
03/17/2025, 2:00 PMallprojects
in
dependencies {
sharedConfiguration(project(path = ":producer", configuration = "sharedConfiguration"))
}
Philip W
03/17/2025, 2:11 PMVampire
03/17/2025, 2:13 PMallprojects
but depend on all projects, you have to list them manually. πPhilip W
03/17/2025, 2:15 PMVampire
03/17/2025, 2:44 PM