diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPDataNodes.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPDataNodes.kt index e0ec8e3dedd..58c282ef311 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPDataNodes.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPDataNodes.kt @@ -27,8 +27,11 @@ import com.intellij.openapi.externalSystem.model.Key as ExternalKey var DataNode.kotlinSourceSet: KotlinSourceSetInfo? by CopyableDataNodeUserDataProperty(Key.create("KOTLIN_SOURCE_SET")) +var DataNode.kotlinImportingDiagnosticsContainer: KotlinImportingDiagnosticsContainer? + by CopyableDataNodeUserDataProperty(Key.create("KOTLIN_IMPORTING_DIAGNOSTICS_CONTAINER")) + val DataNode.kotlinAndroidSourceSets: List? - get() = ExternalSystemApiUtil.getChildren(this, KotlinAndroidSourceSetData.KEY).firstOrNull()?.data?.sourceSetInfos + get() = ExternalSystemApiUtil.getChildren(this, KotlinAndroidSourceSetData.KEY).firstOrNull()?.data?.sourceSetInfos class KotlinSourceSetInfo @PropertyMapping("kotlinModule") constructor(val kotlinModule: KotlinModule) : Serializable { var moduleId: String? = null diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt index 8118c9e688e..dfea085c6ae 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt @@ -61,7 +61,6 @@ import java.io.File import java.lang.reflect.Proxy import java.util.* import java.util.stream.Collectors -import kotlin.collections.HashMap @Order(ExternalSystemConstants.UNORDERED + 1) open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionCompat() { @@ -458,9 +457,12 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp } } - mainModuleNode.kotlinNativeHome = mppModel.kotlinNativeHome - mainModuleNode.coroutines = mppModel.extraFeatures.coroutinesState - mainModuleNode.isHmpp = mppModel.extraFeatures.isHMPPEnabled + with(mainModuleNode) { + kotlinNativeHome = mppModel.kotlinNativeHome + coroutines = mppModel.extraFeatures.coroutinesState + isHmpp = mppModel.extraFeatures.isHMPPEnabled + kotlinImportingDiagnosticsContainer = mppModel.kotlinImportingDiagnostics + } //TODO improve passing version of used multiplatform } @@ -782,7 +784,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp if (fromModule.data == toModule.data) return val fromData = fromModule.data as? ModuleData ?: return val toData = toModule.data as? ModuleData ?: return - val existing = fromModule.children.mapNotNull { it.data as? ModuleDependencyData }.filter { it.target.id == (toModule.data as? ModuleData)?.id } + val existing = fromModule.children.mapNotNull { it.data as? ModuleDependencyData } + .filter { it.target.id == (toModule.data as? ModuleData)?.id } val nodeToModify = existing.singleOrNull() ?: existing.firstOrNull { it.scope == DependencyScope.COMPILE } ?: existing.firstOrNull() if (nodeToModify != null) { diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/HierarchicalMultiplatformProjectImportingTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/HierarchicalMultiplatformProjectImportingTest.kt index f24962e85aa..4972e47949f 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/HierarchicalMultiplatformProjectImportingTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/HierarchicalMultiplatformProjectImportingTest.kt @@ -8,7 +8,10 @@ package org.jetbrains.kotlin.gradle import com.intellij.openapi.roots.DependencyScope import org.jetbrains.jps.model.java.JavaResourceRootType import org.jetbrains.jps.model.java.JavaSourceRootType -import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.config.ResourceKotlinRootType +import org.jetbrains.kotlin.config.SourceKotlinRootType +import org.jetbrains.kotlin.config.TestResourceKotlinRootType +import org.jetbrains.kotlin.config.TestSourceKotlinRootType import org.jetbrains.kotlin.idea.codeInsight.gradle.MultiplePluginVersionGradleImportingTestCase import org.jetbrains.kotlin.idea.codeInsight.gradle.mppImportTestMinVersionForMaster import org.jetbrains.kotlin.konan.target.KonanTarget @@ -440,6 +443,9 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl module("my-app.orphan") { targetPlatform(jvm, js) } + module("my-app") { + diagnostics(OrphanSourceSetsImportingDiagnostic::class.java to 1) + } } } @@ -529,6 +535,10 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl checkProjectStructure(exhaustiveModuleList = false, exhaustiveSourceSourceRootList = false, exhaustiveDependencyList = false) { + module("my-app") { + diagnostics(OrphanSourceSetsImportingDiagnostic::class.java to 3) + } + // (jvm, js, native) is highly undesirable module("my-app.danglingOnJvm") { targetPlatform(jvm, js) diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt index c813198d91e..61d5161a0b8 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/projectStructureDSL.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project @@ -14,13 +15,16 @@ import com.intellij.openapi.util.io.FileUtil import org.jetbrains.jps.model.module.JpsModuleSourceRootType import org.jetbrains.jps.util.JpsPathUtil import org.jetbrains.kotlin.config.ExternalSystemTestRunTask +import org.jetbrains.kotlin.idea.configuration.kotlinImportingDiagnosticsContainer import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.externalSystemTestRunTasks +import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID import org.jetbrains.kotlin.idea.project.isHMPPEnabled import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.platform import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.presentableDescription +import org.jetbrains.plugins.gradle.util.GradleUtil class MessageCollector { private val builder = StringBuilder() @@ -37,6 +41,7 @@ class MessageCollector { } } +@Suppress("UnstableApiUsage") class ProjectInfo( project: Project, internal val projectPath: String, @@ -47,6 +52,7 @@ class ProjectInfo( ) { internal val messageCollector = MessageCollector() private val moduleManager = ModuleManager.getInstance(project) + private val projectDataNode = ExternalSystemApiUtil.findProjectData(project, GRADLE_SYSTEM_ID, projectPath) private val expectedModuleNames = HashSet() private var allModulesAsserter: (ModuleInfo.() -> Unit)? = null @@ -201,7 +207,7 @@ class ModuleInfo( fun moduleDependency(moduleName: String, scope: DependencyScope, productionOnTest: Boolean? = null) { val moduleEntries = - rootModel.orderEntries.filterIsInstance().filter { it.moduleName == moduleName && it.scope == scope} + rootModel.orderEntries.filterIsInstance().filter { it.moduleName == moduleName && it.scope == scope } if (moduleEntries.size > 1) { projectInfo.messageCollector.report( "Found multiple order entries for module $moduleName: ${rootModel.orderEntries.filterIsInstance()}" @@ -270,6 +276,22 @@ class ModuleInfo( } } + @Suppress("UnstableApiUsage") + fun diagnostics(vararg expectedByType: Pair, Int>) { + val moduleNode = GradleUtil.findGradleModuleData(module) + val diagnostics = moduleNode!!.kotlinImportingDiagnosticsContainer!! + expectedByType.forEach { (expectedClazz, expectedCount) -> + val typedDiagnostics = diagnostics.filterIsInstance(expectedClazz) + if (typedDiagnostics.size != expectedCount) { + val actualCount = typedDiagnostics.size + projectInfo.messageCollector.report( + "Expected number of ${expectedClazz.simpleName} diagnostics $expectedCount doesn't match the actual one: $actualCount" + ) + } + } + } + + fun run(body: ModuleInfo.() -> Unit = {}) { body() @@ -287,7 +309,9 @@ class ModuleInfo( } } - if ((!module.externalSystemTestRunTasks().containsAll(expectedExternalSystemTestTasks)) || (projectInfo.exhaustiveTestsList && (module.externalSystemTestRunTasks() != expectedExternalSystemTestTasks))) { + if ((!module.externalSystemTestRunTasks() + .containsAll(expectedExternalSystemTestTasks)) || (projectInfo.exhaustiveTestsList && (module.externalSystemTestRunTasks() != expectedExternalSystemTestTasks)) + ) { projectInfo.messageCollector.report( "Module '${module.name}': Expected tests list $expectedExternalSystemTestTasks doesn't match the actual one: ${module.externalSystemTestRunTasks()}" ) diff --git a/idea/kotlin-gradle-tooling/src/KotlinImportingDiagnostic.kt b/idea/kotlin-gradle-tooling/src/KotlinImportingDiagnostic.kt new file mode 100644 index 00000000000..85ff99baaf0 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/KotlinImportingDiagnostic.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2021 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 java.io.Serializable + + +interface KotlinImportingDiagnostic : Serializable + +interface KotlinSourceSetImportingDiagnostic : KotlinImportingDiagnostic { + val kotlinSourceSet: KotlinSourceSet +} + +typealias KotlinImportingDiagnosticsContainer = MutableSet + +data class OrphanSourceSetsImportingDiagnostic(override val kotlinSourceSet: KotlinSourceSet) : KotlinSourceSetImportingDiagnostic \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt index a6940469f8e..8ae2ccc41d3 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt @@ -180,6 +180,7 @@ interface KotlinMPPGradleModel : Serializable { val targets: Collection val extraFeatures: ExtraFeatures val kotlinNativeHome: String + val kotlinImportingDiagnostics: KotlinImportingDiagnosticsContainer companion object { const val NO_KOTLIN_NATIVE_HOME = "" diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt index bf095012b00..a90787753dc 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt @@ -76,7 +76,9 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { ), kotlinNativeHome, dependencyMapper.toDependencyMap() - ) + ).apply { + kotlinImportingDiagnostics += collectDiagnostics(importingContext) + } } private fun filterOrphanSourceSets( @@ -906,6 +908,17 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { companion object { private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java) + private val DEFAULT_IMPORTING_CHECKERS = listOf( + OrphanSourceSetImportingChecker + ) + + private fun KotlinMPPGradleModel.collectDiagnostics(importingContext: MultiplatformModelImportingContext): KotlinImportingDiagnosticsContainer = + mutableSetOf().apply { + DEFAULT_IMPORTING_CHECKERS.forEach { + it.check(this@collectDiagnostics, this, importingContext) + } + } + fun Project.getTargets(): Collection? { val kotlinExt = project.extensions.findByName("kotlin") ?: return null val getTargets = kotlinExt.javaClass.getMethodOrNull("getTargets") ?: return null diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt index 3bde747398b..800341d5d25 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt @@ -243,7 +243,8 @@ data class KotlinMPPGradleModelImpl( override val targets: Collection, override val extraFeatures: ExtraFeatures, override val kotlinNativeHome: String, - override val dependencyMap: Map + override val dependencyMap: Map, + override val kotlinImportingDiagnostics: KotlinImportingDiagnosticsContainer = mutableSetOf() ) : KotlinMPPGradleModel { constructor(mppModel: KotlinMPPGradleModel, cloningCache: MutableMap) : this( @@ -264,7 +265,8 @@ data class KotlinMPPGradleModelImpl( mppModel.extraFeatures.isNativeDependencyPropagationEnabled ), mppModel.kotlinNativeHome, - mppModel.dependencyMap.map { it.key to it.value.deepCopy(cloningCache) }.toMap() + mppModel.dependencyMap.map { it.key to it.value.deepCopy(cloningCache) }.toMap(), + mppModel.kotlinImportingDiagnostics.toMutableSet() ) } diff --git a/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingChecker.kt b/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingChecker.kt new file mode 100644 index 00000000000..63417a2a4c2 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingChecker.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2021 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 + +internal interface MultiplatformModelImportingChecker { + fun check(model: KotlinMPPGradleModel, reportTo: KotlinImportingDiagnosticsContainer, context: MultiplatformModelImportingContext) +} + +internal object OrphanSourceSetImportingChecker : MultiplatformModelImportingChecker { + override fun check( + model: KotlinMPPGradleModel, + reportTo: KotlinImportingDiagnosticsContainer, + context: MultiplatformModelImportingContext + ) = model.sourceSets.values.filter { context.isOrphanSourceSet(it) } + .map { OrphanSourceSetsImportingDiagnostic(it) } + .forEach { reportTo += it } +} \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingContext.kt b/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingContext.kt index 85ad316f5ca..1d760b09508 100644 --- a/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingContext.kt +++ b/idea/kotlin-gradle-tooling/src/MultiplatformModelImportingContext.kt @@ -7,8 +7,6 @@ package org.jetbrains.kotlin.gradle import org.gradle.api.Project import org.gradle.api.logging.Logging -import org.jetbrains.kotlin.gradle.GradleImportProperties.* -import java.lang.Exception private val logger = Logging.getLogger(KotlinMPPGradleModelBuilder::class.java) @@ -69,7 +67,6 @@ internal class MultiplatformModelImportingContextImpl(override val project: Proj override val sourceSets: Collection get() = sourceSetsByNames.values - /** see [initializeCompilations] */ override lateinit var compilations: Collection private set