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
This commit is contained in:
Alexey Sedunov
2018-08-16 15:13:20 +03:00
parent 80102fc2bc
commit b983c93930
3 changed files with 206 additions and 0 deletions
@@ -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<ModuleData>.kotlinAndroidSourceSets: List<KotlinSourceSetInfo>?
@@ -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)
}
}
}
@@ -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<ModuleData>.kotlinAndroidSourceSets: List<KotlinSourceSetInfo>?
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<Class<out Any>> {
return setOf(KotlinMPPGradleModelBuilder::class.java, Unit::class.java)
}
override fun getExtraProjectModelClasses(): Set<Class<out Any>> {
return setOf(KotlinMPPGradleModel::class.java)
}
override fun createModule(gradleModule: IdeaModule, projectDataNode: DataNode<ProjectData>): DataNode<ModuleData> {
return super.createModule(gradleModule, projectDataNode).also {
initializeModuleData(gradleModule, it, projectDataNode)
}
}
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
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<ModuleData>, ideProject: DataNode<ProjectData>) {
super.populateModuleDependencies(gradleModule, ideModule, ideProject)
if (isAndroidProject) {
KotlinMPPGradleProjectResolver.populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx)
}
}
private fun initializeModuleData(
gradleModule: IdeaModule,
mainModuleData: DataNode<ModuleData>,
projectDataNode: DataNode<ProjectData>
) {
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()
}
}
@@ -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<ModuleData>.kotlinAndroidSourceSets: List<KotlinSourceSetInfo>?
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<Class<out Any>> {
return setOf(KotlinMPPGradleModelBuilder::class.java, Unit::class.java)
}
override fun getExtraProjectModelClasses(): Set<Class<out Any>> {
return setOf(KotlinMPPGradleModel::class.java)
}
override fun createModule(gradleModule: IdeaModule, projectDataNode: DataNode<ProjectData>): DataNode<ModuleData> {
return super.createModule(gradleModule, projectDataNode).also {
initializeModuleData(gradleModule, it, projectDataNode)
}
}
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
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<ModuleData>, ideProject: DataNode<ProjectData>) {
super.populateModuleDependencies(gradleModule, ideModule, ideProject)
if (isAndroidProject) {
KotlinMPPGradleProjectResolver.populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx)
}
}
private fun initializeModuleData(
gradleModule: IdeaModule,
mainModuleData: DataNode<ModuleData>,
projectDataNode: DataNode<ProjectData>
) {
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()
}
}