ProjectDependency added by Configuration.withDependencies has no buildDependencies

Hi,

I have a sub-project which creates an archive artifact to be consumed by other sub-projects:

configurations {
    foo {
        canBeResolved = false
        canBeConsumed = true
    }
}

artifacts {
    foo tasks.named('fooMaker', Jar)
}

But when I try to do (the equivalent of) this from another sub-project:

configurations {
    bonk {
        canBeResolved = false
        canBeConsumed = false
    }.withDependencies { deps ->
        def newdep = project.dependencies(path: 'foo-project', configuration: 'foo')
        deps.add(newdep)
    }
    packaging.extendsFrom bonk
}

tasks.register('bar') {
    input.files(configuration.packaging)

    doFirst {
        configurations.packaging.forEach { file ->
            println "FILE: $file"
        }
    }
}

I discover that the packaging configuration knows where the foo archive should be, but doesn’t know which task should build it! Conversely, adding the dependency like this works as expected:

dependencies {
    bonk project(':foo-project', configuration: 'foo')
}

However, I need the withDependencies approach to work for the sake of a plugin I am writing.

Can anyone suggest what might be happening here please?
Thank you,
Chris

It occurs to me that could it simply be “too late” to add extra task dependencies at this point, because the Task Graph has already been calculated… :slightly_frowning_face:?

This does seem likely, because forcibly resolving this configuration from a Project.afterEvaluate handler (i.e. adding the new ProjectDependency objects before Gradle builds the Task Execution Graph) does the trick!

I feel dirty now, and not in a good way. Better suggestions would be very welcome.

Cheers,
Chris

Edit: While it is presumably necessary for my withDependencies {} handler to execute before Gradle builds the Task Execution Graph, it does not appear to be sufficient. For example, I have since tried forcibly resolving my configuration like this:

configurations.compileClasspath.withDependencies {
    myConfiguration.resolve()
}

on the basis that compileClasspath is already being resolved at the right time. However, while my new ProjectDependency objects were created before the Task Execution Graph, they still did not trigger any new task dependencies. I have therefore reverted to using Project.afterEvaluate {} again.

I fear that I have entered the realm of Undocumented Behaviour…!!!

Works fine here after fixing all your syntax errors:

configurations {
    bonk {
        canBeResolved = false
        canBeConsumed = false
    }.withDependencies { deps ->
        def newdep = project.dependencies.create(project.dependencies.project(path: ':foo-project', configuration: 'foo'))
        deps.add(newdep)
    }
    packaging.extendsFrom bonk
}

tasks.register('bar') {
    inputs.files(configurations.packaging)

    doFirst {
        configurations.packaging.forEach { file ->
            println "FILE: $file"
        }
    }
}

The path is printed and the fooMaker is automatically run before bar.
As the example you posted has various errors that made it not run at all but only throw syntax errors, it is hard to guess what your actual problem is or was.

Hi, thanks for reply. Sorry about the syntax errors, I was trying to transcribe the salient features from my test project late at night and clearly mistyped a few things.

As I have since discovered, Gradle is building the Task Execution Graph before it adds the new ProjectDependency to the sub-project - something I have proved by adding these lines to the “root” project:

gradle.taskGraph.whenReady {
    println "TASK GRAPH READY"
}

When I also add a println to the withDependencies handler, the output is like this:

TASK GRAPH READY

> Task :bar-project:bar
ADDED : DefaultProjectDependency{dependencyProject='project ':foo-project'', configuration='foo'}
FILE: /home/chris/workspace/foobar/foo-project/build/libs/foo-1.0.1-SNAPSHOT-foo.jar

BUILD SUCCESSFUL in 716ms

In my case, nothing is being built.

If you’re interested, here is an extremely trivial example project:
foobar.zip (138.3 KB)

Executing

$ ./gradlew clean bar

should reproduce the issue. Uncomment the line in bar-project's afterEvaluate handler to apply “fix”.

Cheers,
Chris

Well, you prove that it is the case for how you do it.
Here it is different:

withDependencies called
TASK GRAPH READY
> Task :fooMaker

> Task :foo:bar

Can you provide an MCVE?

Can you provide an MCVE?

Yes :wink:

Hm, interesting, I guess you found a bug you should report.
If you add a directroy baz, inside an empty file settings.gradle, and in your top-level setting.gradle the line includeBuild 'baz', then it works as expected.
At least this difference in behaviour is probably not expected.

OK, thanks. I’ve raised #17765 and we’ll see what they say.

Happily, I have confirmed that your “composite build” trick works for my actual Gradle project too :grinning:. (Not that my plugin code can use your trick, but I am now confident that resolving #17765 will also solve my Real World problem :+1:.)