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:
@@ -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
|
||||
|
||||
+2
-5
@@ -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
|
||||
|
||||
|
||||
+21
-4
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user