diff --git a/idea/idea-android/build.gradle.kts b/idea/idea-android/build.gradle.kts index 2d41224b058..e31c039ea98 100644 --- a/idea/idea-android/build.gradle.kts +++ b/idea/idea-android/build.gradle.kts @@ -14,7 +14,7 @@ dependencies { compile(ideaSdkDeps("openapi", "idea")) compile(ideaPluginDeps("gradle-tooling-api", plugin = "gradle")) - compile(ideaPluginDeps("android", "android-common", "sdklib", "sdk-common", "layoutlib-api", plugin = "android")) + compile(ideaPluginDeps("android", "android-common", "sdklib", "sdk-common", "sdk-tools", "layoutlib-api", plugin = "android")) compile(preloadedDeps("dx", subdir = "android-5.0/lib")) testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/AndroidGradleModelFacade.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/AndroidGradleModelFacade.kt index f4e45e564f1..79a6125c577 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/AndroidGradleModelFacade.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/AndroidGradleModelFacade.kt @@ -34,8 +34,12 @@ package org.jetbrains.kotlin.android.configure import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.project.ProjectData import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import org.gradle.tooling.model.idea.IdeaProject import org.jetbrains.kotlin.idea.inspections.gradle.KotlinGradleModelFacade +import org.jetbrains.kotlin.idea.inspections.gradle.findModulesByNames class AndroidGradleModelFacade : KotlinGradleModelFacade { override fun getResolvedKotlinStdlibVersionByModuleData(moduleData: DataNode<*>, libraryIds: List): String? { @@ -51,4 +55,24 @@ class AndroidGradleModelFacade : KotlinGradleModelFacade { } return null } -} \ No newline at end of file + + override fun getDependencyModules(ideModule: DataNode, gradleIdeaProject: IdeaProject): Collection> { + val ideProject = ideModule.parent as DataNode + ExternalSystemApiUtil.find(ideModule, AndroidProjectKeys.JAVA_MODULE_MODEL)?.let { javaModuleModel -> + val moduleNames = javaModuleModel.data.javaModuleDependencies.map { it.moduleName } + return findModulesByNames(moduleNames, gradleIdeaProject, ideProject) + } + ExternalSystemApiUtil.find(ideModule, AndroidProjectKeys.ANDROID_MODEL)?.let { androidModel -> + val libraries = androidModel.data.mainArtifact.dependencies.javaLibraries + val projects = androidModel.data.mainArtifact.dependencies.projects + val projectIds = libraries.mapNotNull { it.project } + projects + return projectIds.mapNotNullTo(LinkedHashSet()) { projectId -> + ExternalSystemApiUtil.findFirstRecursively(ideProject) { + (it.data as? ModuleData)?.id == projectId + } as DataNode? + } + + } + return emptyList() + } +} diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt index 58e6411b687..8521d20ad25 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt @@ -26,10 +26,10 @@ import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.util.Key import com.intellij.openapi.util.io.FileUtil import org.gradle.tooling.model.idea.IdeaModule -import org.gradle.tooling.model.idea.IdeaModuleDependency import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet import org.jetbrains.kotlin.gradle.KotlinGradleModel import org.jetbrains.kotlin.gradle.KotlinGradleModelBuilder +import org.jetbrains.kotlin.idea.inspections.gradle.getDependencyModules import org.jetbrains.kotlin.psi.NotNullableUserDataProperty import org.jetbrains.kotlin.psi.UserDataProperty import org.jetbrains.plugins.gradle.model.ExternalProjectDependency @@ -81,10 +81,7 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() val gradleIdeaProject = gradleModule.project - fun DataNode.getGradleModule() = - if (this != ideModule) gradleIdeaProject.modules.firstOrNull { it.gradleProject.path == data.id } else gradleModule - - fun DataNode.getDependencies(): Set> { + fun DataNode.getDependencies(): Collection> { if (useModulePerSourceSet()) { if (sourceSetByName == null) return emptySet() val externalSourceSet = sourceSetByName[data.id]?.second ?: return emptySet() @@ -110,14 +107,7 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() } } - val dependencies = getGradleModule()?.dependencies - return dependencies?.mapNotNullTo(LinkedHashSet()) { - val targetModuleName = (it as? IdeaModuleDependency)?.targetModuleName ?: return@mapNotNullTo null - val targetGradleModule = gradleIdeaProject.modules.firstOrNull { it.name == targetModuleName } ?: return@mapNotNullTo null - ExternalSystemApiUtil.findFirstRecursively(ideProject) { - (it.data as? ModuleData)?.id == targetGradleModule.gradleProject.path - } as DataNode? - } ?: emptySet() + return getDependencyModules(ideModule, gradleIdeaProject) } fun addTransitiveDependenciesOnImplementedModules() { diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/KotlinGradleModelFacade.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/KotlinGradleModelFacade.kt index abf20daa806..29d6851f5c8 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/KotlinGradleModelFacade.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/KotlinGradleModelFacade.kt @@ -17,9 +17,14 @@ package org.jetbrains.kotlin.idea.inspections.gradle import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.externalSystem.model.DataNode import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.project.ProjectData import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import org.gradle.tooling.model.idea.IdeaModuleDependency +import org.gradle.tooling.model.idea.IdeaProject interface KotlinGradleModelFacade { companion object { @@ -27,6 +32,7 @@ interface KotlinGradleModelFacade { } fun getResolvedKotlinStdlibVersionByModuleData(moduleData: DataNode<*>, libraryIds: List): String? + fun getDependencyModules(ideModule: DataNode, gradleIdeaProject: IdeaProject): Collection> } class DefaultGradleModelFacade : KotlinGradleModelFacade { @@ -41,4 +47,30 @@ class DefaultGradleModelFacade : KotlinGradleModelFacade { } return null } -} \ No newline at end of file + + override fun getDependencyModules(ideModule: DataNode, gradleIdeaProject: IdeaProject): Collection> { + val ideProject = ideModule.parent as DataNode + val gradleModule = gradleIdeaProject.modules.firstOrNull { it.gradleProject.path == ideModule.data.id } + val dependencyModuleNames = gradleModule?.dependencies?.mapNotNull { (it as? IdeaModuleDependency)?.targetModuleName } ?: return emptyList() + return findModulesByNames(dependencyModuleNames, gradleIdeaProject, ideProject) + } +} + +fun getDependencyModules(moduleData: DataNode, gradleIdeaProject: IdeaProject): Collection> { + for (modelFacade in Extensions.getExtensions(KotlinGradleModelFacade.EP_NAME)) { + val dependencies = modelFacade.getDependencyModules(moduleData, gradleIdeaProject) + if (dependencies.isNotEmpty()) { + return dependencies + } + } + return emptyList() +} + +fun findModulesByNames(dependencyModuleNames: List, gradleIdeaProject: IdeaProject, ideProject: DataNode): LinkedHashSet> { + return dependencyModuleNames.mapNotNullTo(LinkedHashSet()) { targetModuleName -> + val targetGradleModule = gradleIdeaProject.modules.firstOrNull { it.name == targetModuleName } ?: return@mapNotNullTo null + ExternalSystemApiUtil.findFirstRecursively(ideProject) { + (it.data as? ModuleData)?.id == targetGradleModule.gradleProject.path + } as DataNode? + } +} 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 ba22c1e8f4f..953955365ad 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/MultiplatformProjectImportingTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/MultiplatformProjectImportingTest.kt @@ -17,8 +17,10 @@ package org.jetbrains.kotlin.gradle import com.intellij.openapi.roots.DependencyScope +import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.idea.codeInsight.gradle.GradleImportingTestCase import org.junit.Test +import java.io.File class MultiplatformProjectImportingTest : GradleImportingTestCase() { @Test @@ -385,4 +387,94 @@ class MultiplatformProjectImportingTest : GradleImportingTestCase() { assertModuleModuleDepScope("project3_custom", "project1_custom", DependencyScope.COMPILE) assertModuleModuleDepScope("project3_custom", "project2_main", DependencyScope.COMPILE) } + + @Test + fun testTransitiveImplementWithAndroid() { + createProjectSubFile( + "settings.gradle", + "include ':project1', ':project2', ':project3'" + ) + + val kotlinVersion = "1.1.51" + + createProjectSubFile("build.gradle", """ + buildscript { + repositories { + jcenter() + maven { url 'https://maven.google.com' } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + classpath 'com.android.tools.build:gradle:2.3.3' + } + } + + project('project1') { + apply plugin: 'kotlin-platform-common' + + sourceSets { + custom + } + } + + project('project2') { + repositories { + mavenCentral() + } + + apply plugin: 'kotlin-platform-jvm' + + sourceSets { + custom + } + + dependencies { + implement project(':project1') + } + } + + project('project3') { + repositories { + mavenCentral() + } + + apply plugin: 'com.android.application' + apply plugin: 'kotlin-android' + + sourceSets { + custom + } + + android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + defaultConfig { + applicationId "org.jetbrains.kotlin" + minSdkVersion 18 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + } + + dependencies { + compile project(':project2') + customCompile project(':project2') + testCompile(project(':project2').sourceSets.test.output) + } + } + """) + createProjectSubFile("local.properties", """ + sdk.dir=/${StringUtil.escapeBackSlashes(File(homePath).parent + "/dependencies/androidSDK")} + """) + + + importProject() + + assertModuleModuleDepScope("project3", "project2", DependencyScope.COMPILE) + assertModuleModuleDepScope("project3", "project1", DependencyScope.COMPILE) + + } } \ No newline at end of file