From 26537cd8fcf0cad0576e68e31e35029eb0b50755 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 7 Mar 2017 16:11:24 +0300 Subject: [PATCH] Kotlin Facet: Distinguish compiler arguments specified for different source sets #KT-16698 Fixed --- .../src/KotlinGradleModelBuilder.kt | 71 +++++-- .../KotlinGradleProjectResolverExtension.kt | 14 +- .../KotlinGradleSourceSetDataService.kt | 11 +- .../gradle/GradleFacetImportTest.kt | 186 +++++++++++++++++- 4 files changed, 253 insertions(+), 29 deletions(-) diff --git a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt index e010dcdbb52..0241927936f 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinGradleModelBuilder.kt @@ -17,29 +17,35 @@ package org.jetbrains.kotlin.gradle import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.artifacts.ProjectDependency import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder import org.jetbrains.plugins.gradle.tooling.ModelBuilderService import java.io.Serializable import java.lang.Exception +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method + +typealias CompilerArgumentsBySourceSet = Map> interface KotlinGradleModel : Serializable { val implements: String? - val currentCompilerArguments: List? - val defaultCompilerArguments: List? + val currentCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet + val defaultCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet val coroutines: String? } class KotlinGradleModelImpl( override val implements: String?, - override val currentCompilerArguments: List?, - override val defaultCompilerArguments: List?, + override val currentCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet, + override val defaultCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet, override val coroutines: String? ) : KotlinGradleModel class KotlinGradleModelBuilder : ModelBuilderService { companion object { - val compileTasks = listOf("compileKotlin", "compileKotlin2Js") + val kotlinCompileTaskClasses = listOf("org.jetbrains.kotlin.gradle.tasks.KotlinCompile_Decorated", + "org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile_Decorated") } override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder { @@ -57,15 +63,35 @@ class KotlinGradleModelBuilder : ModelBuilderService { return null } + private fun Class<*>.findGetterMethod(name: String): Method? { + generateSequence(this) { it.superclass }.forEach { + try { + return it.getDeclaredMethod(name) + } + catch(e: Exception) { + // Check next super class + } + } + return null + } + @Suppress("UNCHECKED_CAST") - private fun getCompilerArguments(project: Project, methodName: String): List? { - val compileTask = compileTasks.mapNotNull { project.getTasksByName(it, false).firstOrNull() }.firstOrNull() ?: return null + private fun collectCompilerArguments( + compileTask: Task, + methodName: String, + argumentsBySourceSet: MutableMap> + ) { val taskClass = compileTask.javaClass - return try { - taskClass.getDeclaredMethod(methodName).invoke(compileTask) as List + val sourceSetName = try { + taskClass.findGetterMethod("getSourceSetName\$kotlin_gradle_plugin")?.invoke(compileTask) as? String + } catch (e : InvocationTargetException) { + null // can be thrown if property is not initialized yet + } ?: "main" + try { + argumentsBySourceSet[sourceSetName] = taskClass.getDeclaredMethod(methodName).invoke(compileTask) as List } catch (e : NoSuchMethodException) { - null + // No argument accessor method is available } } @@ -86,11 +112,22 @@ class KotlinGradleModelBuilder : ModelBuilderService { } } - override fun buildAll(modelName: String?, project: Project) = - KotlinGradleModelImpl( - getImplements(project), - getCompilerArguments(project, "getSerializedCompilerArguments"), - getCompilerArguments(project, "getDefaultSerializedCompilerArguments"), - getCoroutines(project) - ) + override fun buildAll(modelName: String?, project: Project): KotlinGradleModelImpl { + val currentCompilerArgumentsBySourceSet = LinkedHashMap>() + val defaultCompilerArgumentsBySourceSet = LinkedHashMap>() + + project.getAllTasks(false)[project]?.forEach { compileTask -> + if (compileTask.javaClass.name !in kotlinCompileTaskClasses) return@forEach + + collectCompilerArguments(compileTask, "getSerializedCompilerArguments", currentCompilerArgumentsBySourceSet) + collectCompilerArguments(compileTask, "getDefaultSerializedCompilerArguments", defaultCompilerArgumentsBySourceSet) + } + + return KotlinGradleModelImpl( + getImplements(project), + currentCompilerArgumentsBySourceSet, + defaultCompilerArgumentsBySourceSet, + getCoroutines(project) + ) + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt index cda9c71eaf9..f67ff7b0c3f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectResolverExtension.kt @@ -25,6 +25,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.util.Key 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.psi.UserDataProperty @@ -33,9 +34,12 @@ import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.getModuleId -var DataNode.currentCompilerArguments by UserDataProperty(Key.create>("CURRENT_COMPILER_ARGUMENTS")) -var DataNode.defaultCompilerArguments by UserDataProperty(Key.create>("DEFAULT_COMPILER_ARGUMENTS")) -var DataNode.coroutines by UserDataProperty(Key.create("KOTLIN_COROUTINES")) +var DataNode.currentCompilerArgumentsBySourceSet + by UserDataProperty(Key.create("CURRENT_COMPILER_ARGUMENTS")) +var DataNode.defaultCompilerArgumentsBySourceSet + by UserDataProperty(Key.create("DEFAULT_COMPILER_ARGUMENTS")) +var DataNode.coroutines + by UserDataProperty(Key.create("KOTLIN_COROUTINES")) class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() { override fun getToolingExtensionsClasses(): Set> { @@ -61,8 +65,8 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() } } - ideModule.currentCompilerArguments = gradleModel.currentCompilerArguments - ideModule.defaultCompilerArguments = gradleModel.defaultCompilerArguments + ideModule.currentCompilerArgumentsBySourceSet = gradleModel.currentCompilerArgumentsBySourceSet + ideModule.defaultCompilerArgumentsBySourceSet = gradleModel.defaultCompilerArgumentsBySourceSet ideModule.coroutines = gradleModel.coroutines super.populateModuleDependencies(gradleModule, ideModule, ideProject) diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt index bd8b563d14d..4973e1d9ea2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt @@ -62,7 +62,7 @@ class KotlinGradleSourceSetDataService : AbstractProjectDataService): TargetPla private fun configureFacetByGradleModule( moduleNode: DataNode, + sourceSetNode: DataNode?, ideModule: Module, modelsProvider: IdeModifiableModelsProvider ): KotlinFacet? { @@ -121,8 +122,10 @@ private fun configureFacetByGradleModule( val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false) kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider) - val currentCompilerArguments = moduleNode.currentCompilerArguments - val defaultCompilerArguments = moduleNode.defaultCompilerArguments ?: emptyList() + val sourceSetName = sourceSetNode?.data?.id?.let { it.substring(it.lastIndexOf(':') + 1) } ?: "main" + + val currentCompilerArguments = moduleNode.currentCompilerArgumentsBySourceSet?.get(sourceSetName) + val defaultCompilerArguments = moduleNode.defaultCompilerArgumentsBySourceSet?.get(sourceSetName) ?: emptyList() if (currentCompilerArguments != null) { parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt index fb90dad3f19..668109a4eaa 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt @@ -27,8 +27,13 @@ import org.junit.Assert import org.junit.Test class GradleFacetImportTest : GradleImportingTestCase() { + private fun facetSettings(moduleName: String) = KotlinFacet.get(getModule(moduleName))!!.configuration.settings + private val facetSettings: KotlinFacetSettings - get() = KotlinFacet.get(getModule("project_main"))!!.configuration.settings + get() = facetSettings("project_main") + + private val testFacetSettings: KotlinFacetSettings + get() = facetSettings("project_test") @Test fun testJvmImport() { @@ -59,6 +64,12 @@ class GradleFacetImportTest : GradleImportingTestCase() { kotlinOptions.jvmTarget = "1.7" kotlinOptions.freeCompilerArgs = ["-Xsingle-module", "-Xdump-declarations-to", "tmp"] } + + compileTestKotlin { + kotlinOptions.jvmTarget = "1.6" + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-Xdump-declarations-to", "tmpTest"] + } """) importProject() @@ -70,6 +81,83 @@ class GradleFacetImportTest : GradleImportingTestCase() { Assert.assertEquals("-Xdump-declarations-to tmp -Xsingle-module", compilerSettings!!.additionalArguments) } + with (testFacetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + Assert.assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals("-Xdump-declarations-to tmpTest", + compilerSettings!!.additionalArguments) + } + } + + @Test + fun testJvmImportWithCustomSourceSets() { + createProjectSubFile("build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + 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' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + + compileMyMainKotlin { + kotlinOptions.jvmTarget = "1.7" + kotlinOptions.freeCompilerArgs = ["-Xsingle-module", "-Xdump-declarations-to", "tmp"] + } + + compileMyTestKotlin { + kotlinOptions.jvmTarget = "1.6" + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-Xdump-declarations-to", "tmpTest"] + } + """) + importProject() + + with (facetSettings("project_myMain")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], targetPlatformKind) + Assert.assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals("-Xdump-declarations-to tmp -Xsingle-module", + compilerSettings!!.additionalArguments) + } + with (facetSettings("project_myTest")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + Assert.assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals("-Xdump-declarations-to tmpTest", + compilerSettings!!.additionalArguments) + } } @Test @@ -218,7 +306,12 @@ class GradleFacetImportTest : GradleImportingTestCase() { compileKotlin2Js { kotlinOptions.sourceMap = true - kotlinOptions.freeCompilerArgs = ["-module-kind", "plain"] + kotlinOptions.freeCompilerArgs = ["-module-kind", "plain", "-main", "callMain"] + } + + compileTestKotlin2Js { + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-module-kind", "umd", "-main", "callTest"] } """) importProject() @@ -231,7 +324,94 @@ class GradleFacetImportTest : GradleImportingTestCase() { Assert.assertEquals(true, sourceMap) Assert.assertEquals("plain", moduleKind) } - Assert.assertEquals("-version", + Assert.assertEquals("-main callMain", + compilerSettings!!.additionalArguments) + } + + with (testFacetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(false, sourceMap) + Assert.assertEquals("umd", moduleKind) + } + Assert.assertEquals("-main callTest", + compilerSettings!!.additionalArguments) + } + } + + @Test + fun testJsImportWithCustomSourceSets() { + createProjectSubFile("build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + 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: 'kotlin2js' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + } + + compileMyMainKotlin2Js { + kotlinOptions.sourceMap = true + kotlinOptions.freeCompilerArgs = ["-module-kind", "plain", "-main", "callMain"] + } + + compileMyTestKotlin2Js { + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-module-kind", "umd", "-main", "callTest"] + } + """) + importProject() + + with (facetSettings("project_myMain")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(true, sourceMap) + Assert.assertEquals("plain", moduleKind) + } + Assert.assertEquals("-main callMain", + compilerSettings!!.additionalArguments) + } + + with (facetSettings("project_myTest")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(false, sourceMap) + Assert.assertEquals("umd", moduleKind) + } + Assert.assertEquals("-main callTest", compilerSettings!!.additionalArguments) } }