From 514fc029384a9a6d7068feb4a9d40388ad1b07d4 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 20 Dec 2016 14:03:48 +0100 Subject: [PATCH] Propagate 'implement' dependencies from Gradle to IDEA project model --- .idea/artifacts/KotlinPlugin.xml | 3 + .idea/modules.xml | 1 + idea/idea.iml | 1 + .../kotlin-gradle-tooling.iml | 13 +++ .../src/KotlinGradleModelBuilder.kt | 49 ++++++++++ ...plugins.gradle.tooling.ModelBuilderService | 1 + idea/src/META-INF/gradle.xml | 1 + .../KotlinGradleProjectResolverExtension.kt | 89 +++++++++++++++++++ 8 files changed, 158 insertions(+) create mode 100644 idea/kotlin-gradle-tooling/kotlin-gradle-tooling.iml create mode 100644 idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt create mode 100644 idea/kotlin-gradle-tooling/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService create mode 100644 idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml index 857c5128ff4..e69ea537167 100644 --- a/.idea/artifacts/KotlinPlugin.xml +++ b/.idea/artifacts/KotlinPlugin.xml @@ -100,6 +100,9 @@ + + + diff --git a/.idea/modules.xml b/.idea/modules.xml index d68d76f1106..79e1cc0a506 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -74,6 +74,7 @@ + diff --git a/idea/idea.iml b/idea/idea.iml index f9955257518..9cf85b87c4e 100644 --- a/idea/idea.iml +++ b/idea/idea.iml @@ -58,5 +58,6 @@ + \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/kotlin-gradle-tooling.iml b/idea/kotlin-gradle-tooling/kotlin-gradle-tooling.iml new file mode 100644 index 00000000000..8d6ac6d91f9 --- /dev/null +++ b/idea/kotlin-gradle-tooling/kotlin-gradle-tooling.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt new file mode 100644 index 00000000000..641f4322ac4 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.gradle + +import org.gradle.api.Project +import org.gradle.api.artifacts.ProjectDependency +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService +import java.io.Serializable +import java.lang.Exception + +interface KotlinGradleModel : Serializable { + val implements: String? +} + +class KotlinGradleModelImpl(override val implements: String?) : KotlinGradleModel + +class KotlinGradleModelBuilder : ModelBuilderService { + override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder { + return ErrorMessageBuilder.create(project, e, "Gradle import errors").withDescription("Unable to build Kotlin project configuration") + } + + override fun canBuild(modelName: String?): Boolean = modelName == KotlinGradleModel::class.java.name + + override fun buildAll(modelName: String?, project: Project): Any { + val implementsConfiguration = project.configurations.findByName("implement") + if (implementsConfiguration != null) { + val implementsProjectDependency = implementsConfiguration.dependencies.filterIsInstance().firstOrNull() + if (implementsProjectDependency != null) { + return KotlinGradleModelImpl(implementsProjectDependency.dependencyProject.path) + } + } + return KotlinGradleModelImpl(null) + } +} diff --git a/idea/kotlin-gradle-tooling/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService b/idea/kotlin-gradle-tooling/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService new file mode 100644 index 00000000000..b8452a8e834 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService @@ -0,0 +1 @@ +org.jetbrains.kotlin.gradle.KotlinGradleModelBuilder diff --git a/idea/src/META-INF/gradle.xml b/idea/src/META-INF/gradle.xml index 5f861ec32b6..25c3ed02c85 100644 --- a/idea/src/META-INF/gradle.xml +++ b/idea/src/META-INF/gradle.xml @@ -2,6 +2,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt new file mode 100644 index 00000000000..08645035277 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.configuration + +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.ModuleDependencyData +import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.openapi.roots.DependencyScope +import org.gradle.tooling.model.idea.IdeaModule +import org.jetbrains.kotlin.gradle.KotlinGradleModel +import org.jetbrains.kotlin.gradle.KotlinGradleModelBuilder +import org.jetbrains.plugins.gradle.model.ExternalProject +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension +import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.getModuleId + +class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() { + override fun getToolingExtensionsClasses(): Set> { + return setOf(KotlinGradleModelBuilder::class.java, Unit::class.java) + } + + override fun getExtraProjectModelClasses(): Set> { + return setOf(KotlinGradleModel::class.java) + } + + override fun populateModuleDependencies(gradleModule: IdeaModule, + ideModule: DataNode, + ideProject: DataNode) { + val gradleModel = resolverCtx.getExtraProject(gradleModule, KotlinGradleModel::class.java) ?: return + gradleModel.implements?.let { implementsModuleId -> + val targetModule = findModule(ideProject, implementsModuleId) ?: return + if (resolverCtx.isResolveModulePerSourceSet) { + populateSourceSetDependencies(gradleModule, ideModule, targetModule) + } + else { + addDependency(ideModule, targetModule) + } + } + + super.populateModuleDependencies(gradleModule, ideModule, ideProject) + } + + private fun addDependency(ideModule: DataNode, targetModule: DataNode) { + val moduleDependencyData = ModuleDependencyData(ideModule.data, targetModule.data) + moduleDependencyData.scope = DependencyScope.COMPILE + moduleDependencyData.isExported = false + ideModule.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData) + } + + private fun findModule(ideProject: DataNode, moduleId: String): DataNode? { + return ideProject.children.find { (it.data as? ModuleData)?.id == moduleId } as DataNode? + } + + private fun populateSourceSetDependencies(gradleModule: IdeaModule, + fromModule: DataNode, + targetModule: DataNode) { + val fromSourceSets = ExternalSystemApiUtil.findAll(fromModule, GradleSourceSetData.KEY) + .associateBy { it.data.id } + val targetSourceSets = ExternalSystemApiUtil.findAll(targetModule, GradleSourceSetData.KEY) + .associateBy { it.data.id.substringAfterLast(':') } + + val externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java) ?: return + for (sourceSet in externalProject.sourceSets.values) { + if (sourceSet == null || sourceSet.sources.isEmpty()) continue + + val moduleId = getModuleId(externalProject, sourceSet) + val fromModuleDataNode = (if (fromSourceSets.isEmpty()) fromModule else fromSourceSets[moduleId]) ?: continue + val targetModuleDataNode = (if (targetSourceSets.isEmpty()) targetModule else targetSourceSets[sourceSet.name]) ?: continue + addDependency(fromModuleDataNode, targetModuleDataNode) + } + } +}