diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt index 309af0789af..caae2409948 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt @@ -21,7 +21,9 @@ class VariantAwareDependenciesIT : BaseGradleIT() { embedProject(innerProject) gradleBuildScript(innerProject.projectName).appendText("\ndependencies { compile rootProject }") - testResolveAllConfigurations(innerProject.projectName) + testResolveAllConfigurations(innerProject.projectName) { + assertContains(">> :${innerProject.projectName}:runtime --> sample-lib-jvm6-1.0.jar") + } } } @@ -34,7 +36,9 @@ class VariantAwareDependenciesIT : BaseGradleIT() { embedProject(innerProject) gradleBuildScript(innerProject.projectName).appendText("\nrepositories { jcenter() }; dependencies { compile rootProject }") - testResolveAllConfigurations(innerProject.projectName) + testResolveAllConfigurations(innerProject.projectName) { + assertContains(">> :${innerProject.projectName}:runtime --> sample-lib-nodejs-1.0.jar") + } } } @@ -106,6 +110,76 @@ class VariantAwareDependenciesIT : BaseGradleIT() { } } + @Test + fun testMppResolvesJvmAndJsKtLibs() { + val outerProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app") + val innerJvmProject = Project("simpleProject") + val innerJsProject = Project("kotlin2JsInternalTest") + + with(outerProject) { + embedProject(innerJvmProject) + embedProject(innerJsProject) + + gradleBuildScript().appendText("\n" + """ + dependencies { + jvm6Implementation project(':${innerJvmProject.projectName}') + jvm6TestRuntime project(':${innerJvmProject.projectName}') + nodeJsImplementation project(':${innerJsProject.projectName}') + nodeJsTestRuntime project(':${innerJsProject.projectName}') + } + """.trimIndent()) + + testResolveAllConfigurations(innerJvmProject.projectName) + } + } + + @Test + fun testJvmKtAppDependsOnMppTestRuntime() { + val outerProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app") + val innerProject = Project("simpleProject") + + with(outerProject) { + embedProject(innerProject) + + gradleBuildScript(innerProject.projectName).appendText( + "\ndependencies { testCompile project(path: ':', configuration: 'jvm6TestRuntime') }" + ) + + testResolveAllConfigurations(innerProject.projectName) { + assertContains(">> :${innerProject.projectName}:testCompile --> sample-lib-jvm6-1.0.jar") + assertContains(">> :${innerProject.projectName}:testRuntime --> sample-lib-jvm6-1.0.jar") + } + } + } + + @Test + fun testKtAppResolvesOldMpp() { + val outerProject = Project("multiplatformProject") + val innerJvmProject = Project("simpleProject") + val innerJsProject = Project("kotlin2JsInternalTest") + + with(outerProject) { + embedProject(innerJvmProject) + embedProject(innerJsProject) + + listOf(innerJvmProject to ":libJvm", innerJsProject to ":libJs").forEach { (project, dependency) -> + gradleBuildScript(project.projectName).appendText( + "\n" + """ + configurations.create('foo') + dependencies { + foo project('$dependency') + compile project('$dependency') + foo project(':lib') + compile project(':lib') + } + """.trimIndent() + ) + + testResolveAllConfigurations(project.projectName) + } + } + } + private fun Project.embedProject(other: Project) { setupWorkingDir() other.setupWorkingDir() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 5e508309004..c0007093978 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -433,6 +433,10 @@ internal abstract class AbstractKotlinPlugin( AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, kotlinTarget, project.configurations) } + project.configurations.getByName("default").apply { + setupAsLocalTargetSpecificConfigurationIfSupported(kotlinTarget) + } + // Setup the published configurations: // Don't set the attributes for common module; otherwise their 'common' platform won't be compatible with the one in // platform-specific modules diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 424f335b3cf..18852a68bd1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -81,6 +81,7 @@ abstract class KotlinBasePluginWrapper( private fun setupAttributeMatchingStrategy(project: Project) = with(project.dependencies.attributesSchema) { KotlinPlatformType.setupAttributesMatchingStrategy(this) KotlinUsages.setupAttributesMatchingStrategy(this) + ProjectLocalConfigurations.setupAttributesMatchingStrategy(this) } internal abstract fun getPlugin( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index 94a9bd8f86f..70a8df7ac38 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -154,7 +154,10 @@ abstract class AbstractKotlinTargetConfigurator val configurations = project.configurations - val defaultConfiguration = configurations.maybeCreate(target.defaultConfigurationName) + val defaultConfiguration = configurations.maybeCreate(target.defaultConfigurationName).apply { + setupAsLocalTargetSpecificConfigurationIfSupported(target) + } + val mainCompilation = target.compilations.maybeCreate(KotlinCompilation.MAIN_COMPILATION_NAME) val compileConfiguration = configurations.maybeCreate(mainCompilation.deprecatedCompileConfigurationName) @@ -240,6 +243,7 @@ abstract class AbstractKotlinTargetConfigurator configurations: ConfigurationContainer ) { val compileConfiguration = configurations.maybeCreate(compilation.deprecatedCompileConfigurationName).apply { + setupAsLocalTargetSpecificConfigurationIfSupported(target) isVisible = false isCanBeResolved = true // Needed for IDE import description = "Dependencies for $compilation (deprecated, use '${compilation.implementationConfigurationName} ' instead)." @@ -262,6 +266,7 @@ abstract class AbstractKotlinTargetConfigurator } val compileOnlyConfiguration = configurations.maybeCreate(compilation.compileOnlyConfigurationName).apply { + setupAsLocalTargetSpecificConfigurationIfSupported(target) isVisible = false isCanBeResolved = true // Needed for IDE import description = "Compile only dependencies for $compilation." @@ -278,6 +283,7 @@ abstract class AbstractKotlinTargetConfigurator if (compilation is KotlinCompilationToRunnableFiles) { val runtimeConfiguration = configurations.maybeCreate(compilation.deprecatedRuntimeConfigurationName).apply { + setupAsLocalTargetSpecificConfigurationIfSupported(target) extendsFrom(compileConfiguration) isVisible = false isCanBeResolved = true // Needed for IDE import @@ -303,16 +309,15 @@ abstract class AbstractKotlinTargetConfigurator } } } - - - internal val KotlinCompilation.deprecatedCompileConfigurationName: String - get() = disambiguateName("compile") - - internal val KotlinCompilationToRunnableFiles.deprecatedRuntimeConfigurationName: String - get() = disambiguateName("runtime") } } +internal val KotlinCompilation.deprecatedCompileConfigurationName: String + get() = disambiguateName("compile") + +internal val KotlinCompilationToRunnableFiles.deprecatedRuntimeConfigurationName: String + get() = disambiguateName("runtime") + open class KotlinTargetConfigurator( buildOutputCleanupRegistry: BuildOutputCleanupRegistry, createDefaultSourceSets: Boolean, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/ProjectLocalConfigurations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/ProjectLocalConfigurations.kt new file mode 100644 index 00000000000..4bbe44ab783 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/ProjectLocalConfigurations.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.* +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget +import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast + +internal object ProjectLocalConfigurations { + val ATTRIBUTE = Attribute.of("org.jetbrains.kotlin.localToProject", String::class.java) + + fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) = with(attributesSchema) { + attribute(ATTRIBUTE) { + if (gradleVersionSupportsAttributeRules) { + it.compatibilityRules.add(ProjectLocalCompatibility::class.java) + it.disambiguationRules.add(ProjectLocalDisambiguation::class.java) + } + } + } + + class ProjectLocalCompatibility : AttributeCompatibilityRule { + override fun execute(details: CompatibilityCheckDetails) { + details.compatible() + } + } + + class ProjectLocalDisambiguation : AttributeDisambiguationRule { + override fun execute(details: MultipleCandidatesDetails) = with(details) { + if (candidateValues.contains(null)) { + @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") + closestMatch(null as String?) + } + } + } +} + +internal fun Configuration.setupAsLocalTargetSpecificConfigurationIfSupported(target: KotlinTarget) { + if (gradleVersionSupportsAttributeRules && + // don't setup in old MPP common modules, as their output configurations with KotlinPlatformType attribute would + // fail to resolve as transitive dependencies of the platform modules, just as we don't mark their + // `api/RuntimeElements` with the KotlinPlatformType + (target !is KotlinWithJavaTarget || target.platformType != KotlinPlatformType.COMMON) + ) { + usesPlatformOf(target) + attributes.attribute(ProjectLocalConfigurations.ATTRIBUTE, target.project.path) + } +} + +internal val gradleVersionSupportsAttributeRules = isGradleVersionAtLeast(4, 0) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt index 0ef36a937c5..979f073cf32 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinUsages.kt @@ -66,7 +66,7 @@ object KotlinUsages { // if both API and runtime artifacts are chosen according to the compatibility rules, then // the consumer requested nothing specific, so provide them with the runtime variant, which is more complete: - if (candidateNames == setOf(KOTLIN_RUNTIME, KOTLIN_API)) { + if (candidateNames.filterNotNull().toSet() == setOf(KOTLIN_RUNTIME, KOTLIN_API)) { details.closestMatch(candidateValues.single { it?.name == KOTLIN_RUNTIME }!!) } if (JAVA_API in candidateNames && JAVA_RUNTIME_JARS in candidateNames && values.none { it in candidateNames }) {