org.gradle.integtests.resolve.ProjectDependencyResolveIntegrationTest.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.integtests.resolve
import groovy.test.NotYetImplemented
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
import spock.lang.Issue
@FluidDependenciesResolveTest
class ProjectDependencyResolveIntegrationTest extends AbstractIntegrationSpec {
def setup() {
new ResolveTestFixture(buildFile, "compile").addDefaultVariantDerivationStrategy()
}
def "project dependency includes artifacts and transitive dependencies of default configuration in target project"() {
given:
mavenRepo.module("org.other", "externalA", "1.2").publish()
mavenRepo.module("org.other", "externalB", "2.1").publish()
and:
file('settings.gradle') << "include 'a', 'b'"
and:
buildFile << """
allprojects {
repositories { maven { url '$mavenRepo.uri' } }
}
project(":a") {
configurations {
api
'default' { extendsFrom api }
}
dependencies {
api "org.other:externalA:1.2"
'default' "org.other:externalB:2.1"
}
task jar(type: Jar) {
archiveBaseName = 'a'
destinationDirectory = buildDir
}
artifacts { api jar }
}
project(":b") {
group = 'org.gradle'
version = '1.0'
configurations {
compile
}
dependencies {
compile project(':a')
}
task check(dependsOn: configurations.compile) {
doLast {
assert configurations.compile.collect { it.name } == ['a.jar', 'externalA-1.2.jar', 'externalB-2.1.jar']
def result = configurations.compile.incoming.resolutionResult
// Check root component
def rootId = result.root.id
assert rootId instanceof ProjectComponentIdentifier
def rootPublishedAs = result.root.moduleVersion
assert rootPublishedAs.group == 'org.gradle'
assert rootPublishedAs.name == 'b'
assert rootPublishedAs.version == '1.0'
// Check project components
def projectComponents = result.root.dependencies.selected.findAll { it.id instanceof ProjectComponentIdentifier }
assert projectComponents.size() == 1
def projectA = projectComponents[0]
assert projectA.id.projectPath == ':a'
assert projectA.moduleVersion.group != null
assert projectA.moduleVersion.name == 'a'
assert projectA.moduleVersion.version == 'unspecified'
// Check project dependencies
def projectDependencies = result.root.dependencies.requested.findAll { it instanceof ProjectComponentSelector }
assert projectDependencies.size() == 1
def projectDependency = projectDependencies[0]
assert projectDependency.projectPath == ':a'
// Check external module components
def externalComponents = result.allDependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
assert externalComponents.size() == 2
def externalA = externalComponents[0]
assert externalA.id.group == 'org.other'
assert externalA.id.module == 'externalA'
assert externalA.id.version == '1.2'
assert externalA.moduleVersion.group == 'org.other'
assert externalA.moduleVersion.name == 'externalA'
assert externalA.moduleVersion.version == '1.2'
def externalB = externalComponents[1]
assert externalB.id.group == 'org.other'
assert externalB.id.module == 'externalB'
assert externalB.id.version == '2.1'
assert externalB.moduleVersion.group == 'org.other'
assert externalB.moduleVersion.name == 'externalB'
assert externalB.moduleVersion.version == '2.1'
}
}
}
"""
expect:
succeeds ":b:check"
executedAndNotSkipped ":a:jar"
}
def "project dependency that specifies a target configuration includes artifacts and transitive dependencies of selected configuration"() {
given:
mavenRepo.module("org.other", "externalA", "1.2").publish()
and:
file('settings.gradle') << """rootProject.name='test'
include 'a', 'b'"""
and:
buildFile << """
allprojects {
repositories { maven { url '$mavenRepo.uri' } }
}
project(":a") {
apply plugin: 'base'
configurations {
api
runtime { extendsFrom api }
}
dependencies {
api("org.other:externalA:1.2") {
because 'also check dependency reasons'
}
}
task jar(type: Jar) { archiveBaseName = 'a' }
artifacts { api jar }
}
project(":b") {
configurations {
compile
}
dependencies {
compile(project(path: ':a', configuration: 'runtime')) {
because 'can provide a dependency reason for project dependencies too'
}
}
task check(dependsOn: configurations.compile) {
doLast {
assert configurations.compile.collect { it.name } == ['a.jar', 'externalA-1.2.jar']
}
}
}
"""
def resolve = new ResolveTestFixture(buildFile, "compile")
when:
resolve.prepare()
then:
succeeds ":b:check"
executedAndNotSkipped ":a:jar"
when:
succeeds ':b:checkDeps'
then:
resolve.expectGraph {
root(":b", "test:b:") {
project(":a", 'test:a:') {
byReason('can provide a dependency reason for project dependencies too')
variant('runtime')
module('org.other:externalA:1.2') {
byReason('also check dependency reasons')
variant('runtime', ['org.gradle.status': 'release', 'org.gradle.category': 'library', 'org.gradle.usage': 'java-runtime', 'org.gradle.libraryelements': 'jar'])
}
}
}
}
}
@Issue("GRADLE-2899")
def "multiple project configurations can refer to different configurations of target project"() {
given:
file('settings.gradle') << "include 'a', 'b'"
and:
buildFile << """
project(':a') {
apply plugin: 'base'
configurations {
configA1
configA2
}
task A1jar(type: Jar) {
archiveFileName = 'A1.jar'
}
task A2jar(type: Jar) {
archiveFileName = 'A2.jar'
}
artifacts {
configA1 A1jar
configA2 A2jar
}
}
project(':b') {
configurations {
configB1
configB2
}
dependencies {
configB1 project(path:':a', configuration:'configA1')
configB2 project(path:':a', configuration:'configA2')
}
task check(dependsOn: [configurations.configB1, configurations.configB2]) {
doLast {
assert configurations.configB1.collect { it.name } == ['A1.jar']
assert configurations.configB2.collect { it.name } == ['A2.jar']
}
}
}
"""
expect:
succeeds ":b:check"
executedAndNotSkipped ":a:A1jar", ":a:A2jar"
}
def "resolved project artifacts reflect project properties changed after task graph is resolved"() {
given:
file('settings.gradle') << "include 'a', 'b'"
and:
file('a/build.gradle') << '''
apply plugin: 'base'
configurations { compile }
dependencies { compile project(path: ':b', configuration: 'compile') }
task aJar(type: Jar) { }
gradle.taskGraph.whenReady { project.version = 'late' }
artifacts { compile aJar }
'''
file('b/build.gradle') << '''
apply plugin: 'base'
version = 'early'
configurations { compile }
task bJar(type: Jar) { }
gradle.taskGraph.whenReady { project.version = 'transitive-late' }
artifacts { compile bJar }
'''
file('build.gradle') << '''
configurations {
compile
testCompile { extendsFrom compile }
}
dependencies { compile project(path: ':a', configuration: 'compile') }
task test(dependsOn: [configurations.compile, configurations.testCompile]) {
doLast {
assert configurations.compile.collect { it.name } == ['a-late.jar', 'b-transitive-late.jar']
assert configurations.testCompile.collect { it.name } == ['a-late.jar', 'b-transitive-late.jar']
}
}
'''
expect:
succeeds ":test"
executedAndNotSkipped ":a:aJar", ":b:bJar"
}
@UnsupportedWithConfigurationCache(because = "configure task changes jar task")
def "resolved project artifact can be changed by configuration task"() {
given:
file('settings.gradle') << "include 'a'"
and:
file('a/build.gradle') << '''
apply plugin: 'base'
configurations { compile }
task configureJar {
doLast {
tasks.aJar.archiveExtension = "txt"
tasks.aJar.archiveClassifier = "modified"
}
}
task aJar(type: Jar) {
dependsOn configureJar
}
artifacts { compile aJar }
'''
file('build.gradle') << '''
configurations {
compile
testCompile { extendsFrom compile }
}
dependencies { compile project(path: ':a', configuration: 'compile') }
task test(dependsOn: [configurations.compile, configurations.testCompile]) {
doLast {
assert configurations.compile.collect { it.name } == ['a-modified.txt']
assert configurations.testCompile.collect { it.name } == ['a-modified.txt']
}
}
'''
expect:
succeeds ":test"
executedAndNotSkipped ":a:configureJar", ":a:aJar"
}
def "project dependency that references an artifact includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
given:
mavenRepo.module("group", "externalA", "1.5").publish()
and:
file('settings.gradle') << "include 'a', 'b'"
and:
buildFile << """
allprojects {
apply plugin: 'base'
repositories { maven { url '${mavenRepo.uri}' } }
}
project(":a") {
configurations {
deps
'default' { extendsFrom deps }
}
dependencies { deps 'group:externalA:1.5' }
task xJar(type: Jar) { archiveBaseName='x' }
task yJar(type: Jar) { archiveBaseName='y' }
artifacts { 'default' xJar, yJar }
}
project(":b") {
configurations { compile }
dependencies { compile(project(':a')) { artifact { name = 'y'; type = 'jar' } } }
task test {
inputs.files configurations.compile
doFirst {
assert configurations.compile.files.collect { it.name } == ['y.jar', 'externalA-1.5.jar']
}
}
}
"""
expect:
succeeds 'b:test'
executedAndNotSkipped ":a:yJar"
}
@ToBeFixedForConfigurationCache
def "reports project dependency that refers to an unknown artifact"() {
given:
file('settings.gradle') << """
include 'a', 'b'
"""
and:
buildFile << """
allprojects { group = 'test' }
project(":a") {
configurations { 'default' {} }
}
project(":b") {
configurations { compile }
dependencies { compile(project(':a')) { artifact { name = 'b'; type = 'jar' } } }
task test {
inputs.files configurations.compile
outputs.upToDateWhen { false }
doFirst {
configurations.compile.files.collect { it.name }
}
}
}
"""
expect:
fails ':b:test'
and:
failure.assertHasCause("Could not resolve all files for configuration ':b:compile'.")
failure.assertHasCause("Could not find b.jar (project :a).")
}
def "non-transitive project dependency includes only the artifacts of the target configuration"() {
given:
mavenRepo.module("group", "externalA", "1.5").publish()
and:
file('settings.gradle') << "include 'a', 'b'"
and:
buildFile << """
allprojects {
apply plugin: 'java'
repositories { maven { url '${mavenRepo.uri}' } }
}
project(':a') {
dependencies {
implementation 'group:externalA:1.5'
implementation files('libs/externalB.jar')
}
}
project(':b') {
dependencies {
implementation project(':a'), { transitive = false }
}
task listJars(dependsOn: configurations.runtimeClasspath) {
doLast {
assert configurations.runtimeClasspath.collect { it.name } == ['a.jar']
}
}
}
"""
expect:
succeeds ":b:listJars"
executedAndNotSkipped ":a:jar"
}
def "can have cycle in project dependencies"() {
given:
file('settings.gradle') << "include 'a', 'b', 'c'"
and:
buildFile << """
subprojects {
apply plugin: 'base'
configurations {
first
other
'default' {
extendsFrom first
}
}
task jar(type: Jar)
artifacts {
'default' jar
}
}
project('a') {
dependencies {
first project(':b')
other project(':b')
}
task listJars {
dependsOn configurations.first
dependsOn configurations.other
doFirst {
def jars = configurations.first.collect { it.name } as Set
assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
jars = configurations.other.collect { it.name } as Set
assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
// Check type of root component
def defaultResult = configurations.first.incoming.resolutionResult
def defaultRootId = defaultResult.root.id
assert defaultRootId instanceof ProjectComponentIdentifier
def otherResult = configurations.other.incoming.resolutionResult
def otherRootId = otherResult.root.id
assert otherRootId instanceof ProjectComponentIdentifier
}
}
}
project('b') {
dependencies {
first project(':c')
}
}
project('c') {
dependencies {
first project(':a')
}
}
"""
expect:
succeeds ":a:listJars"
executedAndNotSkipped ":b:jar", ":c:jar"
}
@NotYetImplemented
@Issue('GRADLE-3280')
def "can resolve recursive copy of configuration with cyclic project dependencies"() {
given:
settingsFile << "include 'a', 'b', 'c'"
buildScript '''
subprojects {
apply plugin: 'base'
task jar(type: Jar)
artifacts {
'default' jar
}
}
project('a') {
dependencies {
'default' project(':b')
}
task assertCanResolve {
doLast {
assert !project.configurations.default.resolvedConfiguration.hasError()
}
}
task assertCanResolveRecursiveCopy {
doLast {
assert !project.configurations.default.copyRecursive().resolvedConfiguration.hasError()
}
}
}
project('b') {
dependencies {
'default' project(':c')
}
}
project('c') {
dependencies {
'default' project(':a')
}
}
'''.stripIndent()
expect:
succeeds ':a:assertCanResolve'
and:
succeeds ':a:assertCanResolveRecursiveCopy'
}
// this test is largely covered by other tests, but does ensure that there is nothing special about
// project dependencies that are “built” by built in plugins like the Java plugin's created jars
def "can use zip files as project dependencies"() {
given:
file("settings.gradle") << "include 'a'; include 'b'"
file("a/some.txt") << "foo"
file("a/build.gradle") << """
group = "g"
version = 1.0
apply plugin: 'base'
task zip(type: Zip) {
from "some.txt"
}
artifacts {
delegate.default zip
}
"""
file("b/build.gradle") << """
configurations { conf }
dependencies {
conf project(":a")
}
task copyZip(type: Copy) {
from configurations.conf
into "\$buildDir/copied"
}
"""
when:
succeeds ":b:copyZip"
then:
executedAndNotSkipped ":a:zip", ":b:copyZip"
and:
file("b/build/copied/a-1.0.zip").exists()
}
@ToBeFixedForConfigurationCache(because = "Task.getProject() during execution")
def "resolving configuration with project dependency marks dependency's configuration as observed"() {
settingsFile << "include 'api'; include 'impl'"
buildFile << """
allprojects {
configurations {
conf
}
configurations.create("default").extendsFrom(configurations.conf)
}
project(":impl") {
dependencies {
conf project(":api")
}
task check {
doLast {
assert configurations.conf.state == Configuration.State.UNRESOLVED
assert project(":api").configurations.conf.state == Configuration.State.UNRESOLVED
configurations.conf.resolve()
assert configurations.conf.state == Configuration.State.RESOLVED
assert project(":api").configurations.conf.state == Configuration.State.UNRESOLVED
// Attempt to change the configuration, to demonstrate that is has been observed
project(":api").configurations.conf.dependencies.add(null)
}
}
}
"""
when:
fails("impl:check")
then:
failure.assertHasCause "Cannot change dependencies of dependency configuration ':api:conf' after it has been included in dependency resolution"
}
@Issue(["GRADLE-3330", "GRADLE-3362"])
def "project dependency can resolve multiple artifacts from target project that are differentiated by archiveFileName only"() {
given:
file('settings.gradle') << "include 'a', 'b'"
and:
buildFile << """
project(':a') {
apply plugin: 'base'
configurations {
configOne
configTwo
}
task A1jar(type: Jar) {
archiveFileName = 'A1.jar'
}
task A2jar(type: Jar) {
archiveFileName = 'A2.jar'
}
task A3jar(type: Jar) {
archiveFileName = 'A3.jar'
}
artifacts {
configOne A1jar
configTwo A2jar
configTwo A3jar
}
}
project(':b') {
configurations {
configB
}
dependencies {
configB project(path:':a', configuration:'configOne')
configB project(path:':a', configuration:'configTwo')
}
task check(dependsOn: configurations.configB) {
doLast {
assert configurations.configB.collect { it.name } == ['A1.jar', 'A2.jar', 'A3.jar']
}
}
}
"""
expect:
succeeds ":b:check"
executedAndNotSkipped ":a:A1jar", ":a:A2jar", ":a:A3jar"
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy