Resolving a configuration while configuring task

Hello
I tried to provide a file from one project to another project using configuration and it took me a few hours to figure out that resolving a configuration in the configuration block of a task can cause a failure in gradle.
If you use configuration on demand and have a producer (project :list) like this:

plugins {
    id("buildlogic.java-library-conventions")
    //id ("ivy-publish") since ivy-publish uses afterEvaluate, same explosion
}
//if this wouldnt be here it work...but cant easily avoid it, see  used by publishing for example
project.afterEvaluate{}

val myFile by configurations.registering{
    isCanBeResolved=false
    isCanBeConsumed=true
}
dependencies{
    //as seen on https://docs.gradle.org/current/userguide/declaring_dependencies.html#ex-declaring-multiple-file-dependencies
    myFile(project.files("Hello"))
}

And a consumer (project :utilities) like this:

plugins {
    id("buildlogic.java-library-conventions")
}
val getTheFile by configurations.registering

dependencies {
    api(project(":list"))
    getTheFile(project(":list","myFile"))
}
// if the next line would be uncommented it would work
// but i guess not recommended?
//val singleFile = getTheFile.get().resolve()
tasks.register("explode"){
    //the line here causes  afterEvaluate in :list to explode
    val singleFile = getTheFile.get().singleFile
    doFirst {
        println("we dont get here... " + singleFile)
    }
}

and execute gradle :utilities:explode we get:

* What went wrong:
Project#afterEvaluate(Action) on project ':list' cannot be executed in the current context.

reproducer repo can be found here GitHub - TheGoesen/gradle-reproducer

So what i would get from that is that resolving a configuration directly in the configuration block of a task is bad (or at least when you use configure on demand) But There isnt really anything about this in the docs, or am I missing something?

From a quick look I’d say there are at least two quirks.

  1. You define “Hello” as dependency. If this is to share the file with another project you should instead add it as artifact. See Sharing outputs between projects for more information.

  2. The getTheFile configuration is an input to your task, so you should declare it as input like inputs.files(getTheFile). This will probably also solve your actual problem.

Hi, thanks for your interest.

  1. In this simplified case yes since i am only sharing one file the artifact block seems like a reasonable alternative. However the original problem I need to share a filecollection with another project… Something similar to val collection = fileTree("res").filter { it.name.endsWith(".xml") }. I cant do that using artifacts I think?
  2. Inputs where ommited for brevity :wink: declaring as an input alone doesnt fix the issue, converting everything into providers seems to. With the configuration cache placing limitations, Add ability to get named Task file inputs and outputs · Issue #21456 · gradle/gradle · GitHub not solved and the behaviour described here it takes a bit of trial and error to figure out something that does work…
//but is this the recommended way?
tasks.register("doesNotExplode"){
    val works = getTheFile.map { it.singleFile }
    inputs.file(works)
    outputs.file(project.layout.buildDirectory.file("blub"))
    doFirst {
        // in this case we could also directly resolve inputs
       // but in production there are often multiple different inputs...
        println("this works" + works)
        outputs.files.singleFile.writeText("we did it")
    }

}

I now cloned your reproducer and run gw explode:

[...]
> Task :utilities:explode
we dont get here... D:\Sourcecode\other\others\gradle-reproducer\list\Hello

BUILD SUCCESSFUL in 4s

So how would the issue be reproduced with your reproducer?
Or do you maybe have some init script that is breaking things?

cant do that using artifacts I think

Why not?
You are not restricted to share only one file.

Furthermore, if you would not use a legacy configuration setup, you would also see that it is not idiomatic what you do.
If you for example use the new configurations.consumable helper, you would instantly get a complaint, that you cannot declare dependencies on that, as for that you should use configurations.dependencyScope, already hinting at what you do is slightly off-track. :slight_smile: