diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/MultiplatformProjectImportingTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/MultiplatformProjectImportingTest.kt index dcd64054bb1..1a84b62d6a9 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/MultiplatformProjectImportingTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/MultiplatformProjectImportingTest.kt @@ -183,4 +183,71 @@ class MultiplatformProjectImportingTest : GradleImportingTestCase() { assertModuleModuleDepScope("js-app_main", "common-lib_main", DependencyScope.COMPILE) assertModuleModuleDepScope("js-app_main", "js-lib_main", DependencyScope.COMPILE) } + + @Test + fun testDependenciesReachableViaImpl() { + createProjectSubFile( + "settings.gradle", + "include ':common-lib1', ':common-lib2', ':jvm-lib1', ':jvm-lib2', ':jvm-app'" + ) + + val kotlinVersion = "1.1.0" + + createProjectSubFile("build.gradle", """ + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + } + } + + project('common-lib1') { + apply plugin: 'kotlin-platform-common' + } + + project('common-lib2') { + apply plugin: 'kotlin-platform-common' + } + + project('jvm-lib1') { + apply plugin: 'kotlin-platform-jvm' + + dependencies { + implement project(':common-lib1') + } + } + + project('jvm-lib2') { + apply plugin: 'kotlin-platform-jvm' + + dependencies { + implement project(':common-lib2') + compile project(':jvm-lib1') + } + } + + project('jvm-app') { + apply plugin: 'kotlin-platform-jvm' + + dependencies { + compile project(':jvm-lib2') + } + } + """) + + importProject() + + assertModuleModuleDepScope("jvm-app_main", "jvm-lib2_main", DependencyScope.COMPILE) + assertModuleModuleDepScope("jvm-app_main", "jvm-lib1_main", DependencyScope.COMPILE) + assertModuleModuleDepScope("jvm-app_main", "common-lib1_main", DependencyScope.COMPILE) + assertModuleModuleDepScope("jvm-app_main", "common-lib2_main", DependencyScope.COMPILE) + + assertModuleModuleDepScope("jvm-app_test", "jvm-lib2_main", DependencyScope.COMPILE) + assertModuleModuleDepScope("jvm-app_test", "jvm-lib1_main", DependencyScope.COMPILE) + assertModuleModuleDepScope("jvm-app_test", "common-lib1_test", DependencyScope.COMPILE) + assertModuleModuleDepScope("jvm-app_test", "common-lib2_test", DependencyScope.COMPILE) + } } \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt index 7702187ec66..2435adb3de9 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt @@ -96,34 +96,44 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() { return implementsProjectDependency?.dependencyProject } + private val Project.isCommon get() = plugins.hasPlugin(kotlinPlatformCommonPluginId) + private fun transitiveCommonDependencies(startingProject: Project): Set { - val toProcess = LinkedList() - toProcess.add(startingProject) + val toProcess = LinkedList>() + toProcess.add(startingProject to false) val processed = HashSet() val result = HashSet() - result.add(startingProject.pathOrName()) - while (toProcess.isNotEmpty()) { - val project = toProcess.pollFirst() - processed.add(project.path) + fun enqueueDependencies(project: Project, isReachableViaImpl: Boolean, collectImpls: Boolean) { + val isCommonProject = project.isCommon + if (isCommonProject && collectImpls) return - if (!project.plugins.hasPlugin(kotlinPlatformCommonPluginId)) continue - - result.add(project.pathOrName()) - - val compileConfiguration = project.configurations.findByName("compile") ?: continue + val configName = if (collectImpls) "implement" else "compile" + val compileConfiguration = project.configurations.findByName(configName) ?: return val dependencies = compileConfiguration .dependencies .filterIsInstance() .map { it.dependencyProject } for (dep in dependencies) { - if (dep.path !in processed) { - toProcess.add(dep) - } + if (dep.path in processed) continue + if (!dep.isCommon && (collectImpls || isCommonProject)) continue + toProcess.add(dep to (isReachableViaImpl || collectImpls)) } } + while (toProcess.isNotEmpty()) { + val (project, isReachableViaImpl) = toProcess.pollFirst() + processed.add(project.path) + + if (isReachableViaImpl) { + result.add(project.pathOrName()) + } + + enqueueDependencies(project, isReachableViaImpl, false) + enqueueDependencies(project, isReachableViaImpl, true) + } + return result } @@ -196,7 +206,7 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() { val platform = platformPluginId ?: pluginToPlatform.entries.singleOrNull { project.plugins.findPlugin(it.key) != null }?.value val implementedProject = getImplements(project) - val transitiveCommon = implementedProject?.let { transitiveCommonDependencies(it) } ?: emptySet() + val transitiveCommon = transitiveCommonDependencies(project) return KotlinGradleModelImpl( kotlinPluginId != null || platformPluginId != null,