Kotlin Facet: Distinguish compiler arguments specified for different source sets

#KT-16698 Fixed
This commit is contained in:
Alexey Sedunov
2017-03-07 16:11:24 +03:00
parent 0e583aa929
commit 26537cd8fc
4 changed files with 253 additions and 29 deletions
@@ -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<String, List<String>>
interface KotlinGradleModel : Serializable {
val implements: String?
val currentCompilerArguments: List<String>?
val defaultCompilerArguments: List<String>?
val currentCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val defaultCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val coroutines: String?
}
class KotlinGradleModelImpl(
override val implements: String?,
override val currentCompilerArguments: List<String>?,
override val defaultCompilerArguments: List<String>?,
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<String>? {
val compileTask = compileTasks.mapNotNull { project.getTasksByName(it, false).firstOrNull() }.firstOrNull() ?: return null
private fun collectCompilerArguments(
compileTask: Task,
methodName: String,
argumentsBySourceSet: MutableMap<String, List<String>>
) {
val taskClass = compileTask.javaClass
return try {
taskClass.getDeclaredMethod(methodName).invoke(compileTask) as List<String>
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<String>
}
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<String, List<String>>()
val defaultCompilerArgumentsBySourceSet = LinkedHashMap<String, List<String>>()
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)
)
}
}
@@ -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<ModuleData>.currentCompilerArguments by UserDataProperty(Key.create<List<String>>("CURRENT_COMPILER_ARGUMENTS"))
var DataNode<ModuleData>.defaultCompilerArguments by UserDataProperty(Key.create<List<String>>("DEFAULT_COMPILER_ARGUMENTS"))
var DataNode<ModuleData>.coroutines by UserDataProperty(Key.create<String>("KOTLIN_COROUTINES"))
var DataNode<ModuleData>.currentCompilerArgumentsBySourceSet
by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("CURRENT_COMPILER_ARGUMENTS"))
var DataNode<ModuleData>.defaultCompilerArgumentsBySourceSet
by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("DEFAULT_COMPILER_ARGUMENTS"))
var DataNode<ModuleData>.coroutines
by UserDataProperty(Key.create<String>("KOTLIN_COROUTINES"))
class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension() {
override fun getToolingExtensionsClasses(): Set<Class<out Any>> {
@@ -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)
@@ -62,7 +62,7 @@ class KotlinGradleSourceSetDataService : AbstractProjectDataService<GradleSource
val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
val moduleNode = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: continue
val kotlinFacet = configureFacetByGradleModule(moduleNode, ideModule, modelsProvider) ?: continue
val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetNode, ideModule, modelsProvider) ?: continue
GradleProjectImportHandler.getInstances(project).forEach { it.importBySourceSet(kotlinFacet, sourceSetNode) }
}
}
@@ -83,7 +83,7 @@ class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Vo
val moduleData = moduleNode.data
val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
val kotlinFacet = configureFacetByGradleModule(moduleNode, ideModule, modelsProvider) ?: continue
val kotlinFacet = configureFacetByGradleModule(moduleNode, null, ideModule, modelsProvider) ?: continue
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
}
}
@@ -108,6 +108,7 @@ private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPla
private fun configureFacetByGradleModule(
moduleNode: DataNode<ModuleData>,
sourceSetNode: DataNode<GradleSourceSetData>?,
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)
}
@@ -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)
}
}