From bdad58cec6849d3bc0a77b6d753adb76145aa21e Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 15 Aug 2017 19:00:47 +0300 Subject: [PATCH] Kotlin Facet: Support "implements" relation between modules #KT-17593 Fixed --- .../KotlinGradleProjectResolverExtension.kt | 3 + .../KotlinGradleSourceSetDataService.kt | 15 +- .../gradle/GradleFacetImportTest.kt | 259 ++++++++++++++++++ .../kotlin/config/KotlinFacetSettings.kt | 2 + .../kotlin/config/facetSerialization.kt | 7 + .../src/KotlinGradleModelBuilder.kt | 6 +- 6 files changed, 288 insertions(+), 4 deletions(-) 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 bf9e9da1c7c95..b5dbac9a30cf7 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 @@ -45,6 +45,8 @@ var DataNode.coroutines by UserDataProperty(Key.create("KOTLIN_COROUTINES")) var DataNode.platformPluginId by UserDataProperty(Key.create("PLATFORM_PLUGIN_ID")) +var DataNode.implementedModule + by UserDataProperty(Key.create>("IMPLEMENTS")) class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() { override fun getToolingExtensionsClasses(): Set> { @@ -68,6 +70,7 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() ideModule.compilerArgumentsBySourceSet = gradleModel.compilerArgumentsBySourceSet ideModule.coroutines = gradleModel.coroutines ideModule.platformPluginId = gradleModel.platformPluginId + ideModule.implementedModule = gradleModel.implements?.let { findModule(ideProject, it) } super.populateModuleDependencies(gradleModule, ideModule, ideProject) } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt index 22390413c8256..f32a39341c5fc 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt @@ -20,13 +20,13 @@ 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.LibraryData -import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData 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.module.isQualifiedModuleNamesEnabled import com.intellij.openapi.project.Project import com.intellij.openapi.roots.OrderRootType import com.intellij.openapi.roots.impl.libraries.LibraryEx @@ -192,9 +192,9 @@ private fun configureFacetByGradleModule( val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false) kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider) - val sourceSetName = sourceSetNode?.data?.id?.let { it.substring(it.lastIndexOf(':') + 1) } ?: "main" + val sourceSetName = sourceSetNode?.data?.id?.let { it.substring(it.lastIndexOf(':') + 1) } - val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName) + val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main") if (argsInfo != null) { val currentCompilerArguments = argsInfo.currentArguments val defaultCompilerArguments = argsInfo.defaultArguments @@ -205,9 +205,18 @@ private fun configureFacetByGradleModule( adjustClasspath(kotlinFacet, dependencyClasspath) } + kotlinFacet.configuration.settings.implementedModuleName = getImplementedModuleName(moduleNode, sourceSetName) + return kotlinFacet } +private fun getImplementedModuleName(moduleNode: DataNode, sourceSetName: String?): String? { + val baseModuleName = moduleNode.implementedModule?.data?.internalName + if (baseModuleName == null || sourceSetName == null) return baseModuleName + val delimiter = if(isQualifiedModuleNamesEnabled()) "." else "_" + return "$baseModuleName$delimiter$sourceSetName" +} + private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List) { if (dependencyClasspath.isEmpty()) return val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt index 5e8515ec67efb..3e3b5d4042019 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt @@ -1319,6 +1319,265 @@ compileTestKotlin { } } + @Test + fun testImplementsDependency() { + createProjectSubFile( + "build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-common' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + } + + """.trimIndent() + ) + createProjectSubFile( + "settings.gradle", + """ + rootProject.name = 'MultiTest' + include 'MultiTest-jvm', 'MultiTest-js' + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-js/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-js' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-jvm/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-jvm' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + + importProject() + + Assert.assertEquals("MultiTest_main", facetSettings("MultiTest-jvm_main").implementedModuleName) + Assert.assertEquals("MultiTest_test", facetSettings("MultiTest-jvm_test").implementedModuleName) + Assert.assertEquals("MultiTest_main", facetSettings("MultiTest-js_main").implementedModuleName) + Assert.assertEquals("MultiTest_test", facetSettings("MultiTest-js_test").implementedModuleName) + } + + @Test + fun testImplementsDependencyWithCustomSourceSets() { + createProjectSubFile( + "build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-common' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + } + + """.trimIndent() + ) + createProjectSubFile( + "settings.gradle", + """ + rootProject.name = 'MultiTest' + include 'MultiTest-jvm', 'MultiTest-js' + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-js/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-js' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-jvm/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-jvm' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + + importProject() + + Assert.assertEquals("MultiTest_myMain", facetSettings("MultiTest-jvm_myMain").implementedModuleName) + Assert.assertEquals("MultiTest_myTest", facetSettings("MultiTest-jvm_myTest").implementedModuleName) + Assert.assertEquals("MultiTest_myMain", facetSettings("MultiTest-js_myMain").implementedModuleName) + Assert.assertEquals("MultiTest_myTest", facetSettings("MultiTest-js_myTest").implementedModuleName) + } + private fun assertAllModulesConfigured() { runReadAction { for (moduleGroup in ModuleSourceRootMap(myProject).groupByBaseModules(myProject.allModules())) { diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt index d3a23e559ebd2..68fe7afa20c2c 100644 --- a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt @@ -149,6 +149,8 @@ class KotlinFacetSettings { LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> CommonCompilerArguments.ERROR } } + + var implementedModuleName: String? = null } fun TargetPlatformKind<*>.createCompilerArguments(init: CommonCompilerArguments.() -> Unit = {}): CommonCompilerArguments { diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt index 1e0f72fe5e323..439a14b3a2943 100644 --- a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt @@ -22,6 +22,7 @@ import com.intellij.util.xmlb.SkipDefaultsSerializationFilter import com.intellij.util.xmlb.XmlSerializer import org.jdom.DataConversionException import org.jdom.Element +import org.jdom.Text import org.jetbrains.kotlin.cli.common.arguments.* import org.jetbrains.kotlin.load.java.JvmAbi import java.lang.reflect.Modifier @@ -95,6 +96,9 @@ private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings { element.getAttributeValue("useProjectSettings")?.let { useProjectSettings = it.toBoolean() } val platformName = element.getAttributeValue("platform") val platformKind = TargetPlatformKind.ALL_PLATFORMS.firstOrNull { it.description == platformName } ?: TargetPlatformKind.DEFAULT_PLATFORM + element.getChild("implements")?.let { + implementedModuleName = (element.content.firstOrNull() as? Text)?.textTrim + } element.getChild("compilerSettings")?.let { compilerSettings = CompilerSettings() XmlSerializer.deserializeInto(compilerSettings!!, it) @@ -224,6 +228,9 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) { if (!useProjectSettings) { element.setAttribute("useProjectSettings", useProjectSettings.toString()) } + implementedModuleName?.let { + element.addContent(Element("implements").apply { addContent(it) }) + } compilerSettings?.let { copyBean(it) }?.let { it.convertPathsToSystemIndependent() buildChildElement(element, "compilerSettings", it, filter) diff --git a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt index 4480264d034d4..3e90366eca194 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt @@ -47,6 +47,7 @@ interface KotlinGradleModel : Serializable { val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet val coroutines: String? val platformPluginId: String? + val implements: String? val transitiveCommonDependencies: Set } @@ -55,6 +56,7 @@ class KotlinGradleModelImpl( override val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet, override val coroutines: String?, override val platformPluginId: String?, + override val implements: String?, override val transitiveCommonDependencies: Set ) : KotlinGradleModel @@ -191,13 +193,15 @@ class KotlinGradleModelBuilder : ModelBuilderService { } val platform = platformPluginId ?: pluginToPlatform.entries.singleOrNull { project.plugins.findPlugin(it.key) != null }?.value - val transitiveCommon = getImplements(project)?.let { transitiveCommonDependencies(it) } ?: emptySet() + val implementedProject = getImplements(project) + val transitiveCommon = implementedProject?.let { transitiveCommonDependencies(it) } ?: emptySet() return KotlinGradleModelImpl( kotlinPluginId != null || platformPluginId != null, compilerArgumentsBySourceSet, getCoroutines(project), platform, + implementedProject?.pathOrName(), transitiveCommon ) }