Skip to content

Commit

Permalink
Kotlin Facet: Support "implements" relation between modules
Browse files Browse the repository at this point in the history
 #KT-17593 Fixed
  • Loading branch information
asedunov committed Sep 8, 2017
1 parent 8ebc766 commit bdad58c
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var DataNode<ModuleData>.coroutines
by UserDataProperty(Key.create<String>("KOTLIN_COROUTINES"))
var DataNode<ModuleData>.platformPluginId
by UserDataProperty(Key.create<String>("PLATFORM_PLUGIN_ID"))
var DataNode<ModuleData>.implementedModule
by UserDataProperty(Key.create<DataNode<ModuleData>>("IMPLEMENTS"))

class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() {
override fun getToolingExtensionsClasses(): Set<Class<out Any>> {
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -205,9 +205,18 @@ private fun configureFacetByGradleModule(
adjustClasspath(kotlinFacet, dependencyClasspath)
}

kotlinFacet.configuration.settings.implementedModuleName = getImplementedModuleName(moduleNode, sourceSetName)

return kotlinFacet
}

private fun getImplementedModuleName(moduleNode: DataNode<ModuleData>, 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<String>) {
if (dependencyClasspath.isEmpty()) return
val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface KotlinGradleModel : Serializable {
val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val coroutines: String?
val platformPluginId: String?
val implements: String?
val transitiveCommonDependencies: Set<String>
}

Expand All @@ -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<String>
) : KotlinGradleModel

Expand Down Expand Up @@ -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
)
}
Expand Down

0 comments on commit bdad58c

Please sign in to comment.