Gradle: Implement new MPP model import

This commit is contained in:
Alexey Sedunov
2018-08-03 18:54:40 +03:00
committed by Sergey Igushkin
parent caec846330
commit ecf607d4fa
32 changed files with 2128 additions and 10 deletions
@@ -23,12 +23,14 @@ import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties
@Suppress("UNCHECKED_CAST")
fun <T : Any> copyBean(bean: T) =
copyProperties(bean, bean::class.java.newInstance()!!, true, collectProperties(bean::class as KClass<T>, false))
fun <T : Any> copyBean(bean: T) = copyBeanTo(bean, bean::class.java.newInstance()!!)
@Suppress("UNCHECKED_CAST")
fun <T : Any> copyBeanTo(from: T, to: T, filter: ((KProperty1<T, Any?>, Any?) -> Boolean)? = null) =
copyProperties(from, to, true, collectProperties(from::class as KClass<T>, false), filter)
fun <From : Any, To : From> mergeBeans(from: From, to: To): To {
// TODO: rewrite when updated version of com.intellij.util.xmlb is available on TeamCity
@@ -48,7 +50,8 @@ private fun <From : Any, To : Any> copyProperties(
from: From,
to: To,
deepCopyWhenNeeded: Boolean,
propertiesToCopy: List<KProperty1<From, Any?>>
propertiesToCopy: List<KProperty1<From, Any?>>,
filter: ((KProperty1<From, Any?>, Any?) -> Boolean)? = null
): To {
if (from == to) return to
@@ -57,6 +60,7 @@ private fun <From : Any, To : Any> copyProperties(
val toProperty = to::class.memberProperties.firstOrNull { it.name == fromProperty.name } as? KMutableProperty1<To, Any?>
?: continue
val fromValue = fromProperty.get(from)
if (filter != null && !filter(fromProperty, fromValue)) continue
toProperty.set(to, if (deepCopyWhenNeeded) fromValue?.copyValueIfNeeded() else fromValue)
}
return to
@@ -12,6 +12,7 @@ import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModulePointerManager
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.FileIndex
@@ -145,3 +146,6 @@ val PsiElement.module: Module?
get() = ModuleUtilCore.findModuleForPsiElement(this)
fun VirtualFile.findModule(project: Project) = ModuleUtilCore.findModuleForFile(this, project)
fun Module.createPointer() =
ModulePointerManager.getInstance(project).create(this)
@@ -0,0 +1,72 @@
/*
* 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.tools.idea.gradle.project.model.AndroidModuleModel
import com.android.tools.idea.gradle.util.ContentEntries.findParentContentEntry
import com.android.tools.idea.io.FilePaths
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.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.util.containers.stream
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.gradle.KotlinCompilation
import java.io.File
class KotlinAndroidGradleMPPModuleDataService : AbstractProjectDataService<ModuleData, Void>() {
override fun getTargetDataKey() = ProjectKeys.MODULE
override fun postProcess(
toImport: MutableCollection<DataNode<ModuleData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val projectNode = ExternalSystemApiUtil.findParent(nodeToImport, ProjectKeys.PROJECT) ?: continue
val moduleData = nodeToImport.data
val module = modelsProvider.findIdeModule(moduleData) ?: continue
val androidModel = AndroidModuleModel.get(module) ?: continue
val variantName = androidModel.selectedVariant.name
val sourceSetInfo = nodeToImport.kotlinAndroidSourceSets?.firstOrNull { it.kotlinModule.name == variantName } ?: continue
val compilation = sourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
val rootModel = modelsProvider.getModifiableRootModel(module)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.isAndroid) {
val sourceType = if (sourceSet.isTestModule) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
val resourceType = if (sourceSet.isTestModule) JavaResourceRootType.TEST_RESOURCE else JavaResourceRootType.RESOURCE
sourceSet.sourceDirs.forEach { addSourceRoot(it, sourceType, rootModel) }
sourceSet.resourceDirs.forEach { addSourceRoot(it, resourceType, rootModel) }
} else {
val sourceSetId = sourceSetInfo.sourceSetIdsByName[sourceSet.name] ?: continue
val sourceSetData = ExternalSystemApiUtil.findFirstRecursively(projectNode) {
(it.data as? ModuleData)?.id == sourceSetId
}?.data as? ModuleData ?: continue
val sourceSetModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
rootModel.addModuleOrderEntry(sourceSetModule)
}
}
}
}
private fun addSourceRoot(
sourceRoot: File,
type: JpsModuleSourceRootType<*>,
rootModel: ModifiableRootModel
) {
val parent = findParentContentEntry(sourceRoot, rootModel.contentEntries.stream()) ?: return
val url = FilePaths.pathToIdeaUrl(sourceRoot)
parent.addSourceFolder(url, type)
}
}
@@ -0,0 +1,70 @@
/*
* 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.tools.idea.gradle.project.model.AndroidModuleModel
import com.android.tools.idea.gradle.util.FilePaths
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.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModifiableRootModel
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.gradle.KotlinCompilation
import java.io.File
class KotlinAndroidGradleMPPModuleDataService : AbstractProjectDataService<ModuleData, Void>() {
override fun getTargetDataKey() = ProjectKeys.MODULE
override fun postProcess(
toImport: MutableCollection<DataNode<ModuleData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val projectNode = ExternalSystemApiUtil.findParent(nodeToImport, ProjectKeys.PROJECT) ?: continue
val moduleData = nodeToImport.data
val module = modelsProvider.findIdeModule(moduleData) ?: continue
val androidModel = AndroidModuleModel.get(module) ?: continue
val variantName = androidModel.selectedVariant.name
val sourceSetInfo = nodeToImport.kotlinAndroidSourceSets?.firstOrNull { it.kotlinModule.name == variantName } ?: continue
val compilation = sourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
val rootModel = modelsProvider.getModifiableRootModel(module)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.isAndroid) {
val sourceType = if (sourceSet.isTestModule) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
val resourceType = if (sourceSet.isTestModule) JavaResourceRootType.TEST_RESOURCE else JavaResourceRootType.RESOURCE
sourceSet.sourceDirs.forEach { addSourceRoot(it, sourceType, rootModel) }
sourceSet.resourceDirs.forEach { addSourceRoot(it, resourceType, rootModel) }
} else {
val sourceSetId = sourceSetInfo.sourceSetIdsByName[sourceSet.name] ?: continue
val sourceSetData = ExternalSystemApiUtil.findFirstRecursively(projectNode) {
(it.data as? ModuleData)?.id == sourceSetId
}?.data as? ModuleData ?: continue
val sourceSetModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
rootModel.addModuleOrderEntry(sourceSetModule)
}
}
}
}
private fun addSourceRoot(
sourceRoot: File,
type: JpsModuleSourceRootType<*>,
rootModel: ModifiableRootModel
) {
val parent = FilePaths.findParentContentEntry(sourceRoot, rootModel.contentEntries) ?: return
val url = FilePaths.pathToIdeaUrl(sourceRoot)
parent.addSourceFolder(url, type)
}
}
@@ -0,0 +1,72 @@
/*
* 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.tools.idea.gradle.project.model.AndroidModuleModel
import com.android.tools.idea.gradle.util.ContentEntries.findParentContentEntry
import com.android.tools.idea.gradle.util.FilePaths
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.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.util.containers.stream
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.gradle.KotlinCompilation
import java.io.File
class KotlinAndroidGradleMPPModuleDataService : AbstractProjectDataService<ModuleData, Void>() {
override fun getTargetDataKey() = ProjectKeys.MODULE
override fun postProcess(
toImport: MutableCollection<DataNode<ModuleData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val projectNode = ExternalSystemApiUtil.findParent(nodeToImport, ProjectKeys.PROJECT) ?: continue
val moduleData = nodeToImport.data
val module = modelsProvider.findIdeModule(moduleData) ?: continue
val androidModel = AndroidModuleModel.get(module) ?: continue
val variantName = androidModel.selectedVariant.name
val sourceSetInfo = nodeToImport.kotlinAndroidSourceSets?.firstOrNull { it.kotlinModule.name == variantName } ?: continue
val compilation = sourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
val rootModel = modelsProvider.getModifiableRootModel(module)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.isAndroid) {
val sourceType = if (sourceSet.isTestModule) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
val resourceType = if (sourceSet.isTestModule) JavaResourceRootType.TEST_RESOURCE else JavaResourceRootType.RESOURCE
sourceSet.sourceDirs.forEach { addSourceRoot(it, sourceType, rootModel) }
sourceSet.resourceDirs.forEach { addSourceRoot(it, resourceType, rootModel) }
} else {
val sourceSetId = sourceSetInfo.sourceSetIdsByName[sourceSet.name] ?: continue
val sourceSetData = ExternalSystemApiUtil.findFirstRecursively(projectNode) {
(it.data as? ModuleData)?.id == sourceSetId
}?.data as? ModuleData ?: continue
val sourceSetModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
rootModel.addModuleOrderEntry(sourceSetModule)
}
}
}
}
private fun addSourceRoot(
sourceRoot: File,
type: JpsModuleSourceRootType<*>,
rootModel: ModifiableRootModel
) {
val parent = findParentContentEntry(sourceRoot, rootModel.contentEntries.stream()) ?: return
val url = FilePaths.pathToIdeaUrl(sourceRoot)
parent.addSourceFolder(url, type)
}
}
@@ -0,0 +1,72 @@
/*
* 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.tools.idea.gradle.project.model.AndroidModuleModel
import com.android.tools.idea.gradle.util.ContentEntries.findParentContentEntry
import com.android.tools.idea.io.FilePaths
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.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.util.containers.stream
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.gradle.KotlinCompilation
import java.io.File
class KotlinAndroidGradleMPPModuleDataService : AbstractProjectDataService<ModuleData, Void>() {
override fun getTargetDataKey() = ProjectKeys.MODULE
override fun postProcess(
toImport: MutableCollection<DataNode<ModuleData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val projectNode = ExternalSystemApiUtil.findParent(nodeToImport, ProjectKeys.PROJECT) ?: continue
val moduleData = nodeToImport.data
val module = modelsProvider.findIdeModule(moduleData) ?: continue
val androidModel = AndroidModuleModel.get(module) ?: continue
val variantName = androidModel.selectedVariant.name
val sourceSetInfo = nodeToImport.kotlinAndroidSourceSets?.firstOrNull { it.kotlinModule.name == variantName } ?: continue
val compilation = sourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
val rootModel = modelsProvider.getModifiableRootModel(module)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.isAndroid) {
val sourceType = if (sourceSet.isTestModule) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
val resourceType = if (sourceSet.isTestModule) JavaResourceRootType.TEST_RESOURCE else JavaResourceRootType.RESOURCE
sourceSet.sourceDirs.forEach { addSourceRoot(it, sourceType, rootModel) }
sourceSet.resourceDirs.forEach { addSourceRoot(it, resourceType, rootModel) }
} else {
val sourceSetId = sourceSetInfo.sourceSetIdsByName[sourceSet.name] ?: continue
val sourceSetData = ExternalSystemApiUtil.findFirstRecursively(projectNode) {
(it.data as? ModuleData)?.id == sourceSetId
}?.data as? ModuleData ?: continue
val sourceSetModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
rootModel.addModuleOrderEntry(sourceSetModule)
}
}
}
}
private fun addSourceRoot(
sourceRoot: File,
type: JpsModuleSourceRootType<*>,
rootModel: ModifiableRootModel
) {
val parent = findParentContentEntry(sourceRoot, rootModel.contentEntries.stream()) ?: return
val url = FilePaths.pathToIdeaUrl(sourceRoot)
parent.addSourceFolder(url, type)
}
}
@@ -0,0 +1,72 @@
/*
* 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.tools.idea.gradle.project.model.AndroidModuleModel
import com.android.tools.idea.gradle.util.ContentEntries.findParentContentEntry
import com.android.tools.idea.io.FilePaths
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.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.util.containers.stream
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.gradle.KotlinCompilation
import java.io.File
class KotlinAndroidGradleMPPModuleDataService : AbstractProjectDataService<ModuleData, Void>() {
override fun getTargetDataKey() = ProjectKeys.MODULE
override fun postProcess(
toImport: MutableCollection<DataNode<ModuleData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val projectNode = ExternalSystemApiUtil.findParent(nodeToImport, ProjectKeys.PROJECT) ?: continue
val moduleData = nodeToImport.data
val module = modelsProvider.findIdeModule(moduleData) ?: continue
val androidModel = AndroidModuleModel.get(module) ?: continue
val variantName = androidModel.selectedVariant.name
val sourceSetInfo = nodeToImport.kotlinAndroidSourceSets?.firstOrNull { it.kotlinModule.name == variantName } ?: continue
val compilation = sourceSetInfo.kotlinModule as? KotlinCompilation ?: continue
val rootModel = modelsProvider.getModifiableRootModel(module)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.isAndroid) {
val sourceType = if (sourceSet.isTestModule) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
val resourceType = if (sourceSet.isTestModule) JavaResourceRootType.TEST_RESOURCE else JavaResourceRootType.RESOURCE
sourceSet.sourceDirs.forEach { addSourceRoot(it, sourceType, rootModel) }
sourceSet.resourceDirs.forEach { addSourceRoot(it, resourceType, rootModel) }
} else {
val sourceSetId = sourceSetInfo.sourceSetIdsByName[sourceSet.name] ?: continue
val sourceSetData = ExternalSystemApiUtil.findFirstRecursively(projectNode) {
(it.data as? ModuleData)?.id == sourceSetId
}?.data as? ModuleData ?: continue
val sourceSetModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
rootModel.addModuleOrderEntry(sourceSetModule)
}
}
}
}
private fun addSourceRoot(
sourceRoot: File,
type: JpsModuleSourceRootType<*>,
rootModel: ModifiableRootModel
) {
val parent = findParentContentEntry(sourceRoot, rootModel.contentEntries.stream()) ?: return
val url = FilePaths.pathToIdeaUrl(sourceRoot)
parent.addSourceFolder(url, type)
}
}
@@ -0,0 +1,81 @@
/*
* 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.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.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.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.kotlin.idea.configuration.KotlinSourceSetInfo
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
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)
}
}
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.isAndroid }
.map { KotlinMPPGradleProjectResolver.createSourceSetInfo(it, gradleModule, resolverCtx) }
.toList()
}
}
@@ -30,6 +30,7 @@ import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
import org.jetbrains.kotlin.gradle.KotlinGradleModel
import org.jetbrains.kotlin.gradle.KotlinGradleModelBuilder
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel
import org.jetbrains.kotlin.idea.inspections.gradle.getDependencyModules
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataProperty
@@ -78,6 +79,10 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
ideModule: DataNode<ModuleData>,
ideProject: DataNode<ProjectData>
) {
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) != null) {
return super.populateModuleDependencies(gradleModule, ideModule, ideProject)
}
val outputToSourceSet = ideProject.getUserData(GradleProjectResolver.MODULES_OUTPUTS)
val sourceSetByName = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS)
@@ -200,6 +200,7 @@ fun configureFacetByGradleModule(
sourceSetNode: DataNode<GradleSourceSetData>?,
sourceSetName: String? = sourceSetNode?.data?.id?.let { it.substring(it.lastIndexOf(':') + 1) }
): KotlinFacet? {
if (moduleNode.kotlinSourceSet != null) return null // Suppress in the presence of new MPP model
if (!moduleNode.isResolved) return null
if (!moduleNode.hasKotlinPlugin) {
@@ -263,7 +264,7 @@ private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind
return K2JSCompilerArguments().apply { parseCommandLineArguments(k2jsArgumentList, this) }.outputFile
}
private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List<String>) {
internal fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List<String>) {
if (dependencyClasspath.isEmpty()) return
val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return
val fullClasspath = arguments.classpath?.split(File.pathSeparator) ?: emptyList()
@@ -274,7 +275,7 @@ private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List<
private val gradlePropertyFiles = listOf("local.properties", "gradle.properties")
private fun findKotlinCoroutinesProperty(project: Project): String {
internal fun findKotlinCoroutinesProperty(project: Project): String {
for (propertyFileName in gradlePropertyFiles) {
val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue
val properties = Properties()
@@ -0,0 +1,45 @@
/*
* 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.idea.configuration
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.AbstractNamedData
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.gradle.KotlinModule
import org.jetbrains.kotlin.gradle.KotlinPlatform
import org.jetbrains.kotlin.gradle.KotlinSourceSet
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
import com.intellij.openapi.externalSystem.model.Key as ExternalKey
var DataNode<out ModuleData>.kotlinSourceSet: KotlinSourceSetInfo?
by CopyableDataNodeUserDataProperty(Key.create("KOTLIN_SOURCE_SET"))
var DataNode<out ModuleData>.kotlinTargetDataNode: DataNode<KotlinTargetData>?
by CopyableDataNodeUserDataProperty(Key.create("KOTLIN_TARGET_DATA_NODE"))
class KotlinSourceSetInfo(val kotlinModule: KotlinModule) {
var moduleId: String? = null
var platform: KotlinPlatform = KotlinPlatform.COMMON
var defaultCompilerArguments: CommonCompilerArguments? = null
var compilerArguments: CommonCompilerArguments? = null
var dependencyClasspath: List<String> = emptyList()
var isTestModule: Boolean = false
var sourceSetIdsByName: Map<String, String> = emptyMap()
}
class KotlinTargetData(name: String) : AbstractNamedData(GradleConstants.SYSTEM_ID, name) {
var moduleIds: Set<String> = emptySet()
var archiveFile: File? = null
companion object {
val KEY = ExternalKey.create(KotlinTargetData::class.java, ProjectKeys.MODULE.processingWeight + 1)
}
}
@@ -0,0 +1,610 @@
/*
* 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.idea.configuration
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.*
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.roots.DependencyScope
import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.PathUtilRt
import com.intellij.util.SmartList
import com.intellij.util.containers.MultiMap
import com.intellij.util.text.VersionComparatorUtil
import org.gradle.tooling.model.UnsupportedMethodException
import org.gradle.tooling.model.idea.IdeaContentRoot
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.cli.common.arguments.*
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolver
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.CONFIGURATION_ARTIFACTS
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.MODULES_OUTPUTS
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.buildDependencies
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.getModuleId
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
@Order(ExternalSystemConstants.UNORDERED + 1)
open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
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, resolverCtx)
}
}
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) == null) {
return super.populateModuleContentRoots(gradleModule, ideModule)
}
populateContentRoots(gradleModule, ideModule, resolverCtx)
}
override fun populateModuleCompileOutputSettings(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) == null) {
super.populateModuleCompileOutputSettings(gradleModule, ideModule)
}
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return
val ideaOutDir = File(ideModule.data.linkedExternalProjectPath, "out")
val projectDataNode = ideModule.getDataNode(ProjectKeys.PROJECT)!!
val moduleOutputsMap = projectDataNode.getUserData(MODULES_OUTPUTS)!!
val outputDirs = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
var gradleOutputMap = dataNode.getUserData(GradleProjectResolver.GRADLE_OUTPUTS)
if (gradleOutputMap == null) {
gradleOutputMap = MultiMap.create()
dataNode.putUserData(GradleProjectResolver.GRADLE_OUTPUTS, gradleOutputMap)
}
val moduleData = dataNode.data
with(compilation.output) {
effectiveClassesDir?.let {
moduleData.isInheritProjectCompileOutputPath = false
moduleData.setCompileOutputPath(compilation.sourceType, it.absolutePath)
for (gradleOutputDir in classesDirs) {
recordOutputDir(gradleOutputDir, it, compilation.sourceType, moduleData, moduleOutputsMap, gradleOutputMap)
}
}
resourcesDir?.let {
moduleData.setCompileOutputPath(compilation.resourceType, it.absolutePath)
recordOutputDir(it, it, compilation.resourceType, moduleData, moduleOutputsMap, gradleOutputMap)
}
}
}
if (outputDirs.any { FileUtil.isAncestor(ideaOutDir, File(it), false) }) {
excludeOutDir(ideModule, ideaOutDir)
}
}
override fun populateModuleDependencies(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>, ideProject: DataNode<ProjectData>) {
if (resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) == null) {
super.populateModuleDependencies(gradleModule, ideModule, ideProject)
}
populateModuleDependencies(gradleModule, ideProject, ideModule, resolverCtx)
}
private fun recordOutputDir(
gradleOutputDir: File,
effectiveOutputDir: File,
sourceType: ExternalSystemSourceType,
moduleData: GradleSourceSetData,
moduleOutputsMap: MutableMap<String, Pair<String, ExternalSystemSourceType>>,
gradleOutputMap: MultiMap<ExternalSystemSourceType, String>
) {
val gradleOutputPath = ExternalSystemApiUtil.toCanonicalPath(gradleOutputDir.absolutePath)
gradleOutputMap.putValue(sourceType, gradleOutputPath)
if (gradleOutputDir.path != effectiveOutputDir.path) {
moduleOutputsMap[gradleOutputPath] = Pair(moduleData.id, sourceType)
}
}
private fun excludeOutDir(ideModule: DataNode<ModuleData>, ideaOutDir: File) {
val contentRootDataDataNode = ExternalSystemApiUtil.find(ideModule, ProjectKeys.CONTENT_ROOT)
val excludedContentRootData: ContentRootData
if (contentRootDataDataNode == null || !FileUtil.isAncestor(File(contentRootDataDataNode.data.rootPath), ideaOutDir, false)) {
excludedContentRootData = ContentRootData(GradleConstants.SYSTEM_ID, ideaOutDir.absolutePath)
ideModule.createChild(ProjectKeys.CONTENT_ROOT, excludedContentRootData)
} else {
excludedContentRootData = contentRootDataDataNode.data
}
excludedContentRootData.storePath(ExternalSystemSourceType.EXCLUDED, ideaOutDir.absolutePath)
}
companion object {
fun initializeModuleData(
gradleModule: IdeaModule,
mainModuleNode: DataNode<ModuleData>,
projectDataNode: DataNode<ProjectData>,
resolverCtx: ProjectResolverContext
) {
val mainModuleData = mainModuleNode.data
val mainModuleConfigPath = mainModuleData.linkedExternalProjectPath
val mainModuleFileDirectoryPath = mainModuleData.moduleFileDirectoryPath
val externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java)
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java)
if (mppModel == null || externalProject == null) return
val jdkName = gradleModule.jdkNameIfAny
val moduleGroup: Array<String>? = if (!resolverCtx.isUseQualifiedModuleNames) {
val gradlePath = gradleModule.gradleProject.path
val isRootModule = gradlePath.isEmpty() || gradlePath == ":"
if (isRootModule) {
arrayOf(mainModuleData.internalName)
} else {
gradlePath.split(":").drop(1).toTypedArray()
}
} else null
val sourceSetMap = projectDataNode.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS)!!
val sourceSetToCompilationData = LinkedHashMap<KotlinSourceSet, MutableSet<GradleSourceSetData>>()
for (target in mppModel.targets) {
if (target.isAndroid) continue
val targetData = KotlinTargetData(target.name).also {
it.archiveFile = target.jar?.archiveFile
}
val targetDataNode = mainModuleNode.createChild<KotlinTargetData>(KotlinTargetData.KEY, targetData)
val compilationIds = LinkedHashSet<String>()
for (compilation in target.compilations) {
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
if (existingSourceSetDataNode?.kotlinSourceSet != null) continue
compilationIds += moduleId
val moduleExternalName = getExternalModuleName(gradleModule, compilation)
val moduleInternalName = getInternalModuleName(gradleModule, externalProject, compilation, resolverCtx)
val compilationData = existingSourceSetDataNode?.data ?: GradleSourceSetData(
moduleId, moduleExternalName, moduleInternalName, mainModuleFileDirectoryPath, mainModuleConfigPath
).also {
it.group = externalProject.group
it.version = externalProject.version
when (compilation.name) {
KotlinCompilation.MAIN_COMPILATION_NAME -> {
it.publication = ProjectId(externalProject.group, externalProject.name, externalProject.version)
}
KotlinCompilation.TEST_COMPILATION_NAME -> {
it.productionModuleId = getInternalModuleName(
gradleModule,
externalProject,
compilation,
resolverCtx,
KotlinCompilation.MAIN_COMPILATION_NAME
)
}
}
it.ideModuleGroup = moduleGroup
it.sdkName = jdkName
}
val kotlinSourceSet = createSourceSetInfo(compilation, gradleModule, resolverCtx)
if (compilation.platform == KotlinPlatform.JVM) {
compilationData.targetCompatibility = (kotlinSourceSet.compilerArguments as? K2JVMCompilerArguments)?.jvmTarget
}
for (sourceSet in compilation.sourceSets) {
sourceSetToCompilationData.getOrPut(sourceSet) { LinkedHashSet() } += compilationData
}
val compilationDataNode =
(existingSourceSetDataNode ?: mainModuleNode.createChild(GradleSourceSetData.KEY, compilationData)).also {
it.kotlinSourceSet = kotlinSourceSet
it.kotlinTargetDataNode = targetDataNode
}
if (existingSourceSetDataNode == null) {
sourceSetMap[moduleId] = Pair(compilationDataNode, createExternalSourceSet(compilation, compilationData))
}
}
targetData.moduleIds = compilationIds
}
for (sourceSet in mppModel.sourceSets.values) {
if (sourceSet.isAndroid) continue
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
if (existingSourceSetDataNode?.kotlinSourceSet != null) continue
val moduleExternalName = getExternalModuleName(gradleModule, sourceSet)
val moduleInternalName = getInternalModuleName(gradleModule, externalProject, sourceSet, resolverCtx)
val sourceSetData = existingSourceSetDataNode?.data ?: GradleSourceSetData(
moduleId, moduleExternalName, moduleInternalName, mainModuleFileDirectoryPath, mainModuleConfigPath
).also {
it.group = externalProject.group
it.version = externalProject.version
val name = sourceSet.name
val baseName = name.removeSuffix("Test")
if (baseName != name) {
it.productionModuleId = getInternalModuleName(
gradleModule,
externalProject,
sourceSet,
resolverCtx,
baseName + "Main"
)
}
it.ideModuleGroup = moduleGroup
it.sdkName = jdkName
it.targetCompatibility = sourceSetToCompilationData[sourceSet]
?.mapNotNull { it.targetCompatibility }
?.minWith(VersionComparatorUtil.COMPARATOR)
}
val kotlinSourceSet = createSourceSetInfo(sourceSet, gradleModule, resolverCtx)
val sourceSetDataNode =
(existingSourceSetDataNode ?: mainModuleNode.createChild(GradleSourceSetData.KEY, sourceSetData)).also {
it.kotlinSourceSet = kotlinSourceSet
}
if (existingSourceSetDataNode == null) {
sourceSetMap[moduleId] = Pair(sourceSetDataNode, createExternalSourceSet(sourceSet, sourceSetData))
}
}
with(projectDataNode.data) {
if (mainModuleData.linkedExternalProjectPath == linkedExternalProjectPath) {
group = mainModuleData.group
version = mainModuleData.version
}
}
mainModuleNode.coroutines = mppModel.extraFeatures.coroutinesState
}
fun populateContentRoots(
gradleModule: IdeaModule,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext
) {
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return
processSourceSets(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, sourceSet ->
createContentRootData(sourceSet.sourceDirs, sourceSet.sourceType, dataNode)
createContentRootData(sourceSet.resourceDirs, sourceSet.resourceType, dataNode)
}
for (gradleContentRoot in gradleModule.contentRoots ?: emptySet<IdeaContentRoot?>()) {
if (gradleContentRoot == null) continue
val rootDirectory = gradleContentRoot.rootDirectory ?: continue
val ideContentRoot = ContentRootData(GradleConstants.SYSTEM_ID, rootDirectory.absolutePath).also { ideContentRoot ->
(gradleContentRoot.excludeDirectories ?: emptySet()).forEach { file ->
ideContentRoot.storePath(ExternalSystemSourceType.EXCLUDED, file.absolutePath)
}
}
ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot)
}
}
fun populateModuleDependencies(
gradleModule: IdeaModule,
ideProject: DataNode<ProjectData>,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext
) {
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return
val sourceSetMap = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS) ?: return
val artifactsMap = ideProject.getUserData(CONFIGURATION_ARTIFACTS) ?: return
val processedModuleIds = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
if (processedModuleIds.add(getKotlinModuleId(gradleModule, compilation, resolverCtx))) {
buildDependencies(
resolverCtx,
sourceSetMap,
artifactsMap,
dataNode,
preprocessDependencies(compilation, ideProject),
ideProject
)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.fullName() == compilation.fullName()) continue
addDependency(dataNode, sourceSet, gradleModule, ideModule, resolverCtx)
}
}
}
processSourceSets(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, sourceSet ->
dataNode.data.productionModuleId?.let {
val productionModuleDataNode = ideModule.findChildModuleByInternalName(it)
if (productionModuleDataNode != null) {
addDependency(dataNode, productionModuleDataNode)
}
}
if (processedModuleIds.add(getKotlinModuleId(gradleModule, sourceSet, resolverCtx))) {
buildDependencies(
resolverCtx,
sourceSetMap,
artifactsMap,
dataNode,
preprocessDependencies(sourceSet, ideProject),
ideProject
)
}
for (targetSourceSetName in sourceSet.dependsOnSourceSets) {
val targetSourceSet = mppModel.sourceSets[targetSourceSetName] ?: continue
addDependency(dataNode, targetSourceSet, gradleModule, ideModule, resolverCtx)
}
}
}
private fun preprocessDependencies(
kotlinModule: KotlinModule,
ideProject: DataNode<ProjectData>
): List<ExternalDependency> {
return kotlinModule.dependencies
.groupBy { it.id }
.mapValues { it.value.firstOrNull { it.scope == "COMPILE" } ?: it.value.lastOrNull() }
.values
.filterNotNull()
.map { adjustDependency(it, ideProject) }
}
private fun adjustDependency(
dependency: ExternalDependency,
ideProject: DataNode<ProjectData>
): ExternalDependency {
if (dependency !is ExternalProjectDependency) return dependency
val projectPath = dependency.projectPath
val classifier = dependency.classifier ?: return dependency
val targetModuleNode = ExternalSystemApiUtil.findFirstRecursively(ideProject) {
(it.data as? ModuleData)?.id == projectPath
} ?: return dependency
val targetCompilation = ExternalSystemApiUtil
.getChildren(targetModuleNode, GradleSourceSetData.KEY)
.mapNotNull { sourceSetNode ->
sourceSetNode.kotlinSourceSet?.kotlinModule as? KotlinCompilation
}
.firstOrNull {
it.name == KotlinCompilation.MAIN_COMPILATION_NAME && it.target.disambiguationClassifier == classifier
} ?: return dependency
return DefaultExternalProjectDependency(dependency).also {
it.configurationName = targetCompilation.fullName()
}
}
private fun addDependency(fromModule: DataNode<*>, toModule: DataNode<*>) {
val fromData = fromModule.data as? ModuleData ?: return
val toData = toModule.data as? ModuleData ?: return
val moduleDependencyData = ModuleDependencyData(fromData, toData).also {
it.scope = DependencyScope.COMPILE
it.isExported = false
}
fromModule.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData)
}
private fun addDependency(
fromModule: DataNode<*>,
toModule: KotlinModule,
gradleModule: IdeaModule,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext
) {
val usedModuleId = getKotlinModuleId(gradleModule, toModule, resolverCtx)
val usedModuleDataNode = ideModule.findChildModuleById(usedModuleId) ?: return
addDependency(fromModule, usedModuleDataNode)
}
private fun createContentRootData(sourceDirs: Set<File>, sourceType: ExternalSystemSourceType, parentNode: DataNode<*>) {
for (sourceDir in sourceDirs) {
val contentRootData = ContentRootData(GradleConstants.SYSTEM_ID, sourceDir.absolutePath)
contentRootData.storePath(sourceType, sourceDir.absolutePath)
parentNode.createChild(ProjectKeys.CONTENT_ROOT, contentRootData)
}
}
private fun processSourceSets(
gradleModule: IdeaModule,
mppModel: KotlinMPPGradleModel,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext,
processor: (DataNode<GradleSourceSetData>, KotlinSourceSet) -> Unit
) {
val sourceSetsMap = HashMap<String, DataNode<GradleSourceSetData>>()
for (dataNode in ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)) {
if (dataNode.kotlinSourceSet != null) {
sourceSetsMap[dataNode.data.id] = dataNode
}
}
for (sourceSet in mppModel.sourceSets.values) {
if (sourceSet.isAndroid) continue
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
processor(moduleDataNode, sourceSet)
}
}
private fun processCompilations(
gradleModule: IdeaModule,
mppModel: KotlinMPPGradleModel,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext,
processor: (DataNode<GradleSourceSetData>, KotlinCompilation) -> Unit
) {
val sourceSetsMap = HashMap<String, DataNode<GradleSourceSetData>>()
for (dataNode in ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)) {
if (dataNode.kotlinSourceSet != null) {
sourceSetsMap[dataNode.data.id] = dataNode
}
}
for (target in mppModel.targets) {
if (target.isAndroid) continue
for (compilation in target.compilations) {
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
processor(moduleDataNode, compilation)
}
}
}
private val IdeaModule.jdkNameIfAny
get() = try {
jdkName
} catch (e: UnsupportedMethodException) {
null
}
private fun getExternalModuleName(gradleModule: IdeaModule, kotlinModule: KotlinModule) =
gradleModule.name + ":" + kotlinModule.fullName()
private fun getInternalModuleName(
gradleModule: IdeaModule,
externalProject: ExternalProject,
kotlinModule: KotlinModule,
resolverCtx: ProjectResolverContext,
actualName: String = kotlinModule.name
): String {
val delimiter: String
val moduleName = StringBuilder()
if (resolverCtx.isUseQualifiedModuleNames) {
delimiter = "."
if (StringUtil.isNotEmpty(externalProject.group)) {
moduleName.append(externalProject.group).append(delimiter)
}
moduleName.append(externalProject.name)
} else {
delimiter = "_"
moduleName.append(gradleModule.name)
}
moduleName.append(delimiter)
moduleName.append(kotlinModule.fullName(actualName))
return PathUtilRt.suggestFileName(moduleName.toString(), true, false)
}
private fun createExternalSourceSet(compilation: KotlinCompilation, compilationData: GradleSourceSetData): ExternalSourceSet {
return DefaultExternalSourceSet().also { sourceSet ->
val effectiveClassesDir = compilation.output.effectiveClassesDir
val resourcesDir = compilation.output.resourcesDir
sourceSet.name = compilation.fullName()
sourceSet.targetCompatibility = compilationData.targetCompatibility
sourceSet.dependencies += compilation.dependencies
val sourcesWithTypes = SmartList<kotlin.Pair<IExternalSystemSourceType, ExternalSourceDirectorySet>>()
if (effectiveClassesDir != null) {
sourcesWithTypes += compilation.sourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.outputDir = effectiveClassesDir
dirSet.srcDirs = compilation.sourceSets.flatMapTo(LinkedHashSet()) { it.sourceDirs }
dirSet.gradleOutputDirs += compilation.output.classesDirs
dirSet.setInheritedCompilerOutput(false)
}
}
if (resourcesDir != null) {
sourcesWithTypes += compilation.resourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.outputDir = resourcesDir
dirSet.srcDirs = compilation.sourceSets.flatMapTo(LinkedHashSet()) { it.resourceDirs }
dirSet.gradleOutputDirs += resourcesDir
dirSet.setInheritedCompilerOutput(false)
}
}
sourceSet.sources = sourcesWithTypes.toMap()
}
}
private fun createExternalSourceSet(ktSourceSet: KotlinSourceSet, ktSourceSetData: GradleSourceSetData): ExternalSourceSet {
return DefaultExternalSourceSet().also { sourceSet ->
sourceSet.name = ktSourceSet.name
sourceSet.targetCompatibility = ktSourceSetData.targetCompatibility
sourceSet.dependencies += ktSourceSet.dependencies
sourceSet.sources = linkedMapOf<IExternalSystemSourceType, ExternalSourceDirectorySet>(
ktSourceSet.sourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.srcDirs = ktSourceSet.sourceDirs
},
ktSourceSet.resourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.srcDirs = ktSourceSet.resourceDirs
}
)
}
}
private val KotlinModule.sourceType
get() = if (isTestModule) ExternalSystemSourceType.TEST else ExternalSystemSourceType.SOURCE
private val KotlinModule.resourceType
get() = if (isTestModule) ExternalSystemSourceType.TEST_RESOURCE else ExternalSystemSourceType.RESOURCE
private fun createSourceSetInfo(
sourceSet: KotlinSourceSet,
gradleModule: IdeaModule,
resolverCtx: ProjectResolverContext
): KotlinSourceSetInfo {
return KotlinSourceSetInfo(sourceSet).also {
it.moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
it.platform = sourceSet.platform
it.isTestModule = sourceSet.isTestModule
it.compilerArguments = createCompilerArguments(emptyList(), sourceSet.platform).also {
it.multiPlatform = true
}
}
}
fun createSourceSetInfo(
compilation: KotlinCompilation,
gradleModule: IdeaModule,
resolverCtx: ProjectResolverContext
): KotlinSourceSetInfo {
return KotlinSourceSetInfo(compilation).also {
it.moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
it.platform = compilation.platform
it.isTestModule = compilation.isTestModule
it.compilerArguments = createCompilerArguments(compilation.arguments.currentArguments, compilation.platform).also {
it.multiPlatform = true
}
it.dependencyClasspath = compilation.dependencyClasspath
it.defaultCompilerArguments = createCompilerArguments(compilation.arguments.defaultArguments, compilation.platform)
it.sourceSetIdsByName = compilation.sourceSets
.asSequence()
.filter { it.fullName() != compilation.fullName() }
.map { it.name to getKotlinModuleId(gradleModule, it, resolverCtx) }
.toMap()
}
}
private fun createCompilerArguments(args: List<String>, platform: KotlinPlatform): CommonCompilerArguments {
return when (platform) {
KotlinPlatform.COMMON -> K2MetadataCompilerArguments()
KotlinPlatform.JVM -> K2JVMCompilerArguments()
KotlinPlatform.JS -> K2JSCompilerArguments()
}.also {
parseCommandLineArguments(args, it)
}
}
private fun KotlinModule.fullName(simpleName: String = name) = when (this) {
is KotlinCompilation -> target.disambiguationClassifier?.let { it + simpleName.capitalize() } ?: simpleName
else -> simpleName
}
private fun getKotlinModuleId(gradleModule: IdeaModule, kotlinModule: KotlinModule, resolverCtx: ProjectResolverContext) =
getModuleId(resolverCtx, gradleModule) + ":" + kotlinModule.fullName()
}
}
@@ -0,0 +1,163 @@
/*
* 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.idea.configuration
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.config.CoroutineSupport
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.KotlinModuleKind
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.gradle.KotlinCompilation
import org.jetbrains.kotlin.gradle.KotlinModule
import org.jetbrains.kotlin.gradle.KotlinPlatform
import org.jetbrains.kotlin.gradle.KotlinSourceSet
import org.jetbrains.kotlin.idea.facet.applyCompilerArgumentsToFacet
import org.jetbrains.kotlin.idea.facet.configureFacet
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
import org.jetbrains.kotlin.idea.facet.noVersionAutoAdvance
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.framework.effectiveKind
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
class KotlinSourceSetDataService : AbstractProjectDataService<GradleSourceSetData, Void>() {
override fun getTargetDataKey() = GradleSourceSetData.KEY
override fun postProcess(
toImport: MutableCollection<DataNode<GradleSourceSetData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val mainModuleData = ExternalSystemApiUtil.findParent(
nodeToImport,
ProjectKeys.MODULE
) ?: continue
val sourceSetData = nodeToImport.data
val kotlinSourceSet = nodeToImport.kotlinSourceSet ?: continue
val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
val platform = kotlinSourceSet.platform
val rootModel = modelsProvider.getModifiableRootModel(ideModule)
dropWrongLibraries(rootModel, platform, project)
if (platform != KotlinPlatform.JVM) {
migrateNonJvmSourceFolders(rootModel)
}
configureFacet(sourceSetData, kotlinSourceSet, mainModuleData, ideModule, modelsProvider)
}
}
private val KotlinModule.kind
get() = when (this) {
is KotlinCompilation -> KotlinModuleKind.COMPILATION_AND_SOURCE_SET_HOLDER
is KotlinSourceSet -> KotlinModuleKind.SOURCE_SET_HOLDER
else -> KotlinModuleKind.DEFAULT
}
private fun configureFacet(
sourceSetData: GradleSourceSetData,
kotlinSourceSet: KotlinSourceSetInfo,
mainModuleNode: DataNode<ModuleData>,
ideModule: Module,
modelsProvider: IdeModifiableModelsProvider
) {
val compilerVersion = mainModuleNode
.findAll(BuildScriptClasspathData.KEY)
.firstOrNull()
?.data
?.let { findKotlinPluginVersion(it) } ?: return
val platformKind = when (kotlinSourceSet.platform) {
KotlinPlatform.JVM -> TargetPlatformKind.Jvm[JvmTarget.fromString(
sourceSetData.targetCompatibility ?: ""
) ?: JvmTarget.DEFAULT]
KotlinPlatform.JS -> TargetPlatformKind.JavaScript
KotlinPlatform.COMMON -> TargetPlatformKind.Common
}
val coroutinesProperty = CoroutineSupport.byCompilerArgument(
mainModuleNode.coroutines ?: findKotlinCoroutinesProperty(ideModule.project)
)
val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
val compilerArguments = kotlinSourceSet.compilerArguments
val defaultCompilerArguments = kotlinSourceSet.defaultCompilerArguments
if (compilerArguments != null) {
applyCompilerArgumentsToFacet(
compilerArguments,
defaultCompilerArguments,
kotlinFacet,
modelsProvider
)
}
adjustClasspath(kotlinFacet, kotlinSourceSet.dependencyClasspath)
kotlinFacet.noVersionAutoAdvance()
with(kotlinFacet.configuration.settings) {
kind = kotlinSourceSet.kotlinModule.kind
sourceSetNames = kotlinSourceSet.sourceSetIdsByName.values.mapNotNull { sourceSetId ->
val node = mainModuleNode.findChildModuleById(sourceSetId) ?: return@mapNotNull null
val data = node.data as? ModuleData ?: return@mapNotNull null
modelsProvider.findIdeModule(data)?.name
}
if (kotlinSourceSet.isTestModule) {
testOutputPath = (kotlinSourceSet.compilerArguments as? K2JSCompilerArguments)?.outputFile
productionOutputPath = null
} else {
productionOutputPath = (kotlinSourceSet.compilerArguments as? K2JSCompilerArguments)?.outputFile
testOutputPath = null
}
}
}
private fun dropWrongLibraries(
rootModel: ModifiableRootModel,
platform: KotlinPlatform,
project: Project
) {
rootModel.orderEntries().librariesOnly().forEach { orderEntry ->
val library = (orderEntry as? LibraryOrderEntry)?.library
if (library != null && !library.matchesPlatform(platform, project)) {
rootModel.removeOrderEntry(orderEntry)
}
true
}
}
private fun Library.matchesPlatform(platform: KotlinPlatform, project: Project): Boolean {
val kind = (this as? LibraryEx)?.effectiveKind(project)
return when (kind) {
CommonLibraryKind -> true
JSLibraryKind -> platform == KotlinPlatform.JS
else -> platform == KotlinPlatform.JVM
}
}
}
@@ -0,0 +1,46 @@
/*
* 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.idea.configuration
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.packaging.impl.artifacts.JarArtifactType
import com.intellij.packaging.impl.elements.ProductionModuleOutputPackagingElement
import org.jetbrains.kotlin.idea.util.createPointer
class KotlinTargetDataService : AbstractProjectDataService<KotlinTargetData, Void>() {
override fun getTargetDataKey() = KotlinTargetData.KEY
override fun importData(
toImport: MutableCollection<DataNode<KotlinTargetData>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
for (nodeToImport in toImport) {
val targetData = nodeToImport.data
val archiveFile = targetData.archiveFile ?: continue
val artifactModel = modelsProvider.modifiableArtifactModel
val artifactName = FileUtil.getNameWithoutExtension(archiveFile)
artifactModel.findArtifact(artifactName)?.let { artifactModel.removeArtifact(it) }
artifactModel.addArtifact(artifactName, JarArtifactType.getInstance()).also {
it.outputPath = archiveFile.parent
for (moduleId in targetData.moduleIds) {
val compilationModuleDataNode = nodeToImport.parent?.findChildModuleById(moduleId) ?: continue
val compilationData = compilationModuleDataNode.data ?: continue
val kotlinSourceSet = compilationModuleDataNode.kotlinSourceSet ?: continue
if (kotlinSourceSet.isTestModule) continue
val moduleToPackage = modelsProvider.findIdeModule(compilationData) ?: continue
it.rootElement.addOrFindChild(ProductionModuleOutputPackagingElement(project, moduleToPackage.createPointer()))
}
}
}
}
}
@@ -0,0 +1,17 @@
/*
* 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.idea.configuration
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ModuleData
@Suppress("UNCHECKED_CAST")
fun DataNode<*>.findChildModuleById(id: String) =
children.firstOrNull { (it.data as? ModuleData)?.id == id } as? DataNode<out ModuleData>
@Suppress("UNCHECKED_CAST")
fun DataNode<*>.findChildModuleByInternalName(name: String) =
children.firstOrNull { (it.data as? ModuleData)?.internalName == name }
@@ -112,6 +112,12 @@ var CommonCompilerArguments.apiVersionView: VersionView
autoAdvanceApiVersion = value == VersionView.LatestStable
}
enum class KotlinModuleKind {
DEFAULT,
SOURCE_SET_HOLDER,
COMPILATION_AND_SOURCE_SET_HOLDER
}
class KotlinFacetSettings {
companion object {
// Increment this when making serialization-incompatible changes to configuration data
@@ -196,6 +202,9 @@ class KotlinFacetSettings {
var productionOutputPath: String? = null
var testOutputPath: String? = null
var kind: KotlinModuleKind = KotlinModuleKind.DEFAULT
var sourceSetNames: List<String> = emptyList()
}
fun TargetPlatformKind<*>.createCompilerArguments(init: CommonCompilerArguments.() -> Unit = {}): CommonCompilerArguments {
@@ -111,6 +111,20 @@ private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings {
listOfNotNull((it.content.firstOrNull() as? Text)?.textTrim)
}
}
element.getChild("sourceSets")?.let {
val items = it.getChildren("sourceSet")
sourceSetNames = items.mapNotNull { (it.content.firstOrNull() as? Text)?.textTrim }
}
kind = element.getChild("newMppModelJpsModuleKind")?.let {
val kindName = (it.content.firstOrNull() as? Text)?.textTrim
if (kindName != null) {
try {
KotlinModuleKind.valueOf(kindName)
} catch (e: Exception) {
null
}
} else null
} ?: KotlinModuleKind.DEFAULT
element.getChild("compilerSettings")?.let {
compilerSettings = CompilerSettings()
XmlSerializer.deserializeInto(compilerSettings!!, it)
@@ -259,6 +273,16 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
}
)
}
if (sourceSetNames.isNotEmpty()) {
element.addContent(
Element("sourceSets").apply {
sourceSetNames.map { addContent(Element("sourceSet").apply { addContent(it) }) }
}
)
}
if (kind != KotlinModuleKind.DEFAULT) {
element.addContent(Element("newMppModelJpsModuleKind").apply { addContent(kind.name) })
}
productionOutputPath?.let {
if (it != (compilerArguments as? K2JSCompilerArguments)?.outputFile) {
element.addContent(Element("productionOutputPath").apply { addContent(PathUtil.toSystemIndependentName(it)) })
@@ -0,0 +1,83 @@
/*
* 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.gradle
import org.jetbrains.plugins.gradle.model.ExternalDependency
import java.io.File
import java.io.Serializable
typealias KotlinDependency = ExternalDependency
interface KotlinModule : Serializable {
val isAndroid: Boolean
val name: String
val platform: KotlinPlatform
val dependencies: Set<KotlinDependency>
val isTestModule: Boolean
}
interface KotlinSourceSet : KotlinModule {
val sourceDirs: Set<File>
val resourceDirs: Set<File>
val dependsOnSourceSets: Set<String>
}
interface KotlinCompilationOutput : Serializable {
val classesDirs: Set<File>
val effectiveClassesDir: File?
val resourcesDir: File?
}
interface KotlinCompilationArguments : Serializable {
val defaultArguments: List<String>
val currentArguments: List<String>
}
interface KotlinCompilation : KotlinModule {
val sourceSets: Collection<KotlinSourceSet>
val target: KotlinTarget
val output: KotlinCompilationOutput
val arguments: KotlinCompilationArguments
val dependencyClasspath: List<String>
companion object {
const val MAIN_COMPILATION_NAME = "main"
const val TEST_COMPILATION_NAME = "test"
}
}
enum class KotlinPlatform(val id: String) {
COMMON("common"),
JVM("jvm"),
JS("js");
companion object {
fun byId(id: String) = values().firstOrNull { it.id == id }
}
}
interface KotlinTargetJar : Serializable {
val archiveFile: File?
}
interface KotlinTarget : Serializable {
val isAndroid: Boolean
val name: String
val disambiguationClassifier: String?
val platform: KotlinPlatform
val compilations: Collection<KotlinCompilation>
val jar: KotlinTargetJar?
}
interface ExtraFeatures : Serializable {
val coroutinesState: String?
}
interface KotlinMPPGradleModel : Serializable {
val sourceSets: Map<String, KotlinSourceSet>
val targets: Collection<KotlinTarget>
val extraFeatures: ExtraFeatures
}
@@ -0,0 +1,264 @@
/*
* 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.gradle
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.plugins.gradle.model.AbstractExternalDependency
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
import org.jetbrains.plugins.gradle.tooling.util.resolve.DependencyResolverImpl
import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder
import java.io.File
import java.lang.Exception
class KotlinMPPGradleModelBuilder : 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 {
return modelName == KotlinMPPGradleModel::class.java.name
}
override fun buildAll(modelName: String, project: Project): Any? {
val dependencyResolver = DependencyResolverImpl(
project,
false,
false,
true,
SourceSetCachedFinder(project)
)
val sourceSets = buildSourceSets(project) ?: return null
val sourceSetMap = sourceSets.map { it.name to it }.toMap()
val targets = buildTargets(sourceSetMap, dependencyResolver, project) ?: return null
computeSourceSetsDeferredInfo(sourceSets, targets)
val coroutinesState = getCoroutinesState(project)
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState))
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
val experimentalExt = getExperimental(kotlinExt) ?: return null
val getCoroutines = experimentalExt.javaClass.getMethodOrNull("getCoroutines") ?: return null
return getCoroutines(experimentalExt) as? String
}
private fun buildSourceSets(project: Project): Collection<KotlinSourceSetImpl>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getSourceSets = kotlinExt.javaClass.getMethodOrNull("getSourceSets") ?: return null
@Suppress("UNCHECKED_CAST")
val sourceSets =
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
return sourceSets.mapNotNull { buildSourceSet(it) }
}
private fun buildSourceSet(gradleSourceSet: Named): KotlinSourceSetImpl? {
val sourceSetClass = gradleSourceSet.javaClass
val getSourceDirSet = sourceSetClass.getMethodOrNull("getKotlin") ?: return null
val getResourceDirSet = sourceSetClass.getMethodOrNull("getResources") ?: return null
val getDependsOn = sourceSetClass.getMethodOrNull("getDependsOn") ?: return null
val sourceDirs = (getSourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
val resourceDirs = (getResourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
@Suppress("UNCHECKED_CAST")
val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
return KotlinSourceSetImpl(gradleSourceSet.name, sourceDirs, resourceDirs, dependsOnSourceSets)
}
private fun buildDependencies(
gradleCompilation: Any,
dependencyResolver: DependencyResolver,
configurationNameAccessor: String,
scope: String,
project: Project
): Collection<KotlinDependency> {
val gradleCompilationClass = gradleCompilation.javaClass
val getConfigurationName = gradleCompilationClass.getMethodOrNull(configurationNameAccessor) ?: return emptyList()
val configurationName = getConfigurationName(gradleCompilation) as? String ?: return emptyList()
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
if (!configuration.isCanBeResolved) return emptyList()
val artifactsByProjectPath = configuration
.resolvedConfiguration
.lenientConfiguration
.firstLevelModuleDependencies
.flatMap { it.moduleArtifacts }
.filter { it.id.componentIdentifier is ProjectComponentIdentifier }
.groupBy { (it.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
return dependencyResolver.resolveDependencies(configuration)
.apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
}
.map {
if (it !is ExternalProjectDependency || it.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map it
val artifacts = artifactsByProjectPath[it.projectPath] ?: return@map it
val classifier = artifacts.mapTo(LinkedHashSet()) { it.classifier }.singleOrNull() ?: return@map it
DefaultExternalProjectDependency(it).apply {
this.classifier = classifier
}
}
}
private fun buildTargets(
sourceSetMap: Map<String, KotlinSourceSet>,
dependencyResolver: DependencyResolver,
project: Project
): Collection<KotlinTarget>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
val targets = (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
return targets.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
}
private fun buildTarget(
gradleTarget: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
dependencyResolver: DependencyResolver,
project: Project
): KotlinTarget? {
val targetClass = gradleTarget.javaClass
val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null
val getCompilations = targetClass.getMethodOrNull("getCompilations") ?: return null
val getDisambiguationClassifier = targetClass.getMethodOrNull("getDisambiguationClassifier") ?: return null
val platformId = (getPlatformType.invoke(gradleTarget) as? Named)?.name ?: return null
val platform = KotlinPlatform.byId(platformId) ?: return null
val disambiguationClassifier = getDisambiguationClassifier(gradleTarget) as? String
val isAndroid = targetClass.name == KOTLIN_ANDROID_TARGET_CLASS_NAME
@Suppress("UNCHECKED_CAST")
val gradleCompilations =
(getCompilations.invoke(gradleTarget) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
val compilations = gradleCompilations.mapNotNull { buildCompilation(it, sourceSetMap, dependencyResolver, isAndroid, project) }
val jar = buildTargetJar(gradleTarget, project)
val target = KotlinTargetImpl(isAndroid, gradleTarget.name, disambiguationClassifier, platform, compilations, jar)
compilations.forEach { it.target = target }
return target
}
private fun buildTargetJar(gradleTarget: Named, project: Project): KotlinTargetJar? {
val targetClass = gradleTarget.javaClass
val getArtifactsTaskName = targetClass.getMethodOrNull("getArtifactsTaskName") ?: return null
val artifactsTaskName = getArtifactsTaskName(gradleTarget) as? String ?: return null
val jarTask = project.tasks.findByName(artifactsTaskName) ?: return null
val jarTaskClass = jarTask.javaClass
val getArchivePath = jarTaskClass.getMethodOrNull("getArchivePath")
val archiveFile = getArchivePath?.invoke(jarTask) as? File?
return KotlinTargetJarImpl(archiveFile)
}
private fun buildCompilation(
gradleCompilation: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
dependencyResolver: DependencyResolver,
isAndroid: Boolean,
project: Project
): KotlinCompilationImpl? {
val compilationClass = gradleCompilation.javaClass
val getKotlinSourceSets = compilationClass.getMethodOrNull("getKotlinSourceSets") ?: return null
@Suppress("UNCHECKED_CAST")
val kotlinGradleSourceSets = (getKotlinSourceSets(gradleCompilation) as? Collection<Named>) ?: return null
val kotlinSourceSets = kotlinGradleSourceSets.mapNotNull { sourceSetMap[it.name] }
val getCompileKotlinTaskName = compilationClass.getMethodOrNull("getCompileKotlinTaskName") ?: return null
@Suppress("UNCHECKED_CAST")
val compileKotlinTaskName = (getCompileKotlinTaskName(gradleCompilation) as? String) ?: return null
val compileKotlinTask = project.tasks.findByName(compileKotlinTaskName) ?: return null
val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null
val arguments = buildCompilationArguments(compileKotlinTask) ?: return null
val dependencyClasspath = buildDependencyClasspath(compileKotlinTask)
val dependencies = buildDependencies(gradleCompilation, dependencyResolver, project)
return KotlinCompilationImpl(isAndroid, gradleCompilation.name, kotlinSourceSets, dependencies, output, arguments, dependencyClasspath)
}
private fun buildDependencies(
gradleCompilation: Named,
dependencyResolver: DependencyResolver,
project: Project
): Set<KotlinDependency> {
return LinkedHashSet<KotlinDependency>().apply {
this += buildDependencies(
gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project
)
this += buildDependencies(
gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project
)
}
}
private fun buildCompilationArguments(compileKotlinTask: Task): KotlinCompilationArguments? {
val compileTaskClass = compileKotlinTask.javaClass
val getCurrentArguments = compileTaskClass.getMethodOrNull("getSerializedCompilerArguments") ?: return null
val getDefaultArguments = compileTaskClass.getMethodOrNull("getDefaultSerializedCompilerArguments") ?: return null
@Suppress("UNCHECKED_CAST")
val currentArguments = getCurrentArguments(compileKotlinTask) as? List<String> ?: return null
@Suppress("UNCHECKED_CAST")
val defaultArguments = getDefaultArguments(compileKotlinTask) as? List<String> ?: return null
return KotlinCompilationArgumentsImpl(defaultArguments, currentArguments)
}
private fun buildDependencyClasspath(compileKotlinTask: Task): List<String> {
val abstractKotlinCompileClass =
compileKotlinTask.javaClass.classLoader.loadClass(AbstractKotlinGradleModelBuilder.ABSTRACT_KOTLIN_COMPILE_CLASS)
val getCompileClasspath =
abstractKotlinCompileClass.getDeclaredMethodOrNull("getCompileClasspath") ?: return emptyList()
@Suppress("UNCHECKED_CAST")
return (getCompileClasspath(compileKotlinTask) as? Collection<File>)?.map { it.path } ?: emptyList()
}
private fun buildCompilationOutput(
gradleCompilation: Named,
compileKotlinTask: Task
): KotlinCompilationOutput? {
val compilationClass = gradleCompilation.javaClass
val getOutput = compilationClass.getMethodOrNull("getOutput") ?: return null
val gradleOutput = getOutput(gradleCompilation) ?: return null
val gradleOutputClass = gradleOutput.javaClass
val getClassesDirs = gradleOutputClass.getMethodOrNull("getClassesDirs") ?: return null
val getResourcesDir = gradleOutputClass.getMethodOrNull("getResourcesDir") ?: return null
val compileKotlinTaskClass = compileKotlinTask.javaClass
val getDestinationDir = compileKotlinTaskClass.getMethodOrNull("getDestinationDir") ?: return null
val classesDirs = getClassesDirs(gradleOutput) as? FileCollection ?: return null
val resourcesDir = getResourcesDir(gradleOutput) as? File ?: return null
val destinationDir = getDestinationDir(compileKotlinTask) as? File
return KotlinCompilationOutputImpl(classesDirs.files, destinationDir, resourcesDir)
}
private fun computeSourceSetsDeferredInfo(
sourceSets: Collection<KotlinSourceSetImpl>,
targets: Collection<KotlinTarget>
) {
val sourceSetToCompilations = LinkedHashMap<KotlinSourceSet, MutableSet<KotlinCompilation>>()
for (target in targets) {
for (compilation in target.compilations) {
for (sourceSet in compilation.sourceSets) {
sourceSetToCompilations.getOrPut(sourceSet) { LinkedHashSet() } += compilation
}
}
}
for (sourceSet in sourceSets) {
val compilations = sourceSetToCompilations[sourceSet] ?: continue
sourceSet.platform = compilations.map { it.platform }.distinct().singleOrNull() ?: KotlinPlatform.COMMON
sourceSet.dependencies = compilations.flatMapTo(LinkedHashSet()) { it.dependencies }
sourceSet.isTestModule = compilations.all { it.isTestModule }
sourceSet.isAndroid = compilations.all { it.isAndroid }
}
}
companion object {
private val KOTLIN_ANDROID_TARGET_CLASS_NAME = "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget"
}
}
@@ -0,0 +1,264 @@
/*
* 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.gradle
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.plugins.gradle.model.AbstractExternalDependency
import org.jetbrains.plugins.gradle.model.DefaultExternalProjectDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalProjectDependency
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver
import org.jetbrains.plugins.gradle.tooling.util.DependencyResolverImpl
import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder
import java.io.File
import java.lang.Exception
class KotlinMPPGradleModelBuilder : 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 {
return modelName == KotlinMPPGradleModel::class.java.name
}
override fun buildAll(modelName: String, project: Project): Any? {
val dependencyResolver = DependencyResolverImpl(
project,
false,
false,
true,
SourceSetCachedFinder(project)
)
val sourceSets = buildSourceSets(project) ?: return null
val sourceSetMap = sourceSets.map { it.name to it }.toMap()
val targets = buildTargets(sourceSetMap, dependencyResolver, project) ?: return null
computeSourceSetsDeferredInfo(sourceSets, targets)
val coroutinesState = getCoroutinesState(project)
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState))
}
private fun getCoroutinesState(project: Project): String? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getExperimental = kotlinExt.javaClass.getMethodOrNull("getExperimental") ?: return null
val experimentalExt = getExperimental(kotlinExt) ?: return null
val getCoroutines = experimentalExt.javaClass.getMethodOrNull("getCoroutines") ?: return null
return getCoroutines(experimentalExt) as? String
}
private fun buildSourceSets(project: Project): Collection<KotlinSourceSetImpl>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getSourceSets = kotlinExt.javaClass.getMethodOrNull("getSourceSets") ?: return null
@Suppress("UNCHECKED_CAST")
val sourceSets =
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
return sourceSets.mapNotNull { buildSourceSet(it) }
}
private fun buildSourceSet(gradleSourceSet: Named): KotlinSourceSetImpl? {
val sourceSetClass = gradleSourceSet.javaClass
val getSourceDirSet = sourceSetClass.getMethodOrNull("getKotlin") ?: return null
val getResourceDirSet = sourceSetClass.getMethodOrNull("getResources") ?: return null
val getDependsOn = sourceSetClass.getMethodOrNull("getDependsOn") ?: return null
val sourceDirs = (getSourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
val resourceDirs = (getResourceDirSet(gradleSourceSet) as? SourceDirectorySet)?.srcDirs ?: emptySet()
@Suppress("UNCHECKED_CAST")
val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
return KotlinSourceSetImpl(gradleSourceSet.name, sourceDirs, resourceDirs, dependsOnSourceSets)
}
private fun buildDependencies(
gradleCompilation: Any,
dependencyResolver: DependencyResolver,
configurationNameAccessor: String,
scope: String,
project: Project
): Collection<KotlinDependency> {
val gradleCompilationClass = gradleCompilation.javaClass
val getConfigurationName = gradleCompilationClass.getMethodOrNull(configurationNameAccessor) ?: return emptyList()
val configurationName = getConfigurationName(gradleCompilation) as? String ?: return emptyList()
val configuration = project.configurations.findByName(configurationName) ?: return emptyList()
if (!configuration.isCanBeResolved) return emptyList()
val artifactsByProjectPath = configuration
.resolvedConfiguration
.lenientConfiguration
.firstLevelModuleDependencies
.flatMap { it.moduleArtifacts }
.filter { it.id.componentIdentifier is ProjectComponentIdentifier }
.groupBy { (it.id.componentIdentifier as ProjectComponentIdentifier).projectPath }
return dependencyResolver.resolveDependencies(configuration)
.apply {
forEach<ExternalDependency?> { (it as? AbstractExternalDependency)?.scope = scope }
}
.map {
if (it !is ExternalProjectDependency || it.configurationName != Dependency.DEFAULT_CONFIGURATION) return@map it
val artifacts = artifactsByProjectPath[it.projectPath] ?: return@map it
val classifier = artifacts.mapTo(LinkedHashSet()) { it.classifier }.singleOrNull() ?: return@map it
DefaultExternalProjectDependency(it).apply {
this.classifier = classifier
}
}
}
private fun buildTargets(
sourceSetMap: Map<String, KotlinSourceSet>,
dependencyResolver: DependencyResolver,
project: Project
): Collection<KotlinTarget>? {
val kotlinExt = project.extensions.findByName("kotlin") ?: return null
val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null
@Suppress("UNCHECKED_CAST")
val targets = (getTargets.invoke(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
return targets.mapNotNull { buildTarget(it, sourceSetMap, dependencyResolver, project) }
}
private fun buildTarget(
gradleTarget: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
dependencyResolver: DependencyResolver,
project: Project
): KotlinTarget? {
val targetClass = gradleTarget.javaClass
val getPlatformType = targetClass.getMethodOrNull("getPlatformType") ?: return null
val getCompilations = targetClass.getMethodOrNull("getCompilations") ?: return null
val getDisambiguationClassifier = targetClass.getMethodOrNull("getDisambiguationClassifier") ?: return null
val platformId = (getPlatformType.invoke(gradleTarget) as? Named)?.name ?: return null
val platform = KotlinPlatform.byId(platformId) ?: return null
val disambiguationClassifier = getDisambiguationClassifier(gradleTarget) as? String
val isAndroid = targetClass.name == KOTLIN_ANDROID_TARGET_CLASS_NAME
@Suppress("UNCHECKED_CAST")
val gradleCompilations =
(getCompilations.invoke(gradleTarget) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
val compilations = gradleCompilations.mapNotNull { buildCompilation(it, sourceSetMap, dependencyResolver, isAndroid, project) }
val jar = buildTargetJar(gradleTarget, project)
val target = KotlinTargetImpl(isAndroid, gradleTarget.name, disambiguationClassifier, platform, compilations, jar)
compilations.forEach { it.target = target }
return target
}
private fun buildTargetJar(gradleTarget: Named, project: Project): KotlinTargetJar? {
val targetClass = gradleTarget.javaClass
val getArtifactsTaskName = targetClass.getMethodOrNull("getArtifactsTaskName") ?: return null
val artifactsTaskName = getArtifactsTaskName(gradleTarget) as? String ?: return null
val jarTask = project.tasks.findByName(artifactsTaskName) ?: return null
val jarTaskClass = jarTask.javaClass
val getArchivePath = jarTaskClass.getMethodOrNull("getArchivePath")
val archiveFile = getArchivePath?.invoke(jarTask) as? File?
return KotlinTargetJarImpl(archiveFile)
}
private fun buildCompilation(
gradleCompilation: Named,
sourceSetMap: Map<String, KotlinSourceSet>,
dependencyResolver: DependencyResolver,
isAndroid: Boolean,
project: Project
): KotlinCompilationImpl? {
val compilationClass = gradleCompilation.javaClass
val getKotlinSourceSets = compilationClass.getMethodOrNull("getKotlinSourceSets") ?: return null
@Suppress("UNCHECKED_CAST")
val kotlinGradleSourceSets = (getKotlinSourceSets(gradleCompilation) as? Collection<Named>) ?: return null
val kotlinSourceSets = kotlinGradleSourceSets.mapNotNull { sourceSetMap[it.name] }
val getCompileKotlinTaskName = compilationClass.getMethodOrNull("getCompileKotlinTaskName") ?: return null
@Suppress("UNCHECKED_CAST")
val compileKotlinTaskName = (getCompileKotlinTaskName(gradleCompilation) as? String) ?: return null
val compileKotlinTask = project.tasks.findByName(compileKotlinTaskName) ?: return null
val output = buildCompilationOutput(gradleCompilation, compileKotlinTask) ?: return null
val arguments = buildCompilationArguments(compileKotlinTask) ?: return null
val dependencyClasspath = buildDependencyClasspath(compileKotlinTask)
val dependencies = buildDependencies(gradleCompilation, dependencyResolver, project)
return KotlinCompilationImpl(isAndroid, gradleCompilation.name, kotlinSourceSets, dependencies, output, arguments, dependencyClasspath)
}
private fun buildDependencies(
gradleCompilation: Named,
dependencyResolver: DependencyResolver,
project: Project
): Set<KotlinDependency> {
return LinkedHashSet<KotlinDependency>().apply {
this += buildDependencies(
gradleCompilation, dependencyResolver, "getCompileDependencyConfigurationName", "COMPILE", project
)
this += buildDependencies(
gradleCompilation, dependencyResolver, "getRuntimeDependencyConfigurationName", "RUNTIME", project
)
}
}
private fun buildCompilationArguments(compileKotlinTask: Task): KotlinCompilationArguments? {
val compileTaskClass = compileKotlinTask.javaClass
val getCurrentArguments = compileTaskClass.getMethodOrNull("getSerializedCompilerArguments") ?: return null
val getDefaultArguments = compileTaskClass.getMethodOrNull("getDefaultSerializedCompilerArguments") ?: return null
@Suppress("UNCHECKED_CAST")
val currentArguments = getCurrentArguments(compileKotlinTask) as? List<String> ?: return null
@Suppress("UNCHECKED_CAST")
val defaultArguments = getDefaultArguments(compileKotlinTask) as? List<String> ?: return null
return KotlinCompilationArgumentsImpl(defaultArguments, currentArguments)
}
private fun buildDependencyClasspath(compileKotlinTask: Task): List<String> {
val abstractKotlinCompileClass =
compileKotlinTask.javaClass.classLoader.loadClass(AbstractKotlinGradleModelBuilder.ABSTRACT_KOTLIN_COMPILE_CLASS)
val getCompileClasspath =
abstractKotlinCompileClass.getDeclaredMethodOrNull("getCompileClasspath") ?: return emptyList()
@Suppress("UNCHECKED_CAST")
return (getCompileClasspath(compileKotlinTask) as? Collection<File>)?.map { it.path } ?: emptyList()
}
private fun buildCompilationOutput(
gradleCompilation: Named,
compileKotlinTask: Task
): KotlinCompilationOutput? {
val compilationClass = gradleCompilation.javaClass
val getOutput = compilationClass.getMethodOrNull("getOutput") ?: return null
val gradleOutput = getOutput(gradleCompilation) ?: return null
val gradleOutputClass = gradleOutput.javaClass
val getClassesDirs = gradleOutputClass.getMethodOrNull("getClassesDirs") ?: return null
val getResourcesDir = gradleOutputClass.getMethodOrNull("getResourcesDir") ?: return null
val compileKotlinTaskClass = compileKotlinTask.javaClass
val getDestinationDir = compileKotlinTaskClass.getMethodOrNull("getDestinationDir") ?: return null
val classesDirs = getClassesDirs(gradleOutput) as? FileCollection ?: return null
val resourcesDir = getResourcesDir(gradleOutput) as? File ?: return null
val destinationDir = getDestinationDir(compileKotlinTask) as? File
return KotlinCompilationOutputImpl(classesDirs.files, destinationDir, resourcesDir)
}
private fun computeSourceSetsDeferredInfo(
sourceSets: Collection<KotlinSourceSetImpl>,
targets: Collection<KotlinTarget>
) {
val sourceSetToCompilations = LinkedHashMap<KotlinSourceSet, MutableSet<KotlinCompilation>>()
for (target in targets) {
for (compilation in target.compilations) {
for (sourceSet in compilation.sourceSets) {
sourceSetToCompilations.getOrPut(sourceSet) { LinkedHashSet() } += compilation
}
}
}
for (sourceSet in sourceSets) {
val compilations = sourceSetToCompilations[sourceSet] ?: continue
sourceSet.platform = compilations.map { it.platform }.distinct().singleOrNull() ?: KotlinPlatform.COMMON
sourceSet.dependencies = compilations.flatMapTo(LinkedHashSet()) { it.dependencies }
sourceSet.isTestModule = compilations.all { it.isTestModule }
sourceSet.isAndroid = compilations.all { it.isAndroid }
}
}
companion object {
private val KOTLIN_ANDROID_TARGET_CLASS_NAME = "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget"
}
}
@@ -0,0 +1,86 @@
/*
* 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.gradle
import java.io.File
class KotlinSourceSetImpl(
override val name: String,
override val sourceDirs: Set<File>,
override val resourceDirs: Set<File>,
override val dependsOnSourceSets: Set<String>
) : KotlinSourceSet {
override var isAndroid: Boolean = false
internal set
override var platform: KotlinPlatform = KotlinPlatform.COMMON
internal set
override var dependencies: Set<KotlinDependency> = emptySet()
internal set
override var isTestModule: Boolean = false
internal set
override fun toString() = name
}
class KotlinCompilationOutputImpl(
override val classesDirs: Set<File>,
override val effectiveClassesDir: File?,
override val resourcesDir: File?
) : KotlinCompilationOutput
class KotlinCompilationArgumentsImpl(
override val defaultArguments: List<String>,
override val currentArguments: List<String>
) : KotlinCompilationArguments
class KotlinCompilationImpl(
override val isAndroid: Boolean,
override val name: String,
override val sourceSets: Collection<KotlinSourceSet>,
override val dependencies: Set<KotlinDependency>,
override val output: KotlinCompilationOutput,
override val arguments: KotlinCompilationArguments,
override val dependencyClasspath: List<String>
) : KotlinCompilation {
override lateinit var target: KotlinTarget
internal set
override val platform: KotlinPlatform
get() = target.platform
override val isTestModule: Boolean
get() = name == KotlinCompilation.TEST_COMPILATION_NAME
override fun toString() = name
}
class KotlinTargetJarImpl(
override val archiveFile: File?
) : KotlinTargetJar
class KotlinTargetImpl(
override val isAndroid: Boolean,
override val name: String,
override val disambiguationClassifier: String?,
override val platform: KotlinPlatform,
override val compilations: Collection<KotlinCompilation>,
override val jar: KotlinTargetJar?
) : KotlinTarget {
override fun toString() = name
}
class ExtraFeaturesImpl(
override val coroutinesState: String?
) : ExtraFeatures
class KotlinMPPGradleModelImpl(
override val sourceSets: Map<String, KotlinSourceSet>,
override val targets: Collection<KotlinTarget>,
override val extraFeatures: ExtraFeatures
) : KotlinMPPGradleModel
@@ -1 +1,2 @@
org.jetbrains.kotlin.gradle.KotlinGradleModelBuilder
org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder
+20
View File
@@ -0,0 +1,20 @@
/*
* 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.gradle
fun Class<*>.getMethodOrNull(name: String, vararg parameterTypes: Class<*>) =
try {
getMethod(name, *parameterTypes)
} catch (e: Exception) {
null
}
fun Class<*>.getDeclaredMethodOrNull(name: String, vararg parameterTypes: Class<*>) =
try {
getDeclaredMethod(name, *parameterTypes)?.also { it.isAccessible = true }
} catch (e: Exception) {
null
}
+2
View File
@@ -72,10 +72,12 @@
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleMPPModuleDataService"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
<projectResolve implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidMPPGradleProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+2
View File
@@ -70,10 +70,12 @@
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleMPPModuleDataService"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
<projectResolve implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidMPPGradleProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+2
View File
@@ -72,10 +72,12 @@
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleMPPModuleDataService"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
<projectResolve implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidMPPGradleProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+2
View File
@@ -72,10 +72,12 @@
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleMPPModuleDataService"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
<projectResolve implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidMPPGradleProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+3
View File
@@ -13,12 +13,15 @@
<projectResolve implementation="org.jetbrains.kotlin.allopen.ide.AllOpenProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.noarg.ide.NoArgProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinTargetDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<localInspection
+3
View File
@@ -13,12 +13,15 @@
<projectResolve implementation="org.jetbrains.kotlin.allopen.ide.AllOpenProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.noarg.ide.NoArgProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinTargetDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<localInspection
+3
View File
@@ -13,6 +13,7 @@
<projectResolve implementation="org.jetbrains.kotlin.allopen.ide.AllOpenProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.noarg.ide.NoArgProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver"/>
</extensions>
<extensionPoints>
@@ -27,6 +28,8 @@
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinTargetDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<localInspection
+3
View File
@@ -13,6 +13,7 @@
<projectResolve implementation="org.jetbrains.kotlin.allopen.ide.AllOpenProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.noarg.ide.NoArgProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverProjectResolverExtension" order="last"/>
<projectResolve implementation="org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver"/>
</extensions>
<extensionPoints>
@@ -27,6 +28,8 @@
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleLibraryDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinTargetDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<localInspection
@@ -252,16 +252,29 @@ fun parseCompilerArgumentsToFacet(
defaultArguments: List<String>,
kotlinFacet: KotlinFacet,
modelsProvider: IdeModifiableModelsProvider?
) {
val compilerArgumentsClass = kotlinFacet.configuration.settings.compilerArguments?.javaClass ?: return
val currentArgumentsBean = compilerArgumentsClass.newInstance()
val defaultArgumentsBean = compilerArgumentsClass.newInstance()
parseCommandLineArguments(defaultArguments, defaultArgumentsBean)
parseCommandLineArguments(arguments, currentArgumentsBean)
applyCompilerArgumentsToFacet(currentArgumentsBean, defaultArgumentsBean, kotlinFacet, modelsProvider)
}
fun applyCompilerArgumentsToFacet(
arguments: CommonCompilerArguments,
defaultArguments: CommonCompilerArguments?,
kotlinFacet: KotlinFacet,
modelsProvider: IdeModifiableModelsProvider
) {
with(kotlinFacet.configuration.settings) {
val compilerArguments = this.compilerArguments ?: return
val defaultCompilerArguments = compilerArguments::class.java.newInstance()
parseCommandLineArguments(defaultArguments, defaultCompilerArguments)
val defaultCompilerArguments = defaultArguments?.let { copyBean(it) } ?: compilerArguments::class.java.newInstance()
defaultCompilerArguments.convertPathsToSystemIndependent()
parseCommandLineArguments(arguments, compilerArguments)
val emptyArgs = compilerArguments::class.java.newInstance()
copyBeanTo(arguments, compilerArguments) { property, value -> value != property.get(emptyArgs) }
compilerArguments.convertPathsToSystemIndependent()
// Retain only fields exposed (and not explicitly ignored) in facet configuration editor.