Fix stdlib-by-default failures when configuration has been resolved

Issue #KT-38221
This commit is contained in:
Sergey Igushkin
2020-07-17 17:25:06 +04:00
parent dccac34282
commit d3f9f4f3e8
3 changed files with 109 additions and 56 deletions
@@ -257,6 +257,26 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
) )
} }
@Test
fun testNoFailureIfConfigurationIsObserved() = with(jvmProject()) {
lateinit var originalScript: String
try {
gradleBuildScript().modify {
originalScript = it
"""
configurations.create("api")
dependencies {
api("org.jetbrains.kotlin:kotlin-reflect")
}
println(configurations.api.incoming.dependencies.toList())
""".trimIndent() + "\n" + it
}
checkTaskCompileClasspath("compileKotlin", listOf("kotlin-reflect"))
} finally {
gradleBuildScript().writeText(originalScript)
}
}
private fun Project.checkConfigurationContent( private fun Project.checkConfigurationContent(
configurationName: String, configurationName: String,
subproject: String? = null, subproject: String? = null,
@@ -8,9 +8,12 @@ package org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.TestedExtension import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.TestVariant import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.DependencySet
import org.gradle.api.artifacts.ExternalDependency import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.tasks.testing.AbstractTestTask import org.gradle.api.tasks.testing.AbstractTestTask
import org.gradle.api.tasks.testing.Test import org.gradle.api.tasks.testing.Test
@@ -54,18 +57,36 @@ private fun configureDefaultVersionsResolutionStrategy(project: Project) {
} }
//region stdlib //region stdlib
internal fun configureStdlibDefaultDependency(project: Project) = project.whenEvaluated { internal fun configureStdlibDefaultDependency(project: Project) = with(project) {
if (!PropertiesProvider(project).stdlibDefaultDependency) if (!PropertiesProvider(project).stdlibDefaultDependency)
return@whenEvaluated return
val scopesToHandleConfigurations = listOf(KotlinDependencyScope.API_SCOPE, KotlinDependencyScope.IMPLEMENTATION_SCOPE)
project.kotlinExtension.sourceSets.all { kotlinSourceSet -> project.kotlinExtension.sourceSets.all { kotlinSourceSet ->
val scope = if (isRelatedToAndroidTestSourceSet(project, kotlinSourceSet)) // AGP deprecates API configurations in test source sets scopesToHandleConfigurations.forEach { scope ->
val scopeConfiguration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
project.tryWithDependenciesIfUnresolved(scopeConfiguration) { dependencies ->
val scopeToAddStdlibDependency =
if (isRelatedToAndroidTestSourceSet(project, kotlinSourceSet)) // AGP deprecates API configurations in test source sets
KotlinDependencyScope.IMPLEMENTATION_SCOPE KotlinDependencyScope.IMPLEMENTATION_SCOPE
else KotlinDependencyScope.API_SCOPE else KotlinDependencyScope.API_SCOPE
val scopeConfiguration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope) if (scope != scopeToAddStdlibDependency)
return@tryWithDependenciesIfUnresolved
scopeConfiguration.withDependencies { dependencies -> chooseAndAddStdlibDependency(project, kotlinSourceSet, dependencies)
}
}
}
}
private fun chooseAndAddStdlibDependency(
project: Project,
kotlinSourceSet: KotlinSourceSet,
dependencies: DependencySet
) {
val sourceSetDependencyConfigurations = val sourceSetDependencyConfigurations =
KotlinDependencyScope.values().map { project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, it) } KotlinDependencyScope.values().map { project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, it) }
@@ -111,8 +132,6 @@ internal fun configureStdlibDefaultDependency(project: Project) = project.whenEv
dependencies.add(project.kotlinDependency(stdlibModuleName, project.kotlinExtension.coreLibrariesVersion)) dependencies.add(project.kotlinDependency(stdlibModuleName, project.kotlinExtension.coreLibrariesVersion))
} }
} }
}
}
private fun Project.findAndroidTarget(): KotlinAndroidTarget? { private fun Project.findAndroidTarget(): KotlinAndroidTarget? {
val kotlinExtension = project.kotlinExtension val kotlinExtension = project.kotlinExtension
@@ -178,7 +197,7 @@ internal fun configureKotlinTestDependencies(project: Project) {
} }
} }
configuration.withDependencies { dependencies -> project.tryWithDependenciesIfUnresolved(configuration) { dependencies ->
val parentOrOwnVersions: List<String?> = val parentOrOwnVersions: List<String?> =
kotlinSourceSet.getSourceSetHierarchy().filter(versionOrNullBySourceSet::contains).map(versionOrNullBySourceSet::get) kotlinSourceSet.getSourceSetHierarchy().filter(versionOrNullBySourceSet::contains).map(versionOrNullBySourceSet::get)
@@ -284,3 +303,21 @@ private fun KotlinTargetWithTests<*, *>.findTestRunsByCompilation(byCompilation:
return testRuns.filter { it.executionSource.isProducedFromTheCompilation() }.takeIf { it.isNotEmpty() } return testRuns.filter { it.executionSource.isProducedFromTheCompilation() }.takeIf { it.isNotEmpty() }
} }
//endregion //endregion
private fun Project.tryWithDependenciesIfUnresolved(configuration: Configuration, action: (DependencySet) -> Unit) {
fun reportAlreadyResolved() {
logger.info("Could not setup Kotlin-specific dependencies for $configuration as it is already resolved")
}
if (configuration.state != Configuration.State.UNRESOLVED)
return reportAlreadyResolved()
// Gradle doesn't provide any public API to check if it's safe to modify a configuration's dependencies.
// The state of the configuration may still be UNRESOLVED but it might have 'participated' in dependency resolution and its dependencies
// may now be frozen.
try {
configuration.withDependencies(action)
} catch (e: InvalidUserDataException) {
reportAlreadyResolved()
}
}
@@ -99,11 +99,7 @@ abstract class AbstractKotlinTarget(
?: project.configurations.create(kotlinUsageContext.name).also { configuration -> ?: project.configurations.create(kotlinUsageContext.name).also { configuration ->
configuration.isCanBeConsumed = false configuration.isCanBeConsumed = false
configuration.isCanBeResolved = false configuration.isCanBeResolved = false
/** Add dependencies lazily to avoid freezing the content of the configuration held by [kotlinUsageContext] */ configuration.extendsFrom(project.configurations.getByName(kotlinUsageContext.dependencyConfigurationName))
configuration.withDependencies { dependencies ->
dependencies.addAll(kotlinUsageContext.dependencies)
}
configuration.dependencyConstraints.addAll(kotlinUsageContext.dependencyConstraints)
configuration.artifacts.addAll(kotlinUsageContext.artifacts) configuration.artifacts.addAll(kotlinUsageContext.artifacts)
val attributes = kotlinUsageContext.attributes val attributes = kotlinUsageContext.attributes