ysb33r
11/24/2024, 4:42 PM// Assume that there is a task with a single output property
// and signature Provider<File> getOutputFile().
// just calling it 'task' below instead of the whole task.named(....)
configurations.create('myCustom') {
canBeResolved = false
canBeConsumed = true
attributes { /* do some custom attrs */ }
outgoing {
// Option 1
artifact(task.flatMap { t -> t.outputFile } ) { cpa ->
cpa.builtBy(task)
}
// Option 2
artifact(task)
}
}
// or even option 3
project.artifacts.add('myCustom',task)
When that is linked to another subproject
// assume two configurations myCustomIncoming, myCustomResolvable where the latter extends the former, but is resolvable
dependencies {
myCustomIncoming project(path: ':sub-project-with-my-custom', configuration: 'myCustom')
}
When I resolve myCustomResolvable
, I would expect the task from earlier to be executed in order to create the appropriate artifact, but it doesn't. (it knows what the artifacts path is, it just does not execute the task beforehand).
I would expect this to be relatively straight forward, but sometimes I end chasing my tail in getting something like this to work.
Anyone else jumping the same kind of hurdles?Philip W
11/24/2024, 5:50 PMtony
11/24/2024, 5:51 PMysb33r
11/24/2024, 10:53 PMVampire
11/25/2024, 11:11 AMval task by tasks.registering {
val output = layout.buildDirectory.file("foo.txt")
outputs.file(output)
doLast {
output.get().asFile.writeText("foo")
}
}
configurations.create("myCustom") {
isCanBeResolved = false
isCanBeConsumed = true
outgoing {
artifact(task)
}
}
and
val myCustomIncoming = configurations.dependencyScope("myCustomIncoming")
val myCustomResolvable = configurations.resolvable("myCustomResolvable") {
extendsFrom(myCustomIncoming.get())
}
dependencies {
myCustomIncoming(project(path = ":foo", configuration = "myCustom"))
}
val foo by tasks.registering {
inputs.files(myCustomResolvable)
doLast {
myCustomResolvable.get().files.forEach { println(it) }
}
}
works fine here.ysb33r
11/25/2024, 12:25 PMinputs.files(myCustomResolvable)
is a trick. It is not obvious that it should be done and I don't think it is anywhere in the docs. I have fallen into trap at least once, where I was using providers in the task, but nothing worked until I added the configuration as an input.Vampire
11/25/2024, 12:43 PMYes, attributes serves as a filter, and if not supplied then all files should be available.Attributes serve as filter if you request a specific configuration explicitly? o_O Wasn't aware of that, I thought if you request by configuration explicitly you get that configuration, if you do not specify a configuration explicitly, then attribute- / variant-aware resolution is used.
BTWIt is not so much a trick imho. It is just declaring the task inputs via ad-hoc API as I did not use a proper task implementation with configured input properties. Using a proper task implementation like this also works fine:is a trick.inputs.files(myCustomResolvable)
dependencies {
myCustomIncoming(project(path = ":foo", configuration = "myCustom"))
}
abstract class Foo : DefaultTask() {
@get:InputFiles
abstract val input: ConfigurableFileCollection
@TaskAction
fun foo() {
input.files.forEach { println(it) }
}
}
val foo by tasks.registering(Foo::class) {
input.from(myCustomResolvable)
}
ysb33r
11/25/2024, 12:44 PMVampire
11/25/2024, 12:45 PMysb33r
11/25/2024, 1:13 PM@Input
Provider<String> getSomeContent()
and that provider is something like
final FileCollection incomingFiles = project.configurations['myCustomIncoming']
// just assume one file for simplicity
project.provider { -> incomingFiles.singleFile.text }
that won't invoke the task in the producing subproject when the consuming task's properties are serialized.
and the way to get it to work is to add
inputs.files(incomingFiles)
once you know that and retrospectively, you understand why, but this is not straight-forward and clear when wiring up something complex for the first time.Vampire
11/25/2024, 1:40 PMabstract class Foo : DefaultTask() {
@get:Input
abstract val input: Property<String>
@TaskAction
fun foo() {
println(input.get())
}
}
val foo by tasks.registering(Foo::class) {
input = myCustomResolvable.flatMap { it.elements }.map { it.single().asFile.readText() }
}