Kotlin Facet: Do not import dependency-based classpath from Gradle

JPS obtains it automatically via imported module dependencies

 #KT-18475 Fixed
This commit is contained in:
Alexey Sedunov
2017-06-19 16:37:20 +03:00
parent f68689b5e4
commit 3460a31e3a
4 changed files with 149 additions and 33 deletions
@@ -21,18 +21,30 @@ 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.File
import java.io.Serializable
import java.lang.Exception
import java.lang.reflect.InvocationTargetException
import java.util.*
import kotlin.collections.HashSet
typealias CompilerArgumentsBySourceSet = Map<String, List<String>>
interface ArgsInfo : Serializable {
val currentArguments: List<String>
val defaultArguments: List<String>
val dependencyClasspath: List<String>
}
class ArgsInfoImpl(
override val currentArguments: List<String>,
override val defaultArguments: List<String>,
override val dependencyClasspath: List<String>
) : ArgsInfo
typealias CompilerArgumentsBySourceSet = Map<String, ArgsInfo>
interface KotlinGradleModel : Serializable {
val hasKotlinPlugin: Boolean
val currentCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val defaultCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
val coroutines: String?
val platformPluginId: String?
val transitiveCommonDependencies: Set<String>
@@ -40,8 +52,7 @@ interface KotlinGradleModel : Serializable {
class KotlinGradleModelImpl(
override val hasKotlinPlugin: Boolean,
override val currentCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet,
override val defaultCompilerArgumentsBySourceSet: CompilerArgumentsBySourceSet,
override val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet,
override val coroutines: String?,
override val platformPluginId: String?,
override val transitiveCommonDependencies: Set<String>
@@ -58,6 +69,7 @@ class KotlinGradleModelBuilder : ModelBuilderService {
)
val kotlinPluginIds = listOf("kotlin", "kotlin2js", "kotlin-android")
private val kotlinPlatformCommonPluginId = "kotlin-platform-common"
private val ABSTRACT_KOTLIN_COMPILE_CLASS = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile"
}
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
@@ -104,25 +116,36 @@ class KotlinGradleModelBuilder : ModelBuilderService {
}
@Suppress("UNCHECKED_CAST")
private fun collectCompilerArguments(
compileTask: Task,
methodName: String,
argumentsBySourceSet: MutableMap<String, List<String>>
) {
val taskClass = compileTask::class.java
val sourceSetName = try {
taskClass.methods.firstOrNull { it.name.startsWith("getSourceSetName") && it.parameterCount == 0 }?.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>
private fun Task.getCompilerArguments(methodName: String): List<String> {
return try {
javaClass.getDeclaredMethod(methodName).invoke(this) as List<String>
}
catch (e : NoSuchMethodException) {
// No argument accessor method is available
emptyList()
}
}
private fun Task.getDependencyClasspath(): List<String> {
try {
val abstractKotlinCompileClass = javaClass.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE_CLASS)
val getCompileClasspath = abstractKotlinCompileClass.getDeclaredMethod("getCompileClasspath").apply { isAccessible = true }
@Suppress("UNCHECKED_CAST")
return (getCompileClasspath.invoke(this) as Collection<File>).map { it.path }
}
catch(e: ClassNotFoundException) {
// Leave arguments unchanged
}
catch (e: NoSuchMethodException) {
// Leave arguments unchanged
}
catch (e: InvocationTargetException) {
// We can safely ignore this exception here as getCompileClasspath() gets called again at a later time
// Leave arguments unchanged
}
return emptyList()
}
private fun getCoroutines(project: Project): String? {
val kotlinExtension = project.extensions.findByName("kotlin") ?: return null
val experimentalExtension = try {
@@ -140,18 +163,28 @@ class KotlinGradleModelBuilder : ModelBuilderService {
}
}
private fun Task.getSourceSetName(): String {
return try {
javaClass.methods.firstOrNull { it.name.startsWith("getSourceSetName") && it.parameterCount == 0 }?.invoke(this) as? String
} catch (e : InvocationTargetException) {
null // can be thrown if property is not initialized yet
} ?: "main"
}
override fun buildAll(modelName: String?, project: Project): KotlinGradleModelImpl {
val kotlinPluginId = kotlinPluginIds.singleOrNull { project.plugins.findPlugin(it) != null }
val platformPluginId = platformPluginIds.singleOrNull { project.plugins.findPlugin(it) != null }
val currentCompilerArgumentsBySourceSet = LinkedHashMap<String, List<String>>()
val defaultCompilerArgumentsBySourceSet = LinkedHashMap<String, List<String>>()
val compilerArgumentsBySourceSet = LinkedHashMap<String, ArgsInfo>()
project.getAllTasks(false)[project]?.forEach { compileTask ->
if (compileTask.javaClass.name !in kotlinCompileTaskClasses) return@forEach
collectCompilerArguments(compileTask, "getSerializedCompilerArguments", currentCompilerArgumentsBySourceSet)
collectCompilerArguments(compileTask, "getDefaultSerializedCompilerArguments", defaultCompilerArgumentsBySourceSet)
val sourceSetName = compileTask.getSourceSetName()
val currentArguments = compileTask.getCompilerArguments("getSerializedCompilerArguments")
val defaultArguments = compileTask.getCompilerArguments("getDefaultSerializedCompilerArguments")
val dependencyClasspath = compileTask.getDependencyClasspath()
compilerArgumentsBySourceSet[sourceSetName] = ArgsInfoImpl(currentArguments, defaultArguments, dependencyClasspath)
}
val platform = platformPluginId ?: pluginToPlatform.entries.singleOrNull { project.plugins.findPlugin(it.key) != null }?.value
@@ -159,8 +192,7 @@ class KotlinGradleModelBuilder : ModelBuilderService {
return KotlinGradleModelImpl(
kotlinPluginId != null || platformPluginId != null,
currentCompilerArgumentsBySourceSet,
defaultCompilerArgumentsBySourceSet,
compilerArgumentsBySourceSet,
getCoroutines(project),
platform,
transitiveCommon
@@ -37,10 +37,8 @@ import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.ge
var DataNode<ModuleData>.hasKotlinPlugin
by NotNullableUserDataProperty(Key.create<Boolean>("HAS_KOTLIN_PLUGIN"), false)
var DataNode<ModuleData>.currentCompilerArgumentsBySourceSet
var DataNode<ModuleData>.compilerArgumentsBySourceSet
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"))
var DataNode<ModuleData>.platformPluginId
@@ -71,8 +69,7 @@ class KotlinGradleProjectResolverExtension : AbstractProjectResolverExtension()
}
ideModule.hasKotlinPlugin = gradleModel.hasKotlinPlugin
ideModule.currentCompilerArgumentsBySourceSet = gradleModel.currentCompilerArgumentsBySourceSet
ideModule.defaultCompilerArgumentsBySourceSet = gradleModel.defaultCompilerArgumentsBySourceSet
ideModule.compilerArgumentsBySourceSet = gradleModel.compilerArgumentsBySourceSet
ideModule.coroutines = gradleModel.coroutines
ideModule.platformPluginId = gradleModel.platformPluginId
@@ -25,6 +25,8 @@ import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjec
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.CoroutineSupport
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
@@ -36,6 +38,7 @@ import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedKotlinStdlibVersionByModuleData
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import java.io.File
import java.util.*
interface GradleProjectImportHandler {
@@ -130,15 +133,29 @@ private fun configureFacetByGradleModule(
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)
val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName)
if (argsInfo != null) {
val currentCompilerArguments = argsInfo.currentArguments
val defaultCompilerArguments = argsInfo.defaultArguments
val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
if (currentCompilerArguments.isNotEmpty()) {
parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet)
}
adjustClasspath(kotlinFacet, dependencyClasspath)
}
return kotlinFacet
}
private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List<String>) {
if (dependencyClasspath.isEmpty()) return
val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return
val fullClasspath = arguments.classpath?.split(File.pathSeparator) ?: emptyList()
if (fullClasspath.isEmpty()) return
val newClasspath = fullClasspath - dependencyClasspath
arguments.classpath = if (newClasspath.isNotEmpty()) newClasspath.joinToString(File.pathSeparator) else null
}
private val gradlePropertyFiles = listOf("local.properties", "gradle.properties")
private fun findKotlinCoroutinesProperty(project: Project): String {
@@ -1009,4 +1009,74 @@ class GradleFacetImportTest : GradleImportingTestCase() {
Assert.assertNull(KotlinFacet.get(getModule("m1_main")))
Assert.assertNull(KotlinFacet.get(getModule("m1_test")))
}
@Test
fun testClasspathWithDependenciesImport() {
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'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0"
compile "org.apache.logging.log4j:log4j-core:2.7"
}
compileKotlin {
kotlinOptions.freeCompilerArgs += ["-cp", "tmp.jar"]
}
""")
importProject()
with (facetSettings) {
Assert.assertEquals("tmp.jar", (compilerArguments as K2JVMCompilerArguments).classpath)
}
}
@Test
fun testDependenciesClasspathImport() {
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'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0"
compile "org.apache.logging.log4j:log4j-core:2.7"
}
""")
importProject()
with (facetSettings) {
Assert.assertEquals(null, (compilerArguments as K2JVMCompilerArguments).classpath)
}
}
}