diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt index 5b84ea4428c..5b8694e2fa0 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt @@ -19,10 +19,7 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.openapi.diagnostic.Logger 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.ModuleDependencyData -import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.model.project.* import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.util.Key @@ -43,6 +40,7 @@ import org.jetbrains.plugins.gradle.model.FileCollectionDependency 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.GradleProjectResolverUtil import java.io.File import java.util.* import kotlin.collections.HashMap @@ -66,6 +64,8 @@ var DataNode.dependenciesCache by DataNodeUserDataProperty( Key.create, Collection>>>("MODULE_DEPENDENCIES_CACHE") ) +var DataNode.isPureKotlinSourceFolder + by NotNullableCopyableDataNodeUserDataProperty(Key.create("PURE_KOTLIN_SOURCE_FOLDER"), false) class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() { @@ -282,4 +282,35 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() @Suppress("UNCHECKED_CAST") return ideProject.children.find { (it.data as? ModuleData)?.id == fullModuleId } as DataNode? } + + override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode) { + nextResolver.populateModuleContentRoots(gradleModule, ideModule) + val moduleNamePrefix = GradleProjectResolverUtil.getModuleId(resolverCtx, gradleModule) + resolverCtx.getExtraProject(gradleModule, KotlinGradleModel::class.java)?.let { gradleModel -> + val gradleSourceSets = ideModule.children.filter { it.data is GradleSourceSetData } as Collection> + for (gradleSourceSetNode in gradleSourceSets) { + val propertiesForSourceSet = + gradleModel.kotlinTaskProperties.filter { (k, v) -> gradleSourceSetNode.data.externalName == "$moduleNamePrefix:$k" } + .toList().singleOrNull() + gradleSourceSetNode.children.forEach { dataNode -> + val data = dataNode.data as? ContentRootData + if (data != null) { + if (propertiesForSourceSet?.second?.pureKotlinSourceFolders?.contains(File(data.rootPath)) == true) { + @Suppress("UNCHECKED_CAST") + (dataNode as DataNode).isPureKotlinSourceFolder = true + } + val packagePrefix = propertiesForSourceSet?.second?.packagePrefix + if (packagePrefix != null) { + ExternalSystemSourceType.values().filter { !(it.isResource || it.isGenerated) }.forEach { type -> + val paths = data.getPaths(type) + val newPaths = paths.map { ContentRootData.SourceRoot(it.path, packagePrefix) } + paths.clear() + paths.addAll(newPaths) + } + } + } + } + } + } + } } diff --git a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt index 9418828f051..f0ca8a2220d 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt @@ -65,6 +65,7 @@ interface KotlinGradleModel : Serializable { val platformPluginId: String? val implements: List val kotlinTarget: String? + val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet } data class KotlinGradleModelImpl( @@ -73,7 +74,8 @@ data class KotlinGradleModelImpl( override val coroutines: String?, override val platformPluginId: String?, override val implements: List, - override val kotlinTarget: String? = null + override val kotlinTarget: String? = null, + override val kotlinTaskProperties: KotlinTaskPropertiesBySourceSet ) : KotlinGradleModel abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService { @@ -97,6 +99,9 @@ abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService { val kotlinPluginIds = listOf("kotlin", "kotlin2js", "kotlin-android") val ABSTRACT_KOTLIN_COMPILE_CLASS = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile" + val kotlinProjectExtensionClass = "org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension" + val kotlinSourceSetClass = "org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet" + fun Task.getSourceSetName(): String { return try { javaClass.methods.firstOrNull { it.name.startsWith("getSourceSetName") && it.parameterTypes.isEmpty() }?.invoke(this) as? String @@ -172,6 +177,7 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() { val platformPluginId = platformPluginIds.singleOrNull { project.plugins.findPlugin(it) != null } val compilerArgumentsBySourceSet = LinkedHashMap() + val extraProperties = HashMap() project.getAllTasks(false)[project]?.forEach { compileTask -> if (compileTask.javaClass.name !in kotlinCompileTaskClasses) return@forEach @@ -182,6 +188,7 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() { val defaultArguments = compileTask.getCompilerArguments("getDefaultSerializedCompilerArguments").orEmpty() val dependencyClasspath = compileTask.getDependencyClasspath() compilerArgumentsBySourceSet[sourceSetName] = ArgsInfoImpl(currentArguments, defaultArguments, dependencyClasspath) + extraProperties.acknowledgeTask(compileTask) } val platform = platformPluginId ?: pluginToPlatform.entries.singleOrNull { project.plugins.findPlugin(it.key) != null }?.value @@ -193,7 +200,8 @@ class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() { getCoroutines(project), platform, implementedProjects.map { it.pathOrName() }, - platform ?: kotlinPluginId + platform ?: kotlinPluginId, + extraProperties ) } } diff --git a/idea/kotlin-gradle-tooling/src/KotlinTasksPropertyUtils.kt b/idea/kotlin-gradle-tooling/src/KotlinTasksPropertyUtils.kt new file mode 100644 index 00000000000..dfb3b6efeae --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/KotlinTasksPropertyUtils.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.Task +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.internal.FactoryNamedDomainObjectContainer +import org.gradle.api.plugins.JavaPluginConvention +import org.jetbrains.kotlin.gradle.AbstractKotlinGradleModelBuilder.Companion.getSourceSetName +import org.jetbrains.kotlin.gradle.AbstractKotlinGradleModelBuilder.Companion.kotlinProjectExtensionClass +import org.jetbrains.kotlin.gradle.AbstractKotlinGradleModelBuilder.Companion.kotlinSourceSetClass +import java.io.File +import java.io.Serializable +import java.lang.Exception + +interface KotlinTaskProperties : Serializable { + val incremental: Boolean? + val packagePrefix: String? + val pureKotlinSourceFolders: List? +} + +data class KotlinTaskPropertiesImpl( + override val incremental: Boolean?, + override val packagePrefix: String?, + override val pureKotlinSourceFolders: List? + +) : KotlinTaskProperties + +typealias KotlinTaskPropertiesBySourceSet = MutableMap + +private fun Task.getPackagePrefix(): String? { + try { + val getJavaPackagePrefix = this.javaClass.getMethod("getJavaPackagePrefix") + @Suppress("UNCHECKED_CAST") + return (getJavaPackagePrefix.invoke(this) as? String) + } catch (e: Exception) { + } + return null +} + +private fun Task.getIsIncremental(): Boolean? { + try { + val abstractKotlinCompileClass = javaClass.classLoader.loadClass(AbstractKotlinGradleModelBuilder.ABSTRACT_KOTLIN_COMPILE_CLASS) + val getIncremental = abstractKotlinCompileClass.getDeclaredMethod("getIncremental") + @Suppress("UNCHECKED_CAST") + return (getIncremental.invoke(this) as? Boolean) + } catch (e: Exception) { + } + return null +} + +private fun Task.getPureKotlinSourceRoots(sourceSet: String): List? { + try { + val kotlinExtensionClass = project.extensions.findByType(javaClass.classLoader.loadClass(kotlinProjectExtensionClass)) + val getKotlinMethod = javaClass.classLoader.loadClass(kotlinSourceSetClass).getMethod("getKotlin") + val kotlinSourceSet = (kotlinExtensionClass?.javaClass?.getMethod("getSourceSets")?.invoke(kotlinExtensionClass) + as? FactoryNamedDomainObjectContainer)?.asMap?.get(sourceSet) ?: return null + val javaSourceSet = + (project.convention.getPlugin(JavaPluginConvention::class.java) as JavaPluginConvention).sourceSets.asMap[sourceSet] + val pureJava = javaSourceSet?.java?.srcDirs + + return (getKotlinMethod.invoke(kotlinSourceSet) as? SourceDirectorySet)?.srcDirs?.filter { + !(pureJava?.contains(it) ?: false) + }?.toList() + } catch (e: Exception) { + } + return null +} + +fun KotlinTaskPropertiesBySourceSet.acknowledgeTask(compileTask: Task) { + this[compileTask.getSourceSetName()] = + KotlinTaskPropertiesImpl( + compileTask.getIsIncremental(), + compileTask.getPackagePrefix(), + compileTask.getPureKotlinSourceRoots(compileTask.getSourceSetName()) + ) +}