Configuration.getIncoming().getResolutionResult().getRoot().getDependencies() can return graph with loop

here’s the dependency setup.
app project + test-utils project.

app:testCompile -> test-utils
test-utils:compile -> app

This should be fine since test-utils depends on the app’s main artifact, not the test artifact and app’s test artifact depends on both the app’s main artifact and test-utils.

Calling Configuration.getFiles().getResolvedConfiguration().getResolvedArtifacts() on app:testCompile returns the right thing.

However using testCompile.getIncoming().getResolutionResult().getRoot().getDependencies() can return a graph with a loop. Printing the graph returns something like:

- circularJava:test-util:unspecified
  - circularJava:app:unspecified
    - circularJava:test-util:unspecified
      - circularJava:app:unspecified
etc...

it looks like when it finds the transitive dependencies of test-utils (app), and search for app’s transitive dependencies, it doesn’t realize that app’s main artifact has no dependencies on test-utils (it’s its test artifact that does).

I’ve uploaded a small test project. You can run ‘gradle lib:printDep’ to see the code that outputs the graph using the API in question.

demo.zip (6.2 KB)

When the graph is created, nodes of the same projects but different configurations within that project are effectively merged, which results in an artificial cycle being reported as you say. Basically, it’s because there’s a lossy step in the resolution process in this regard.

I’m not aware of a way to impose this information back into the resolution graph. @adammurdoch do you have any advice here?