From b983c93930b3cd374089b6149b8259d31343d985 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 16 Aug 2018 15:13:20 +0300 Subject: [PATCH] MPP: Add ImportedModule for non-Android source set modules The goal is to work around model disposal service which discards module data without ImportedModule instance --- .../KotlinAndroidMPPGradleProjectResolver.kt | 14 +++ ...tlinAndroidMPPGradleProjectResolver.kt.173 | 96 +++++++++++++++++++ ...linAndroidMPPGradleProjectResolver.kt.as31 | 96 +++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.173 create mode 100644 idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.as31 diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt index ca570f91ad8..3c140e15055 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt @@ -7,9 +7,12 @@ package org.jetbrains.kotlin.android.configure import com.android.builder.model.AndroidProject import com.android.builder.model.NativeAndroidProject +import com.android.tools.idea.gradle.project.sync.idea.data.model.ImportedModule +import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys.IMPORTED_MODULE 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 com.intellij.openapi.externalSystem.util.ExternalSystemConstants import com.intellij.openapi.externalSystem.util.Order import com.intellij.openapi.util.Key @@ -18,7 +21,9 @@ import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver import org.jetbrains.kotlin.idea.configuration.KotlinSourceSetInfo +import org.jetbrains.kotlin.idea.configuration.kotlinSourceSet import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension var DataNode.kotlinAndroidSourceSets: List? @@ -49,6 +54,15 @@ class KotlinAndroidMPPGradleProjectResolver : AbstractProjectResolverExtension() super.populateModuleContentRoots(gradleModule, ideModule) if (isAndroidProject) { KotlinMPPGradleProjectResolver.populateContentRoots(gradleModule, ideModule, resolverCtx) + // Work around module disposal service which discards modules without accompanying ImportedModule instance + for (childNode in ExternalSystemApiUtil.getChildren(ideModule, GradleSourceSetData.KEY)) { + if (childNode.kotlinSourceSet == null) continue + val moduleName = childNode.data.internalName + val importedModule = object : ImportedModule(gradleModule) { + override fun getName() = moduleName + } + ideModule.createChild(IMPORTED_MODULE, importedModule) + } } } diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.173 new file mode 100644 index 00000000000..14628cdbf47 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.173 @@ -0,0 +1,96 @@ +/* + * 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.android.configure + +import com.android.builder.model.AndroidProject +import com.android.builder.model.NativeAndroidProject +import com.android.tools.idea.gradle.ImportedModule +import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys.IMPORTED_MODULE +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 com.intellij.openapi.externalSystem.util.ExternalSystemConstants +import com.intellij.openapi.externalSystem.util.Order +import com.intellij.openapi.util.Key +import org.gradle.tooling.model.idea.IdeaModule +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder +import org.jetbrains.kotlin.gradle.KotlinPlatform +import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver +import org.jetbrains.kotlin.idea.configuration.KotlinSourceSetInfo +import org.jetbrains.kotlin.idea.configuration.kotlinSourceSet +import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension + +var DataNode.kotlinAndroidSourceSets: List? + by CopyableDataNodeUserDataProperty(Key.create("ANDROID_COMPILATIONS")) + +@Order(ExternalSystemConstants.UNORDERED - 1) +class KotlinAndroidMPPGradleProjectResolver : AbstractProjectResolverExtension() { + private val isAndroidProject by lazy { + resolverCtx.hasModulesWithModel(AndroidProject::class.java) + || resolverCtx.hasModulesWithModel(NativeAndroidProject::class.java) + } + + override fun getToolingExtensionsClasses(): Set> { + return setOf(KotlinMPPGradleModelBuilder::class.java, Unit::class.java) + } + + override fun getExtraProjectModelClasses(): Set> { + return setOf(KotlinMPPGradleModel::class.java) + } + + override fun createModule(gradleModule: IdeaModule, projectDataNode: DataNode): DataNode { + return super.createModule(gradleModule, projectDataNode).also { + initializeModuleData(gradleModule, it, projectDataNode) + } + } + + override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode) { + super.populateModuleContentRoots(gradleModule, ideModule) + if (isAndroidProject) { + KotlinMPPGradleProjectResolver.populateContentRoots(gradleModule, ideModule, resolverCtx) + // Work around module disposal service which discards modules without accompanying ImportedModule instance + for (childNode in ExternalSystemApiUtil.getChildren(ideModule, GradleSourceSetData.KEY)) { + if (childNode.kotlinSourceSet == null) continue + val moduleName = childNode.data.internalName + val importedModule = object : ImportedModule(gradleModule) { + override fun getName() = moduleName + } + ideModule.createChild(IMPORTED_MODULE, importedModule) + } + } + } + + override fun populateModuleDependencies(gradleModule: IdeaModule, ideModule: DataNode, ideProject: DataNode) { + super.populateModuleDependencies(gradleModule, ideModule, ideProject) + if (isAndroidProject) { + KotlinMPPGradleProjectResolver.populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx) + } + } + + private fun initializeModuleData( + gradleModule: IdeaModule, + mainModuleData: DataNode, + projectDataNode: DataNode + ) { + if (!isAndroidProject) return + + KotlinMPPGradleProjectResolver.initializeModuleData(gradleModule, mainModuleData, projectDataNode, resolverCtx) + + val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return + + mainModuleData.kotlinAndroidSourceSets = mppModel + .targets + .asSequence() + .flatMap { it.compilations.asSequence() } + .filter { it.platform == KotlinPlatform.ANDROID } + .map { KotlinMPPGradleProjectResolver.createSourceSetInfo(it, gradleModule, resolverCtx) } + .toList() + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.as31 b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.as31 new file mode 100644 index 00000000000..011ff1f2b7b --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidMPPGradleProjectResolver.kt.as31 @@ -0,0 +1,96 @@ +/* + * 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.android.configure + +import com.android.builder.model.AndroidProject +import com.android.builder.model.NativeAndroidProject +import com.android.tools.idea.gradle.project.sync.idea.data.model.ImportedModule +import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys.IMPORTED_MODULE +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 com.intellij.openapi.externalSystem.util.ExternalSystemConstants +import com.intellij.openapi.externalSystem.util.Order +import com.intellij.openapi.util.Key +import org.gradle.tooling.model.idea.IdeaModule +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder +import org.jetbrains.kotlin.gradle.KotlinPlatform +import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver +import org.jetbrains.kotlin.idea.configuration.KotlinSourceSetInfo +import org.jetbrains.kotlin.idea.configuration.kotlinSourceSet +import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension + +var DataNode.kotlinAndroidSourceSets: List? + by CopyableDataNodeUserDataProperty(Key.create("ANDROID_COMPILATIONS")) + +@Order(ExternalSystemConstants.UNORDERED - 1) +class KotlinAndroidMPPGradleProjectResolver : AbstractProjectResolverExtension() { + private val isAndroidProject by lazy { + resolverCtx.hasModulesWithModel(AndroidProject::class.java) + || resolverCtx.hasModulesWithModel(NativeAndroidProject::class.java) + } + + override fun getToolingExtensionsClasses(): Set> { + return setOf(KotlinMPPGradleModelBuilder::class.java, Unit::class.java) + } + + override fun getExtraProjectModelClasses(): Set> { + return setOf(KotlinMPPGradleModel::class.java) + } + + override fun createModule(gradleModule: IdeaModule, projectDataNode: DataNode): DataNode { + return super.createModule(gradleModule, projectDataNode).also { + initializeModuleData(gradleModule, it, projectDataNode) + } + } + + override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode) { + super.populateModuleContentRoots(gradleModule, ideModule) + if (isAndroidProject) { + KotlinMPPGradleProjectResolver.populateContentRoots(gradleModule, ideModule, resolverCtx) + // Work around module disposal service which discards modules without accompanying ImportedModule instance + for (childNode in ExternalSystemApiUtil.getChildren(ideModule, GradleSourceSetData.KEY)) { + if (childNode.kotlinSourceSet == null) continue + val moduleName = childNode.data.internalName + val importedModule = object : ImportedModule(gradleModule) { + override fun getName() = moduleName + } + ideModule.createChild(IMPORTED_MODULE, importedModule) + } + } + } + + override fun populateModuleDependencies(gradleModule: IdeaModule, ideModule: DataNode, ideProject: DataNode) { + super.populateModuleDependencies(gradleModule, ideModule, ideProject) + if (isAndroidProject) { + KotlinMPPGradleProjectResolver.populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx) + } + } + + private fun initializeModuleData( + gradleModule: IdeaModule, + mainModuleData: DataNode, + projectDataNode: DataNode + ) { + if (!isAndroidProject) return + + KotlinMPPGradleProjectResolver.initializeModuleData(gradleModule, mainModuleData, projectDataNode, resolverCtx) + + val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return + + mainModuleData.kotlinAndroidSourceSets = mppModel + .targets + .asSequence() + .flatMap { it.compilations.asSequence() } + .filter { it.platform == KotlinPlatform.ANDROID } + .map { KotlinMPPGradleProjectResolver.createSourceSetInfo(it, gradleModule, resolverCtx) } + .toList() + } +} \ No newline at end of file