a2a8335bab
#KT-16926 Fixed
174 lines
7.2 KiB
Kotlin
174 lines
7.2 KiB
Kotlin
/*
|
|
* Copyright 2010-2016 JetBrains s.r.o.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
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.File
|
|
import java.io.Serializable
|
|
import java.lang.Exception
|
|
import java.lang.reflect.InvocationTargetException
|
|
import java.util.*
|
|
|
|
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 compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet
|
|
val coroutines: String?
|
|
val platformPluginId: String?
|
|
val implements: String?
|
|
}
|
|
|
|
class KotlinGradleModelImpl(
|
|
override val hasKotlinPlugin: Boolean,
|
|
override val compilerArgumentsBySourceSet: CompilerArgumentsBySourceSet,
|
|
override val coroutines: String?,
|
|
override val platformPluginId: String?,
|
|
override val implements: String?
|
|
) : KotlinGradleModel
|
|
|
|
abstract class AbstractKotlinGradleModelBuilder : ModelBuilderService {
|
|
companion object {
|
|
val kotlinCompileTaskClasses = listOf("org.jetbrains.kotlin.gradle.tasks.KotlinCompile_Decorated",
|
|
"org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile_Decorated")
|
|
val platformPluginIds = listOf("kotlin-platform-jvm", "kotlin-platform-js", "kotlin-platform-common")
|
|
val pluginToPlatform = linkedMapOf(
|
|
"kotlin" to "kotlin-platform-jvm",
|
|
"kotlin2js" to "kotlin-platform-js"
|
|
)
|
|
val kotlinPluginIds = listOf("kotlin", "kotlin2js", "kotlin-android")
|
|
val ABSTRACT_KOTLIN_COMPILE_CLASS = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile"
|
|
|
|
fun Task.getSourceSetName(): String {
|
|
return try {
|
|
javaClass.methods.firstOrNull { it.name.startsWith("getSourceSetName") && it.parameterTypes.isEmpty() }?.invoke(this) as? String
|
|
} catch (e : InvocationTargetException) {
|
|
null // can be thrown if property is not initialized yet
|
|
} ?: "main"
|
|
}
|
|
}
|
|
}
|
|
|
|
class KotlinGradleModelBuilder : AbstractKotlinGradleModelBuilder() {
|
|
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
|
|
return ErrorMessageBuilder.create(project, e, "Gradle import errors").withDescription("Unable to build Kotlin project configuration")
|
|
}
|
|
|
|
override fun canBuild(modelName: String?): Boolean = modelName == KotlinGradleModel::class.java.name
|
|
|
|
private fun getImplements(project: Project): Project? {
|
|
val implementsConfiguration = project.configurations.run { findByName("implement") } ?: return null
|
|
val implementsProjectDependency = implementsConfiguration.dependencies.filterIsInstance<ProjectDependency>().firstOrNull()
|
|
return implementsProjectDependency?.dependencyProject
|
|
}
|
|
|
|
// see GradleProjectResolverUtil.getModuleId() in IDEA codebase
|
|
private fun Project.pathOrName() = if (path == ":") name else path
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
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 {
|
|
kotlinExtension::class.java.getMethod("getExperimental").invoke(kotlinExtension)
|
|
}
|
|
catch(e: NoSuchMethodException) {
|
|
return null
|
|
}
|
|
|
|
return try {
|
|
experimentalExtension::class.java.getMethod("getCoroutines").invoke(experimentalExtension)?.toString()
|
|
}
|
|
catch(e: NoSuchMethodException) {
|
|
null
|
|
}
|
|
}
|
|
|
|
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 compilerArgumentsBySourceSet = LinkedHashMap<String, ArgsInfo>()
|
|
|
|
project.getAllTasks(false)[project]?.forEach { compileTask ->
|
|
if (compileTask.javaClass.name !in kotlinCompileTaskClasses) return@forEach
|
|
|
|
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
|
|
val implementedProject = getImplements(project)
|
|
|
|
return KotlinGradleModelImpl(
|
|
kotlinPluginId != null || platformPluginId != null,
|
|
compilerArgumentsBySourceSet,
|
|
getCoroutines(project),
|
|
platform,
|
|
implementedProject?.pathOrName()
|
|
)
|
|
}
|
|
}
|