Substitution of transitive dependency pulls in all artifacts of substituting project

Hi,

I’ve encountered a strange scenario that I’m not sure how to work around, and I think could possibly be a bug (or in need of improvement).

Given the following build.gradle:

task wrapper(type: Wrapper) {
    gradleVersion = '2.13'
}
allprojects {
    apply plugin: 'java'
}
subprojects {
    configurations {
        otherConfig
        otherConfig2
    }
    task otherJar( type: Jar ) {
        classifier 'other'
    }
    task otherJar2( type: Jar ) {
        classifier 'other2'
    }
    artifacts {
        otherConfig otherJar
        otherConfig2 otherJar2
    }
}
repositories {
    jcenter()
}
configurations.compile.resolutionStrategy {
    dependencySubstitution {
        substitute module('org.hamcrest:hamcrest-core') with project(':subproject')
        //this however works as I would expect:
        //substitute module('junit:junit') with project(':subproject')
    }
}
dependencies {
    compile 'junit:junit:+'
}
task useFiles {
    inputs.files configurations.compile
    doLast {
        configurations.compile.each {
            println "dep ${it}"
        }
    }
}

and settings.gradle:

include ':subproject'

The output of the useFiles task are a bit unexpected:

:useFiles
dep /home/cdore/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar
dep /home/cdore/gradle-configartifacts-test/subproject/build/libs/subproject.jar
dep /home/cdore/gradle-configartifacts-test/subproject/build/libs/subproject-other2.jar
dep /home/cdore/gradle-configartifacts-test/subproject/build/libs/subproject-other.jar

If instead you substitute the first level dependency (junit in my example):

:useFiles
dep /home/cdore/gradle-configartifacts-test/subproject/build/libs/subproject.jar

Why is Gradle adding all artifacts of subproject to the compile configuration when substituting the transitive dependency?

Thanks, Chris

If it adds anything useful; The same behaviour is present when not using dependency substitution explicitly but a local project with the same group and module ID automagically substitutes an external dependency. In other words, when a circular dependency tree exists (published jar comes back in as a transitive). When this happens the test jars get into the compile configuration like the “other” jars in my example.

Aside, I’m actually using Gradle 2.3 and not 2.13 due to 3rd party plugin incompatibilities.

The situation is actually a bit worse. I just discovered that dependencies of any configuration in the subproject are also pulled in, even when not related to an artifact of the subproject.

IE, add:

subprojects {
    configurations {
        otherConfig3
    }
    dependencies {
        otherConfig3 'jmock:jmock:1.1.0'
    }
}

and now we get:

:useFiles
dep /home/cdore/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar
dep /home/cdore/gradle-configurationartifacts-test/subproject/build/libs/subproject-other2.jar
dep /home/cdore/gradle-configurationartifacts-test/subproject/build/libs/subproject.jar
dep /home/cdore/gradle-configurationartifacts-test/subproject/build/libs/subproject-other.jar
dep /home/cdore/.gradle/caches/modules-2/files-2.1/jmock/jmock/1.1.0/90e87194c8113e1901d7196b359e48f9eb9c63d/jmock-1.1.0.jar

Any thoughts on this behaviour?

Hey Chris,
sorry for coming back to you late. Indeed this seems like a bug or as you said it, something which should definitely improved in Gradle. I’ve raised GRADLE-3542 for this.

Thanks René, this at least helps confirm that we probably weren’t doing something incorrectly.

Hi Chris,

It seems this was already fixed in 3.1 (I can still reproduce it with 3.0, but not 3.1).

Can you confirm that for your case?