Misc: Fix compatibility issues with AS3.3C4
This commit is contained in:
+36
-7
@@ -31,6 +31,7 @@ import com.intellij.openapi.roots.OrderRootType
|
|||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryImpl
|
import com.intellij.openapi.roots.impl.libraries.LibraryImpl
|
||||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.util.PathUtil
|
import com.intellij.util.PathUtil
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
@@ -40,6 +41,8 @@ import org.jetbrains.kotlin.config.JvmTarget
|
|||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||||
|
import org.jetbrains.kotlin.gradle.ArgsInfo
|
||||||
|
import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
|
||||||
import org.jetbrains.kotlin.idea.facet.*
|
import org.jetbrains.kotlin.idea.facet.*
|
||||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||||
@@ -48,11 +51,18 @@ import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
|||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
||||||
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
||||||
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
||||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
var Module.compilerArgumentsBySourceSet
|
||||||
|
by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("CURRENT_COMPILER_ARGUMENTS"))
|
||||||
|
|
||||||
|
var Module.sourceSetName
|
||||||
|
by UserDataProperty(Key.create<String>("SOURCE_SET_NAME"))
|
||||||
|
|
||||||
interface GradleProjectImportHandler {
|
interface GradleProjectImportHandler {
|
||||||
companion object : ProjectExtensionDescriptor<GradleProjectImportHandler>(
|
companion object : ProjectExtensionDescriptor<GradleProjectImportHandler>(
|
||||||
"org.jetbrains.kotlin.gradleProjectImportHandler",
|
"org.jetbrains.kotlin.gradleProjectImportHandler",
|
||||||
@@ -173,6 +183,16 @@ private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPla
|
|||||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused") // Used in the Android plugin
|
||||||
|
fun configureFacetByGradleModule(
|
||||||
|
moduleNode: DataNode<ModuleData>,
|
||||||
|
sourceSetName: String?,
|
||||||
|
ideModule: Module,
|
||||||
|
modelsProvider: IdeModifiableModelsProvider
|
||||||
|
): KotlinFacet? {
|
||||||
|
return configureFacetByGradleModule(ideModule, modelsProvider, moduleNode, null, sourceSetName)
|
||||||
|
}
|
||||||
|
|
||||||
fun configureFacetByGradleModule(
|
fun configureFacetByGradleModule(
|
||||||
ideModule: Module,
|
ideModule: Module,
|
||||||
modelsProvider: IdeModifiableModelsProvider,
|
modelsProvider: IdeModifiableModelsProvider,
|
||||||
@@ -202,15 +222,14 @@ fun configureFacetByGradleModule(
|
|||||||
val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
|
val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
|
||||||
kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
|
kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
|
||||||
|
|
||||||
|
if (sourceSetNode == null) {
|
||||||
|
ideModule.compilerArgumentsBySourceSet = moduleNode.compilerArgumentsBySourceSet
|
||||||
|
ideModule.sourceSetName = sourceSetName
|
||||||
|
}
|
||||||
|
|
||||||
val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main")
|
val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main")
|
||||||
if (argsInfo != null) {
|
if (argsInfo != null) {
|
||||||
val currentCompilerArguments = argsInfo.currentArguments
|
configureFacetByCompilerArguments(kotlinFacet, argsInfo, modelsProvider)
|
||||||
val defaultCompilerArguments = argsInfo.defaultArguments
|
|
||||||
val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
|
|
||||||
if (currentCompilerArguments.isNotEmpty()) {
|
|
||||||
parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider)
|
|
||||||
}
|
|
||||||
adjustClasspath(kotlinFacet, dependencyClasspath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
with(kotlinFacet.configuration.settings) {
|
with(kotlinFacet.configuration.settings) {
|
||||||
@@ -228,6 +247,16 @@ fun configureFacetByGradleModule(
|
|||||||
return kotlinFacet
|
return kotlinFacet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun configureFacetByCompilerArguments(kotlinFacet: KotlinFacet, argsInfo: ArgsInfo, modelsProvider: IdeModifiableModelsProvider?) {
|
||||||
|
val currentCompilerArguments = argsInfo.currentArguments
|
||||||
|
val defaultCompilerArguments = argsInfo.defaultArguments
|
||||||
|
val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
|
||||||
|
if (currentCompilerArguments.isNotEmpty()) {
|
||||||
|
parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider)
|
||||||
|
}
|
||||||
|
adjustClasspath(kotlinFacet, dependencyClasspath)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? {
|
private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? {
|
||||||
if (platformKind !== TargetPlatformKind.JavaScript) return null
|
if (platformKind !== TargetPlatformKind.JavaScript) return null
|
||||||
val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null
|
val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null
|
||||||
|
|||||||
-284
@@ -1,284 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2017 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.idea.configuration
|
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.externalSystem.model.DataNode
|
|
||||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
|
||||||
import com.intellij.openapi.externalSystem.model.project.LibraryData
|
|
||||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
|
||||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
|
||||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.isQualifiedModuleNamesEnabled
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.OrderRootType
|
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryImpl
|
|
||||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.util.PathUtil
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
|
||||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
|
||||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
|
||||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
|
||||||
import org.jetbrains.kotlin.gradle.ArgsInfo
|
|
||||||
import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
|
|
||||||
import org.jetbrains.kotlin.idea.facet.*
|
|
||||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
|
||||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
|
||||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
|
||||||
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
|
||||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
|
||||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
var Module.compilerArgumentsBySourceSet
|
|
||||||
by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("CURRENT_COMPILER_ARGUMENTS"))
|
|
||||||
|
|
||||||
var Module.sourceSetName
|
|
||||||
by UserDataProperty(Key.create<String>("SOURCE_SET_NAME"))
|
|
||||||
|
|
||||||
interface GradleProjectImportHandler {
|
|
||||||
companion object : ProjectExtensionDescriptor<GradleProjectImportHandler>(
|
|
||||||
"org.jetbrains.kotlin.gradleProjectImportHandler",
|
|
||||||
GradleProjectImportHandler::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
fun importBySourceSet(facet: KotlinFacet, sourceSetNode: DataNode<GradleSourceSetData>)
|
|
||||||
fun importByModule(facet: KotlinFacet, moduleNode: DataNode<ModuleData>)
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinGradleSourceSetDataService : AbstractProjectDataService<GradleSourceSetData, Void>() {
|
|
||||||
override fun getTargetDataKey() = GradleSourceSetData.KEY
|
|
||||||
|
|
||||||
override fun postProcess(
|
|
||||||
toImport: Collection<DataNode<GradleSourceSetData>>,
|
|
||||||
projectData: ProjectData?,
|
|
||||||
project: Project,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
for (sourceSetNode in toImport) {
|
|
||||||
val sourceSetData = sourceSetNode.data
|
|
||||||
val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
|
|
||||||
|
|
||||||
val moduleNode = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: continue
|
|
||||||
val sourceSetName = sourceSetNode.data.id.let { it.substring(it.lastIndexOf(':') + 1) }
|
|
||||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue
|
|
||||||
GradleProjectImportHandler.getInstances(project).forEach { it.importBySourceSet(kotlinFacet, sourceSetNode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Void>() {
|
|
||||||
override fun getTargetDataKey() = ProjectKeys.MODULE
|
|
||||||
|
|
||||||
override fun postProcess(
|
|
||||||
toImport: MutableCollection<DataNode<ModuleData>>,
|
|
||||||
projectData: ProjectData?,
|
|
||||||
project: Project,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
for (moduleNode in toImport) {
|
|
||||||
// If source sets are present, configure facets in the their modules
|
|
||||||
if (ExternalSystemApiUtil.getChildren(moduleNode, GradleSourceSetData.KEY).isNotEmpty()) continue
|
|
||||||
|
|
||||||
val moduleData = moduleNode.data
|
|
||||||
val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
|
|
||||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, null, ideModule, modelsProvider) ?: continue
|
|
||||||
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinGradleLibraryDataService : AbstractProjectDataService<LibraryData, Void>() {
|
|
||||||
override fun getTargetDataKey() = ProjectKeys.LIBRARY
|
|
||||||
|
|
||||||
override fun postProcess(
|
|
||||||
toImport: MutableCollection<DataNode<LibraryData>>,
|
|
||||||
projectData: ProjectData?,
|
|
||||||
project: Project,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
if (toImport.isEmpty()) return
|
|
||||||
val projectDataNode = toImport.first().parent!!
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val moduleDataNodes = projectDataNode.children.filter { it.data is ModuleData } as List<DataNode<ModuleData>>
|
|
||||||
val anyNonJvmModules = moduleDataNodes.any { detectPlatformByPlugin(it)?.takeIf { it !is TargetPlatformKind.Jvm } != null }
|
|
||||||
for (libraryDataNode in toImport) {
|
|
||||||
val ideLibrary = modelsProvider.findIdeLibrary(libraryDataNode.data) ?: continue
|
|
||||||
|
|
||||||
val modifiableModel = modelsProvider.getModifiableLibraryModel(ideLibrary) as LibraryEx.ModifiableModelEx
|
|
||||||
if (anyNonJvmModules || ideLibrary.name?.looksAsNonJvmLibraryName() == true) {
|
|
||||||
detectLibraryKind(modifiableModel.getFiles(OrderRootType.CLASSES))?.let { modifiableModel.kind = it }
|
|
||||||
} else if (ideLibrary is LibraryImpl && (ideLibrary.kind === JSLibraryKind || ideLibrary.kind === CommonLibraryKind)) {
|
|
||||||
resetLibraryKind(modifiableModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.looksAsNonJvmLibraryName() = nonJvmSuffixes.any { it in this }
|
|
||||||
|
|
||||||
private fun resetLibraryKind(modifiableModel: LibraryEx.ModifiableModelEx) {
|
|
||||||
try {
|
|
||||||
val cls = LibraryImpl::class.java
|
|
||||||
// Don't use name-based lookup because field names are scrambled in IDEA Ultimate
|
|
||||||
for (field in cls.declaredFields) {
|
|
||||||
if (field.type == PersistentLibraryKind::class.java) {
|
|
||||||
field.isAccessible = true
|
|
||||||
field.set(modifiableModel, null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG.info("Could not find field of type PersistentLibraryKind in LibraryImpl.class")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
LOG.info("Failed to reset library kind", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val LOG = Logger.getInstance(KotlinGradleLibraryDataService::class.java)
|
|
||||||
|
|
||||||
val nonJvmSuffixes = listOf("-common", "-js", "-native", "-kjsm")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
|
||||||
return when (moduleNode.platformPluginId) {
|
|
||||||
"kotlin-platform-jvm" -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
|
||||||
"kotlin-platform-js" -> TargetPlatformKind.JavaScript
|
|
||||||
"kotlin-platform-common" -> TargetPlatformKind.Common
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
|
||||||
val detectedPlatforms =
|
|
||||||
mavenLibraryIdToPlatform.entries
|
|
||||||
.filter { moduleNode.getResolvedVersionByModuleData(KOTLIN_GROUP_ID, listOf(it.key)) != null }
|
|
||||||
.map { it.value }.distinct()
|
|
||||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureFacetByGradleModule(
|
|
||||||
moduleNode: DataNode<ModuleData>,
|
|
||||||
sourceSetName: String?,
|
|
||||||
ideModule: Module,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
): KotlinFacet? {
|
|
||||||
if (!moduleNode.isResolved) return null
|
|
||||||
|
|
||||||
if (!moduleNode.hasKotlinPlugin) {
|
|
||||||
val facetModel = modelsProvider.getModifiableFacetModel(ideModule)
|
|
||||||
val facet = facetModel.getFacetByType(KotlinFacetType.TYPE_ID)
|
|
||||||
if (facet != null) {
|
|
||||||
facetModel.removeFacet(facet)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val compilerVersion = moduleNode.findAll(BuildScriptClasspathData.KEY).firstOrNull()?.data?.let(::findKotlinPluginVersion)
|
|
||||||
?: return null
|
|
||||||
val platformKind = detectPlatformByPlugin(moduleNode) ?: detectPlatformByLibrary(moduleNode)
|
|
||||||
|
|
||||||
val coroutinesProperty = CoroutineSupport.byCompilerArgument(
|
|
||||||
moduleNode.coroutines ?: findKotlinCoroutinesProperty(ideModule.project)
|
|
||||||
)
|
|
||||||
|
|
||||||
val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
|
|
||||||
kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
|
|
||||||
|
|
||||||
ideModule.compilerArgumentsBySourceSet = moduleNode.compilerArgumentsBySourceSet
|
|
||||||
ideModule.sourceSetName = sourceSetName
|
|
||||||
|
|
||||||
val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main")
|
|
||||||
if (argsInfo != null) {
|
|
||||||
configureFacetByCompilerArguments(kotlinFacet, argsInfo, modelsProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
with(kotlinFacet.configuration.settings) {
|
|
||||||
val sourceSetNameForImplementedModules = if (ideModule.getBuildSystemType() == AndroidGradle) null else sourceSetName
|
|
||||||
implementedModuleNames = getImplementedModuleNames(moduleNode, sourceSetNameForImplementedModules, ideModule.project)
|
|
||||||
|
|
||||||
productionOutputPath = getExplicitOutputPath(moduleNode, platformKind, "main")
|
|
||||||
testOutputPath = getExplicitOutputPath(moduleNode, platformKind, "test")
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinFacet.noVersionAutoAdvance()
|
|
||||||
|
|
||||||
if (platformKind != null && platformKind !is TargetPlatformKind.Jvm) {
|
|
||||||
migrateNonJvmSourceFolders(modelsProvider.getModifiableRootModel(ideModule))
|
|
||||||
}
|
|
||||||
|
|
||||||
return kotlinFacet
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureFacetByCompilerArguments(kotlinFacet: KotlinFacet, argsInfo: ArgsInfo, modelsProvider: IdeModifiableModelsProvider?) {
|
|
||||||
val currentCompilerArguments = argsInfo.currentArguments
|
|
||||||
val defaultCompilerArguments = argsInfo.defaultArguments
|
|
||||||
val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
|
|
||||||
if (currentCompilerArguments.isNotEmpty()) {
|
|
||||||
parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider)
|
|
||||||
}
|
|
||||||
adjustClasspath(kotlinFacet, dependencyClasspath)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getImplementedModuleNames(moduleNode: DataNode<ModuleData>, sourceSetName: String?, project: Project): List<String> {
|
|
||||||
val baseModuleNames = moduleNode.implementedModuleNames
|
|
||||||
if (baseModuleNames.isEmpty() || sourceSetName == null) return baseModuleNames
|
|
||||||
val delimiter = if(isQualifiedModuleNamesEnabled(project)) "." else "_"
|
|
||||||
return baseModuleNames.map { "$it$delimiter$sourceSetName" }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? {
|
|
||||||
if (platformKind !== TargetPlatformKind.JavaScript) return null
|
|
||||||
val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null
|
|
||||||
return K2JSCompilerArguments().apply { parseCommandLineArguments(k2jsArgumentList, this) }.outputFile
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
for (propertyFileName in gradlePropertyFiles) {
|
|
||||||
val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue
|
|
||||||
val properties = Properties()
|
|
||||||
properties.load(propertyFile.inputStream)
|
|
||||||
properties.getProperty("kotlin.coroutines")?.let { return it }
|
|
||||||
}
|
|
||||||
|
|
||||||
return CoroutineSupport.getCompilerArgument(LanguageFeature.Coroutines.defaultState)
|
|
||||||
}
|
|
||||||
-279
@@ -1,279 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2017 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.idea.configuration
|
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.externalSystem.model.DataNode
|
|
||||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
|
||||||
import com.intellij.openapi.externalSystem.model.project.LibraryData
|
|
||||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
|
||||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
|
||||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.isQualifiedModuleNamesEnabled
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.OrderRootType
|
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryImpl
|
|
||||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.util.PathUtil
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
|
||||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
|
||||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
|
||||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
|
||||||
import org.jetbrains.kotlin.gradle.ArgsInfo
|
|
||||||
import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
|
|
||||||
import org.jetbrains.kotlin.idea.facet.*
|
|
||||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
|
||||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
|
||||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
|
||||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
|
||||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
var Module.compilerArgumentsBySourceSet
|
|
||||||
by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("CURRENT_COMPILER_ARGUMENTS"))
|
|
||||||
|
|
||||||
var Module.sourceSetName
|
|
||||||
by UserDataProperty(Key.create<String>("SOURCE_SET_NAME"))
|
|
||||||
|
|
||||||
interface GradleProjectImportHandler {
|
|
||||||
companion object : ProjectExtensionDescriptor<GradleProjectImportHandler>(
|
|
||||||
"org.jetbrains.kotlin.gradleProjectImportHandler",
|
|
||||||
GradleProjectImportHandler::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
fun importBySourceSet(facet: KotlinFacet, sourceSetNode: DataNode<GradleSourceSetData>)
|
|
||||||
fun importByModule(facet: KotlinFacet, moduleNode: DataNode<ModuleData>)
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinGradleSourceSetDataService : AbstractProjectDataService<GradleSourceSetData, Void>() {
|
|
||||||
override fun getTargetDataKey() = GradleSourceSetData.KEY
|
|
||||||
|
|
||||||
override fun postProcess(
|
|
||||||
toImport: Collection<DataNode<GradleSourceSetData>>,
|
|
||||||
projectData: ProjectData?,
|
|
||||||
project: Project,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
for (sourceSetNode in toImport) {
|
|
||||||
val sourceSetData = sourceSetNode.data
|
|
||||||
val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
|
|
||||||
|
|
||||||
val moduleNode = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: continue
|
|
||||||
val sourceSetName = sourceSetNode.data.id.let { it.substring(it.lastIndexOf(':') + 1) }
|
|
||||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue
|
|
||||||
GradleProjectImportHandler.getInstances(project).forEach { it.importBySourceSet(kotlinFacet, sourceSetNode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Void>() {
|
|
||||||
override fun getTargetDataKey() = ProjectKeys.MODULE
|
|
||||||
|
|
||||||
override fun postProcess(
|
|
||||||
toImport: MutableCollection<DataNode<ModuleData>>,
|
|
||||||
projectData: ProjectData?,
|
|
||||||
project: Project,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
for (moduleNode in toImport) {
|
|
||||||
// If source sets are present, configure facets in the their modules
|
|
||||||
if (ExternalSystemApiUtil.getChildren(moduleNode, GradleSourceSetData.KEY).isNotEmpty()) continue
|
|
||||||
|
|
||||||
val moduleData = moduleNode.data
|
|
||||||
val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
|
|
||||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, null, ideModule, modelsProvider) ?: continue
|
|
||||||
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinGradleLibraryDataService : AbstractProjectDataService<LibraryData, Void>() {
|
|
||||||
override fun getTargetDataKey() = ProjectKeys.LIBRARY
|
|
||||||
|
|
||||||
override fun postProcess(
|
|
||||||
toImport: MutableCollection<DataNode<LibraryData>>,
|
|
||||||
projectData: ProjectData?,
|
|
||||||
project: Project,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
if (toImport.isEmpty()) return
|
|
||||||
val projectDataNode = toImport.first().parent!!
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val moduleDataNodes = projectDataNode.children.filter { it.data is ModuleData } as List<DataNode<ModuleData>>
|
|
||||||
val anyNonJvmModules = moduleDataNodes.any { detectPlatformByPlugin(it)?.takeIf { it !is TargetPlatformKind.Jvm } != null }
|
|
||||||
for (libraryDataNode in toImport) {
|
|
||||||
val ideLibrary = modelsProvider.findIdeLibrary(libraryDataNode.data) ?: continue
|
|
||||||
|
|
||||||
val modifiableModel = modelsProvider.getModifiableLibraryModel(ideLibrary) as LibraryEx.ModifiableModelEx
|
|
||||||
if (anyNonJvmModules || ideLibrary.name?.looksAsNonJvmLibraryName() == true) {
|
|
||||||
detectLibraryKind(modifiableModel.getFiles(OrderRootType.CLASSES))?.let { modifiableModel.kind = it }
|
|
||||||
} else if (ideLibrary is LibraryImpl && (ideLibrary.kind === JSLibraryKind || ideLibrary.kind === CommonLibraryKind)) {
|
|
||||||
resetLibraryKind(modifiableModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.looksAsNonJvmLibraryName() = nonJvmSuffixes.any { it in this }
|
|
||||||
|
|
||||||
private fun resetLibraryKind(modifiableModel: LibraryEx.ModifiableModelEx) {
|
|
||||||
try {
|
|
||||||
val cls = LibraryImpl::class.java
|
|
||||||
// Don't use name-based lookup because field names are scrambled in IDEA Ultimate
|
|
||||||
for (field in cls.declaredFields) {
|
|
||||||
if (field.type == PersistentLibraryKind::class.java) {
|
|
||||||
field.isAccessible = true
|
|
||||||
field.set(modifiableModel, null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG.info("Could not find field of type PersistentLibraryKind in LibraryImpl.class")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
LOG.info("Failed to reset library kind", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val LOG = Logger.getInstance(KotlinGradleLibraryDataService::class.java)
|
|
||||||
|
|
||||||
val nonJvmSuffixes = listOf("-common", "-js", "-native", "-kjsm")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
|
||||||
return when (moduleNode.platformPluginId) {
|
|
||||||
"kotlin-platform-jvm" -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
|
||||||
"kotlin-platform-js" -> TargetPlatformKind.JavaScript
|
|
||||||
"kotlin-platform-common" -> TargetPlatformKind.Common
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
|
||||||
val detectedPlatforms =
|
|
||||||
mavenLibraryIdToPlatform.entries
|
|
||||||
.filter { moduleNode.getResolvedVersionByModuleData(KOTLIN_GROUP_ID, listOf(it.key)) != null }
|
|
||||||
.map { it.value }.distinct()
|
|
||||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureFacetByGradleModule(
|
|
||||||
moduleNode: DataNode<ModuleData>,
|
|
||||||
sourceSetName: String?,
|
|
||||||
ideModule: Module,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
): KotlinFacet? {
|
|
||||||
if (!moduleNode.isResolved) return null
|
|
||||||
|
|
||||||
if (!moduleNode.hasKotlinPlugin) {
|
|
||||||
val facetModel = modelsProvider.getModifiableFacetModel(ideModule)
|
|
||||||
val facet = facetModel.getFacetByType(KotlinFacetType.TYPE_ID)
|
|
||||||
if (facet != null) {
|
|
||||||
facetModel.removeFacet(facet)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val compilerVersion = moduleNode.findAll(BuildScriptClasspathData.KEY).firstOrNull()?.data?.let(::findKotlinPluginVersion)
|
|
||||||
?: return null
|
|
||||||
val platformKind = detectPlatformByPlugin(moduleNode) ?: detectPlatformByLibrary(moduleNode)
|
|
||||||
|
|
||||||
val coroutinesProperty = CoroutineSupport.byCompilerArgument(
|
|
||||||
moduleNode.coroutines ?: findKotlinCoroutinesProperty(ideModule.project)
|
|
||||||
)
|
|
||||||
|
|
||||||
val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
|
|
||||||
kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
|
|
||||||
|
|
||||||
ideModule.compilerArgumentsBySourceSet = moduleNode.compilerArgumentsBySourceSet
|
|
||||||
ideModule.sourceSetName = sourceSetName
|
|
||||||
|
|
||||||
val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main")
|
|
||||||
if (argsInfo != null) {
|
|
||||||
configureFacetByCompilerArguments(kotlinFacet, argsInfo, modelsProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
with(kotlinFacet.configuration.settings) {
|
|
||||||
val sourceSetNameForImplementedModules = if (ideModule.getBuildSystemType() == AndroidGradle) null else sourceSetName
|
|
||||||
implementedModuleNames = getImplementedModuleNames(moduleNode, sourceSetNameForImplementedModules, ideModule.project)
|
|
||||||
|
|
||||||
productionOutputPath = getExplicitOutputPath(moduleNode, platformKind, "main")
|
|
||||||
testOutputPath = getExplicitOutputPath(moduleNode, platformKind, "test")
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinFacet.noVersionAutoAdvance()
|
|
||||||
|
|
||||||
return kotlinFacet
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureFacetByCompilerArguments(kotlinFacet: KotlinFacet, argsInfo: ArgsInfo, modelsProvider: IdeModifiableModelsProvider?) {
|
|
||||||
val currentCompilerArguments = argsInfo.currentArguments
|
|
||||||
val defaultCompilerArguments = argsInfo.defaultArguments
|
|
||||||
val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
|
|
||||||
if (currentCompilerArguments.isNotEmpty()) {
|
|
||||||
parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider)
|
|
||||||
}
|
|
||||||
adjustClasspath(kotlinFacet, dependencyClasspath)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getImplementedModuleNames(moduleNode: DataNode<ModuleData>, sourceSetName: String?, project: Project): List<String> {
|
|
||||||
val baseModuleNames = moduleNode.implementedModuleNames
|
|
||||||
if (baseModuleNames.isEmpty() || sourceSetName == null) return baseModuleNames
|
|
||||||
val delimiter = if(isQualifiedModuleNamesEnabled(project)) "." else "_"
|
|
||||||
return baseModuleNames.map { "$it$delimiter$sourceSetName" }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? {
|
|
||||||
if (platformKind !== TargetPlatformKind.JavaScript) return null
|
|
||||||
val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null
|
|
||||||
return K2JSCompilerArguments().apply { parseCommandLineArguments(k2jsArgumentList, this) }.outputFile
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
for (propertyFileName in gradlePropertyFiles) {
|
|
||||||
val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue
|
|
||||||
val properties = Properties()
|
|
||||||
properties.load(propertyFile.inputStream)
|
|
||||||
properties.getProperty("kotlin.coroutines")?.let { return it }
|
|
||||||
}
|
|
||||||
|
|
||||||
return CoroutineSupport.getCompilerArgument(LanguageFeature.Coroutines.defaultState)
|
|
||||||
}
|
|
||||||
+8
-3
@@ -96,14 +96,19 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
|||||||
dialog.show()
|
dialog.show()
|
||||||
if (!dialog.isOK) return
|
if (!dialog.isOK) return
|
||||||
|
|
||||||
project.executeCommand("Configure Kotlin") {
|
val collector = configureSilently(project, dialog.modulesToConfigure, dialog.kotlinVersion)
|
||||||
|
collector.showNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun configureSilently(project: Project, modules: List<Module>, version: String): NotificationMessageCollector {
|
||||||
|
return project.executeCommand("Configure Kotlin") {
|
||||||
val collector = createConfigureKotlinNotificationCollector(project)
|
val collector = createConfigureKotlinNotificationCollector(project)
|
||||||
val changedFiles = configureWithVersion(project, dialog.modulesToConfigure, dialog.kotlinVersion, collector)
|
val changedFiles = configureWithVersion(project, modules, version, collector)
|
||||||
|
|
||||||
for (file in changedFiles) {
|
for (file in changedFiles) {
|
||||||
OpenFileAction.openFile(file.virtualFile, project)
|
OpenFileAction.openFile(file.virtualFile, project)
|
||||||
}
|
}
|
||||||
collector.showNotification()
|
collector
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-403
@@ -1,403 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
||||||
* that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.configuration
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.CodeInsightUtilCore
|
|
||||||
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
|
|
||||||
import com.intellij.ide.actions.OpenFileAction
|
|
||||||
import com.intellij.openapi.extensions.Extensions
|
|
||||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
|
||||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.ModuleUtil
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.projectRoots.Sdk
|
|
||||||
import com.intellij.openapi.roots.DependencyScope
|
|
||||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
|
||||||
import com.intellij.openapi.roots.ModuleRootManager
|
|
||||||
import com.intellij.openapi.ui.Messages
|
|
||||||
import com.intellij.openapi.vfs.VfsUtil
|
|
||||||
import com.intellij.openapi.vfs.WritingAccessProvider
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.PsiManager
|
|
||||||
import com.intellij.util.PathUtil
|
|
||||||
import org.jetbrains.kotlin.config.ApiVersion
|
|
||||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
|
||||||
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
|
|
||||||
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
|
||||||
|
|
||||||
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
|
||||||
val module = moduleSourceRootGroup.baseModule
|
|
||||||
if (!isApplicable(module)) {
|
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) {
|
|
||||||
return ConfigureKotlinStatus.CONFIGURED
|
|
||||||
}
|
|
||||||
|
|
||||||
val buildFiles = runReadAction {
|
|
||||||
listOf(
|
|
||||||
module.getBuildScriptPsiFile(),
|
|
||||||
module.project.getTopLevelBuildScriptPsiFile()
|
|
||||||
).filterNotNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildFiles.isEmpty()) {
|
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildFiles.none { it.isConfiguredByAnyGradleConfigurator() }) {
|
|
||||||
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConfigureKotlinStatus.BROKEN
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiFile.isConfiguredByAnyGradleConfigurator(): Boolean {
|
|
||||||
return Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
|
|
||||||
.filterIsInstance<KotlinWithGradleConfigurator>()
|
|
||||||
.any { it.isFileConfigured(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun isApplicable(module: Module): Boolean =
|
|
||||||
module.getBuildSystemType() == Gradle
|
|
||||||
|
|
||||||
protected open fun getMinimumSupportedVersion() = "1.0.0"
|
|
||||||
|
|
||||||
protected fun PsiFile.isKtDsl() = this is KtFile
|
|
||||||
|
|
||||||
private fun isFileConfigured(buildScript: PsiFile): Boolean = with(getManipulator(buildScript)) {
|
|
||||||
isConfiguredWithOldSyntax(kotlinPluginName) || isConfigured(getKotlinPluginExpression(buildScript.isKtDsl()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmSuppressWildcards
|
|
||||||
override fun configure(project: Project, excludeModules: Collection<Module>) {
|
|
||||||
val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion())
|
|
||||||
|
|
||||||
dialog.show()
|
|
||||||
if (!dialog.isOK) return
|
|
||||||
|
|
||||||
val collector = configureSilently(project, dialog.modulesToConfigure, dialog.kotlinVersion)
|
|
||||||
collector.showNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureSilently(project: Project, modules: List<Module>, version: String): NotificationMessageCollector {
|
|
||||||
return project.executeCommand("Configure Kotlin") {
|
|
||||||
val collector = createConfigureKotlinNotificationCollector(project)
|
|
||||||
val changedFiles = configureWithVersion(project, modules, version, collector)
|
|
||||||
|
|
||||||
for (file in changedFiles) {
|
|
||||||
OpenFileAction.openFile(file.virtualFile, project)
|
|
||||||
}
|
|
||||||
collector
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureWithVersion(
|
|
||||||
project: Project,
|
|
||||||
modulesToConfigure: List<Module>,
|
|
||||||
kotlinVersion: String,
|
|
||||||
collector: NotificationMessageCollector
|
|
||||||
): HashSet<PsiFile> {
|
|
||||||
val filesToOpen = HashSet<PsiFile>()
|
|
||||||
val buildScript = project.getTopLevelBuildScriptPsiFile()
|
|
||||||
if (buildScript != null && canConfigureFile(buildScript)) {
|
|
||||||
val isModified = configureBuildScript(buildScript, true, kotlinVersion, collector)
|
|
||||||
if (isModified) {
|
|
||||||
filesToOpen.add(buildScript)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (module in modulesToConfigure) {
|
|
||||||
val file = module.getBuildScriptPsiFile()
|
|
||||||
if (file != null && canConfigureFile(file)) {
|
|
||||||
configureModule(module, file, false, kotlinVersion, collector, filesToOpen)
|
|
||||||
} else {
|
|
||||||
showErrorMessage(project, "Cannot find build.gradle file for module " + module.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filesToOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun configureModule(
|
|
||||||
module: Module,
|
|
||||||
file: PsiFile,
|
|
||||||
isTopLevelProjectFile: Boolean,
|
|
||||||
version: String,
|
|
||||||
collector: NotificationMessageCollector,
|
|
||||||
filesToOpen: MutableCollection<PsiFile>
|
|
||||||
) {
|
|
||||||
val isModified = configureBuildScript(file, isTopLevelProjectFile, version, collector)
|
|
||||||
if (isModified) {
|
|
||||||
filesToOpen.add(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun configureModuleBuildScript(file: PsiFile, version: String): Boolean {
|
|
||||||
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
|
|
||||||
val jvmTarget = getJvmTarget(sdk, version)
|
|
||||||
return getManipulator(file).configureModuleBuildScript(
|
|
||||||
kotlinPluginName,
|
|
||||||
getKotlinPluginExpression(file.isKtDsl()),
|
|
||||||
getStdlibArtifactName(sdk, version),
|
|
||||||
version,
|
|
||||||
jvmTarget
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun getStdlibArtifactName(sdk: Sdk?, version: String) = getStdlibArtifactId(sdk, version)
|
|
||||||
|
|
||||||
protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null
|
|
||||||
|
|
||||||
protected abstract val kotlinPluginName: String
|
|
||||||
protected abstract fun getKotlinPluginExpression(forKotlinDsl: Boolean): String
|
|
||||||
|
|
||||||
protected open fun addElementsToFile(
|
|
||||||
file: PsiFile,
|
|
||||||
isTopLevelProjectFile: Boolean,
|
|
||||||
version: String
|
|
||||||
): Boolean {
|
|
||||||
if (!isTopLevelProjectFile) {
|
|
||||||
var wasModified = getManipulator(file).configureProjectBuildScript(kotlinPluginName, version)
|
|
||||||
wasModified = wasModified or configureModuleBuildScript(file, version)
|
|
||||||
return wasModified
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun configureBuildScript(
|
|
||||||
file: PsiFile,
|
|
||||||
isTopLevelProjectFile: Boolean,
|
|
||||||
version: String,
|
|
||||||
collector: NotificationMessageCollector
|
|
||||||
): Boolean {
|
|
||||||
val isModified = file.project.executeWriteCommand("Configure ${file.name}", null) {
|
|
||||||
val isModified = addElementsToFile(file, isTopLevelProjectFile, version)
|
|
||||||
|
|
||||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file)
|
|
||||||
isModified
|
|
||||||
}
|
|
||||||
|
|
||||||
val virtualFile = file.virtualFile
|
|
||||||
if (virtualFile != null && isModified) {
|
|
||||||
collector.addMessage(virtualFile.path + " was modified")
|
|
||||||
}
|
|
||||||
return isModified
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateLanguageVersion(
|
|
||||||
module: Module,
|
|
||||||
languageVersion: String?,
|
|
||||||
apiVersion: String?,
|
|
||||||
requiredStdlibVersion: ApiVersion,
|
|
||||||
forTests: Boolean
|
|
||||||
) {
|
|
||||||
val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion ->
|
|
||||||
runtimeVersion < requiredStdlibVersion
|
|
||||||
} ?: false
|
|
||||||
|
|
||||||
if (runtimeUpdateRequired) {
|
|
||||||
Messages.showErrorDialog(
|
|
||||||
module.project,
|
|
||||||
"This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " +
|
|
||||||
"Please update the version in your build script.",
|
|
||||||
"Update Language Version"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = changeLanguageVersion(module, languageVersion, apiVersion, forTests)
|
|
||||||
|
|
||||||
element?.let {
|
|
||||||
OpenFileDescriptor(module.project, it.containingFile.virtualFile, it.textRange.startOffset).navigate(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
|
|
||||||
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
|
|
||||||
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
|
|
||||||
|
|
||||||
if (runtimeUpdateRequired) {
|
|
||||||
Messages.showErrorDialog(
|
|
||||||
module.project,
|
|
||||||
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
|
|
||||||
"Please update the version in your build script.",
|
|
||||||
ChangeCoroutineSupportFix.getFixText(state)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = changeCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state))
|
|
||||||
if (element != null) {
|
|
||||||
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addLibraryDependency(
|
|
||||||
module: Module,
|
|
||||||
element: PsiElement,
|
|
||||||
library: ExternalLibraryDescriptor,
|
|
||||||
libraryJarDescriptors: List<LibraryJarDescriptor>
|
|
||||||
) {
|
|
||||||
val scope = OrderEntryFix.suggestScopeByLocation(module, element)
|
|
||||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(module, scope, library)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getManipulator(file: PsiFile, preferNewSyntax: Boolean = true): GradleBuildScriptManipulator<*> = when (file) {
|
|
||||||
is KtFile -> KotlinBuildScriptManipulator(file, preferNewSyntax)
|
|
||||||
is GroovyFile -> GroovyBuildScriptManipulator(file, preferNewSyntax)
|
|
||||||
else -> error("Unknown build script file type (${file::class.qualifiedName})!")
|
|
||||||
}
|
|
||||||
|
|
||||||
val GROUP_ID = "org.jetbrains.kotlin"
|
|
||||||
val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
|
|
||||||
|
|
||||||
val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
|
|
||||||
|
|
||||||
private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
|
|
||||||
private val KOTLIN_SETTINGS_SCRIPT_NAME = "settings.gradle.kts"
|
|
||||||
|
|
||||||
fun getGroovyDependencySnippet(artifactName: String, scope: String, withVersion: Boolean) =
|
|
||||||
"$scope \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\""
|
|
||||||
|
|
||||||
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
|
|
||||||
|
|
||||||
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
|
|
||||||
val buildScript = module.getBuildScriptPsiFile() ?: return
|
|
||||||
if (!canConfigureFile(buildScript)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
getManipulator(buildScript)
|
|
||||||
.addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor, module.getBuildSystemType() == AndroidGradle)
|
|
||||||
|
|
||||||
buildScript.virtualFile?.let {
|
|
||||||
createConfigureKotlinNotificationCollector(buildScript.project)
|
|
||||||
.addMessage(it.path + " was modified")
|
|
||||||
.showNotification()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? = changeBuildGradle(module) {
|
|
||||||
getManipulator(it).changeCoroutineConfiguration(coroutineOption)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) =
|
|
||||||
changeBuildGradle(module) { buildScriptFile ->
|
|
||||||
val manipulator = getManipulator(buildScriptFile)
|
|
||||||
var result: PsiElement? = null
|
|
||||||
if (languageVersion != null) {
|
|
||||||
result = manipulator.changeLanguageVersion(languageVersion, forTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (apiVersion != null) {
|
|
||||||
result = manipulator.changeApiVersion(apiVersion, forTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? {
|
|
||||||
val buildScriptFile = module.getBuildScriptPsiFile()
|
|
||||||
if (buildScriptFile != null && canConfigureFile(buildScriptFile)) {
|
|
||||||
return buildScriptFile.project.executeWriteCommand("Change build.gradle configuration", null) {
|
|
||||||
body(buildScriptFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKotlinStdlibVersion(module: Module): String? {
|
|
||||||
return module.getBuildScriptPsiFile()?.let {
|
|
||||||
getManipulator(it).getKotlinStdlibVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
|
|
||||||
|
|
||||||
private fun Module.getBuildScriptPsiFile() =
|
|
||||||
getBuildScriptFile(GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(project)
|
|
||||||
|
|
||||||
fun Module.getBuildScriptSettingsPsiFile() =
|
|
||||||
getBuildScriptSettingsFile(GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
|
|
||||||
|
|
||||||
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let {
|
|
||||||
findBuildGradleFile(it, GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Module.getTopLevelBuildScriptSettingsPsiFile() =
|
|
||||||
ExternalSystemApiUtil.getExternalRootProjectPath(this)?.let { externalProjectPath ->
|
|
||||||
findBuildGradleFile(externalProjectPath, GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.getBuildScriptFile(vararg fileNames: String): File? {
|
|
||||||
val moduleDir = File(moduleFilePath).parent
|
|
||||||
findBuildGradleFile(moduleDir, *fileNames)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
|
|
||||||
findBuildGradleFile(root.path, *fileNames)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
|
|
||||||
findBuildGradleFile(externalProjectPath, *fileNames)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.getBuildScriptSettingsFile(vararg fileNames: String): File? {
|
|
||||||
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
|
|
||||||
return generateSequence(externalProjectPath) {
|
|
||||||
PathUtil.getParentPath(it).let { if (it.isBlank()) null else it }
|
|
||||||
}.mapNotNull {
|
|
||||||
findBuildGradleFile(it, *fileNames)
|
|
||||||
}.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findBuildGradleFile(path: String, vararg fileNames: String): File? =
|
|
||||||
fileNames.asSequence().map { File(path + "/" + it) }.firstOrNull { it.exists() }
|
|
||||||
|
|
||||||
private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let {
|
|
||||||
PsiManager.getInstance(project).findFile(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showErrorMessage(project: Project, message: String?) {
|
|
||||||
Messages.showErrorDialog(
|
|
||||||
project,
|
|
||||||
"<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
|
|
||||||
(if (message != null) message + "<br/>" else "") +
|
|
||||||
"<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
|
|
||||||
"Configure Kotlin-Gradle Plugin"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-403
@@ -1,403 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
|
||||||
* that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.configuration
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.CodeInsightUtilCore
|
|
||||||
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
|
|
||||||
import com.intellij.ide.actions.OpenFileAction
|
|
||||||
import com.intellij.openapi.extensions.Extensions
|
|
||||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
|
||||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.ModuleUtil
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.projectRoots.Sdk
|
|
||||||
import com.intellij.openapi.roots.DependencyScope
|
|
||||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
|
||||||
import com.intellij.openapi.roots.ModuleRootManager
|
|
||||||
import com.intellij.openapi.ui.Messages
|
|
||||||
import com.intellij.openapi.vfs.VfsUtil
|
|
||||||
import com.intellij.openapi.vfs.WritingAccessProvider
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.PsiManager
|
|
||||||
import com.intellij.util.PathUtil
|
|
||||||
import org.jetbrains.kotlin.config.ApiVersion
|
|
||||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
|
||||||
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
|
|
||||||
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
|
||||||
|
|
||||||
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
|
||||||
val module = moduleSourceRootGroup.baseModule
|
|
||||||
if (!isApplicable(module)) {
|
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) {
|
|
||||||
return ConfigureKotlinStatus.CONFIGURED
|
|
||||||
}
|
|
||||||
|
|
||||||
val buildFiles = runReadAction {
|
|
||||||
listOf(
|
|
||||||
module.getBuildScriptPsiFile(),
|
|
||||||
module.project.getTopLevelBuildScriptPsiFile()
|
|
||||||
).filterNotNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildFiles.isEmpty()) {
|
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildFiles.none { it.isConfiguredByAnyGradleConfigurator() }) {
|
|
||||||
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConfigureKotlinStatus.BROKEN
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiFile.isConfiguredByAnyGradleConfigurator(): Boolean {
|
|
||||||
return Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
|
|
||||||
.filterIsInstance<KotlinWithGradleConfigurator>()
|
|
||||||
.any { it.isFileConfigured(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun isApplicable(module: Module): Boolean =
|
|
||||||
module.getBuildSystemType() == Gradle
|
|
||||||
|
|
||||||
protected open fun getMinimumSupportedVersion() = "1.0.0"
|
|
||||||
|
|
||||||
protected fun PsiFile.isKtDsl() = this is KtFile
|
|
||||||
|
|
||||||
private fun isFileConfigured(buildScript: PsiFile): Boolean = with(getManipulator(buildScript)) {
|
|
||||||
isConfiguredWithOldSyntax(kotlinPluginName) || isConfigured(getKotlinPluginExpression(buildScript.isKtDsl()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmSuppressWildcards
|
|
||||||
override fun configure(project: Project, excludeModules: Collection<Module>) {
|
|
||||||
val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion())
|
|
||||||
|
|
||||||
dialog.show()
|
|
||||||
if (!dialog.isOK) return
|
|
||||||
|
|
||||||
val collector = configureSilently(project, dialog.modulesToConfigure, dialog.kotlinVersion)
|
|
||||||
collector.showNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureSilently(project: Project, modules: List<Module>, version: String): NotificationMessageCollector {
|
|
||||||
return project.executeCommand("Configure Kotlin") {
|
|
||||||
val collector = createConfigureKotlinNotificationCollector(project)
|
|
||||||
val changedFiles = configureWithVersion(project, modules, version, collector)
|
|
||||||
|
|
||||||
for (file in changedFiles) {
|
|
||||||
OpenFileAction.openFile(file.virtualFile, project)
|
|
||||||
}
|
|
||||||
collector
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configureWithVersion(
|
|
||||||
project: Project,
|
|
||||||
modulesToConfigure: List<Module>,
|
|
||||||
kotlinVersion: String,
|
|
||||||
collector: NotificationMessageCollector
|
|
||||||
): HashSet<PsiFile> {
|
|
||||||
val filesToOpen = HashSet<PsiFile>()
|
|
||||||
val buildScript = project.getTopLevelBuildScriptPsiFile()
|
|
||||||
if (buildScript != null && canConfigureFile(buildScript)) {
|
|
||||||
val isModified = configureBuildScript(buildScript, true, kotlinVersion, collector)
|
|
||||||
if (isModified) {
|
|
||||||
filesToOpen.add(buildScript)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (module in modulesToConfigure) {
|
|
||||||
val file = module.getBuildScriptPsiFile()
|
|
||||||
if (file != null && canConfigureFile(file)) {
|
|
||||||
configureModule(module, file, false, kotlinVersion, collector, filesToOpen)
|
|
||||||
} else {
|
|
||||||
showErrorMessage(project, "Cannot find build.gradle file for module " + module.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filesToOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun configureModule(
|
|
||||||
module: Module,
|
|
||||||
file: PsiFile,
|
|
||||||
isTopLevelProjectFile: Boolean,
|
|
||||||
version: String,
|
|
||||||
collector: NotificationMessageCollector,
|
|
||||||
filesToOpen: MutableCollection<PsiFile>
|
|
||||||
) {
|
|
||||||
val isModified = configureBuildScript(file, isTopLevelProjectFile, version, collector)
|
|
||||||
if (isModified) {
|
|
||||||
filesToOpen.add(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun configureModuleBuildScript(file: PsiFile, version: String): Boolean {
|
|
||||||
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
|
|
||||||
val jvmTarget = getJvmTarget(sdk, version)
|
|
||||||
return getManipulator(file).configureModuleBuildScript(
|
|
||||||
kotlinPluginName,
|
|
||||||
getKotlinPluginExpression(file.isKtDsl()),
|
|
||||||
getStdlibArtifactName(sdk, version),
|
|
||||||
version,
|
|
||||||
jvmTarget
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun getStdlibArtifactName(sdk: Sdk?, version: String) = getStdlibArtifactId(sdk, version)
|
|
||||||
|
|
||||||
protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null
|
|
||||||
|
|
||||||
protected abstract val kotlinPluginName: String
|
|
||||||
protected abstract fun getKotlinPluginExpression(forKotlinDsl: Boolean): String
|
|
||||||
|
|
||||||
protected open fun addElementsToFile(
|
|
||||||
file: PsiFile,
|
|
||||||
isTopLevelProjectFile: Boolean,
|
|
||||||
version: String
|
|
||||||
): Boolean {
|
|
||||||
if (!isTopLevelProjectFile) {
|
|
||||||
var wasModified = getManipulator(file).configureProjectBuildScript(kotlinPluginName, version)
|
|
||||||
wasModified = wasModified or configureModuleBuildScript(file, version)
|
|
||||||
return wasModified
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun configureBuildScript(
|
|
||||||
file: PsiFile,
|
|
||||||
isTopLevelProjectFile: Boolean,
|
|
||||||
version: String,
|
|
||||||
collector: NotificationMessageCollector
|
|
||||||
): Boolean {
|
|
||||||
val isModified = file.project.executeWriteCommand("Configure ${file.name}", null) {
|
|
||||||
val isModified = addElementsToFile(file, isTopLevelProjectFile, version)
|
|
||||||
|
|
||||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file)
|
|
||||||
isModified
|
|
||||||
}
|
|
||||||
|
|
||||||
val virtualFile = file.virtualFile
|
|
||||||
if (virtualFile != null && isModified) {
|
|
||||||
collector.addMessage(virtualFile.path + " was modified")
|
|
||||||
}
|
|
||||||
return isModified
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateLanguageVersion(
|
|
||||||
module: Module,
|
|
||||||
languageVersion: String?,
|
|
||||||
apiVersion: String?,
|
|
||||||
requiredStdlibVersion: ApiVersion,
|
|
||||||
forTests: Boolean
|
|
||||||
) {
|
|
||||||
val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion ->
|
|
||||||
runtimeVersion < requiredStdlibVersion
|
|
||||||
} ?: false
|
|
||||||
|
|
||||||
if (runtimeUpdateRequired) {
|
|
||||||
Messages.showErrorDialog(
|
|
||||||
module.project,
|
|
||||||
"This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " +
|
|
||||||
"Please update the version in your build script.",
|
|
||||||
"Update Language Version"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = changeLanguageVersion(module, languageVersion, apiVersion, forTests)
|
|
||||||
|
|
||||||
element?.let {
|
|
||||||
OpenFileDescriptor(module.project, it.containingFile.virtualFile, it.textRange.startOffset).navigate(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
|
|
||||||
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
|
|
||||||
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
|
|
||||||
|
|
||||||
if (runtimeUpdateRequired) {
|
|
||||||
Messages.showErrorDialog(
|
|
||||||
module.project,
|
|
||||||
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
|
|
||||||
"Please update the version in your build script.",
|
|
||||||
ChangeCoroutineSupportFix.getFixText(state)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val element = changeCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state))
|
|
||||||
if (element != null) {
|
|
||||||
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addLibraryDependency(
|
|
||||||
module: Module,
|
|
||||||
element: PsiElement,
|
|
||||||
library: ExternalLibraryDescriptor,
|
|
||||||
libraryJarDescriptors: List<LibraryJarDescriptor>
|
|
||||||
) {
|
|
||||||
val scope = OrderEntryFix.suggestScopeByLocation(module, element)
|
|
||||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(module, scope, library)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getManipulator(file: PsiFile, preferNewSyntax: Boolean = true): GradleBuildScriptManipulator<*> = when (file) {
|
|
||||||
is KtFile -> KotlinBuildScriptManipulator(file, preferNewSyntax)
|
|
||||||
is GroovyFile -> GroovyBuildScriptManipulator(file, preferNewSyntax)
|
|
||||||
else -> error("Unknown build script file type (${file::class.qualifiedName})!")
|
|
||||||
}
|
|
||||||
|
|
||||||
val GROUP_ID = "org.jetbrains.kotlin"
|
|
||||||
val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
|
|
||||||
|
|
||||||
val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
|
|
||||||
|
|
||||||
private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
|
|
||||||
private val KOTLIN_SETTINGS_SCRIPT_NAME = "settings.gradle.kts"
|
|
||||||
|
|
||||||
fun getGroovyDependencySnippet(artifactName: String, scope: String, withVersion: Boolean) =
|
|
||||||
"$scope \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\""
|
|
||||||
|
|
||||||
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
|
|
||||||
|
|
||||||
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
|
|
||||||
val buildScript = module.getBuildScriptPsiFile() ?: return
|
|
||||||
if (!canConfigureFile(buildScript)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
getManipulator(buildScript)
|
|
||||||
.addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor, module.getBuildSystemType() == AndroidGradle)
|
|
||||||
|
|
||||||
buildScript.virtualFile?.let {
|
|
||||||
createConfigureKotlinNotificationCollector(buildScript.project)
|
|
||||||
.addMessage(it.path + " was modified")
|
|
||||||
.showNotification()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? = changeBuildGradle(module) {
|
|
||||||
getManipulator(it).changeCoroutineConfiguration(coroutineOption)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) =
|
|
||||||
changeBuildGradle(module) { buildScriptFile ->
|
|
||||||
val manipulator = getManipulator(buildScriptFile)
|
|
||||||
var result: PsiElement? = null
|
|
||||||
if (languageVersion != null) {
|
|
||||||
result = manipulator.changeLanguageVersion(languageVersion, forTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (apiVersion != null) {
|
|
||||||
result = manipulator.changeApiVersion(apiVersion, forTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? {
|
|
||||||
val buildScriptFile = module.getBuildScriptPsiFile()
|
|
||||||
if (buildScriptFile != null && canConfigureFile(buildScriptFile)) {
|
|
||||||
return buildScriptFile.project.executeWriteCommand("Change build.gradle configuration", null) {
|
|
||||||
body(buildScriptFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKotlinStdlibVersion(module: Module): String? {
|
|
||||||
return module.getBuildScriptPsiFile()?.let {
|
|
||||||
getManipulator(it).getKotlinStdlibVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
|
|
||||||
|
|
||||||
private fun Module.getBuildScriptPsiFile() =
|
|
||||||
getBuildScriptFile(GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(project)
|
|
||||||
|
|
||||||
fun Module.getBuildScriptSettingsPsiFile() =
|
|
||||||
getBuildScriptSettingsFile(GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
|
|
||||||
|
|
||||||
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let {
|
|
||||||
findBuildGradleFile(it, GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Module.getTopLevelBuildScriptSettingsPsiFile() =
|
|
||||||
ExternalSystemApiUtil.getExternalRootProjectPath(this)?.let { externalProjectPath ->
|
|
||||||
findBuildGradleFile(externalProjectPath, GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.getBuildScriptFile(vararg fileNames: String): File? {
|
|
||||||
val moduleDir = File(moduleFilePath).parent
|
|
||||||
findBuildGradleFile(moduleDir, *fileNames)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
|
|
||||||
findBuildGradleFile(root.path, *fileNames)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
|
|
||||||
findBuildGradleFile(externalProjectPath, *fileNames)?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.getBuildScriptSettingsFile(vararg fileNames: String): File? {
|
|
||||||
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
|
|
||||||
return generateSequence(externalProjectPath) {
|
|
||||||
PathUtil.getParentPath(it).let { if (it.isBlank()) null else it }
|
|
||||||
}.mapNotNull {
|
|
||||||
findBuildGradleFile(it, *fileNames)
|
|
||||||
}.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findBuildGradleFile(path: String, vararg fileNames: String): File? =
|
|
||||||
fileNames.asSequence().map { File(path + "/" + it) }.firstOrNull { it.exists() }
|
|
||||||
|
|
||||||
private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let {
|
|
||||||
PsiManager.getInstance(project).findFile(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showErrorMessage(project: Project, message: String?) {
|
|
||||||
Messages.showErrorDialog(
|
|
||||||
project,
|
|
||||||
"<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
|
|
||||||
(if (message != null) message + "<br/>" else "") +
|
|
||||||
"<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
|
|
||||||
"Configure Kotlin-Gradle Plugin"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -251,7 +251,7 @@ fun parseCompilerArgumentsToFacet(
|
|||||||
arguments: List<String>,
|
arguments: List<String>,
|
||||||
defaultArguments: List<String>,
|
defaultArguments: List<String>,
|
||||||
kotlinFacet: KotlinFacet,
|
kotlinFacet: KotlinFacet,
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
modelsProvider: IdeModifiableModelsProvider?
|
||||||
) {
|
) {
|
||||||
with(kotlinFacet.configuration.settings) {
|
with(kotlinFacet.configuration.settings) {
|
||||||
val compilerArguments = this.compilerArguments ?: return
|
val compilerArguments = this.compilerArguments ?: return
|
||||||
@@ -267,7 +267,8 @@ fun parseCompilerArgumentsToFacet(
|
|||||||
// Retain only fields exposed (and not explicitly ignored) in facet configuration editor.
|
// Retain only fields exposed (and not explicitly ignored) in facet configuration editor.
|
||||||
// The rest is combined into string and stored in CompilerSettings.additionalArguments
|
// The rest is combined into string and stored in CompilerSettings.additionalArguments
|
||||||
|
|
||||||
kotlinFacet.module.configureSdkIfPossible(compilerArguments, modelsProvider)
|
if (modelsProvider != null)
|
||||||
|
kotlinFacet.module.configureSdkIfPossible(compilerArguments, modelsProvider)
|
||||||
|
|
||||||
val primaryFields = compilerArguments.primaryFields
|
val primaryFields = compilerArguments.primaryFields
|
||||||
val ignoredFields = compilerArguments.ignoredFields
|
val ignoredFields = compilerArguments.ignoredFields
|
||||||
|
|||||||
@@ -1,302 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2017 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.idea.facet
|
|
||||||
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.projectRoots.JavaSdk
|
|
||||||
import com.intellij.openapi.projectRoots.JavaSdkVersion
|
|
||||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
|
||||||
import com.intellij.openapi.roots.ModuleRootManager
|
|
||||||
import com.intellij.openapi.roots.ModuleRootModel
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
|
||||||
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
|
|
||||||
import org.jetbrains.kotlin.config.*
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
|
|
||||||
import org.jetbrains.kotlin.idea.configuration.externalCompilerVersion
|
|
||||||
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
|
||||||
import org.jetbrains.kotlin.idea.versions.*
|
|
||||||
import kotlin.reflect.KProperty1
|
|
||||||
|
|
||||||
private fun getDefaultTargetPlatform(module: Module, rootModel: ModuleRootModel?): TargetPlatformKind<*> {
|
|
||||||
for (platform in TargetPlatformKind.ALL_PLATFORMS) {
|
|
||||||
if (getRuntimeLibraryVersions(module, rootModel, platform).isNotEmpty()) {
|
|
||||||
return platform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val sdk = ((rootModel ?: ModuleRootManager.getInstance(module))).sdk
|
|
||||||
val sdkVersion = (sdk?.sdkType as? JavaSdk)?.getVersion(sdk!!)
|
|
||||||
return when {
|
|
||||||
sdkVersion == null || sdkVersion >= JavaSdkVersion.JDK_1_8 -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_8]
|
|
||||||
else -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinFacetSettings.initializeIfNeeded(
|
|
||||||
module: Module,
|
|
||||||
rootModel: ModuleRootModel?,
|
|
||||||
platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies
|
|
||||||
compilerVersion: String? = null
|
|
||||||
) {
|
|
||||||
val project = module.project
|
|
||||||
|
|
||||||
val shouldInferLanguageLevel = languageLevel == null
|
|
||||||
val shouldInferAPILevel = apiLevel == null
|
|
||||||
|
|
||||||
if (compilerSettings == null) {
|
|
||||||
compilerSettings = KotlinCompilerSettings.getInstance(project).settings
|
|
||||||
}
|
|
||||||
|
|
||||||
val commonArguments = KotlinCommonCompilerArgumentsHolder.getInstance(module.project).settings
|
|
||||||
|
|
||||||
if (compilerArguments == null) {
|
|
||||||
val targetPlatformKind = platformKind ?: getDefaultTargetPlatform(module, rootModel)
|
|
||||||
compilerArguments = targetPlatformKind.createCompilerArguments {
|
|
||||||
targetPlatformKind.getPlatformCompilerArgumentsByProject(module.project)?.let { mergeBeans(it, this) }
|
|
||||||
mergeBeans(commonArguments, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldInferLanguageLevel) {
|
|
||||||
languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null)
|
|
||||||
?: getDefaultLanguageLevel(module, compilerVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldInferAPILevel) {
|
|
||||||
apiLevel = if (useProjectSettings) {
|
|
||||||
LanguageVersion.fromVersionString(commonArguments.apiVersion) ?: languageLevel
|
|
||||||
} else {
|
|
||||||
languageLevel!!.coerceAtMost(getLibraryLanguageLevel(module, rootModel, targetPlatformKind))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TargetPlatformKind<*>.getPlatformCompilerArgumentsByProject(project: Project): CommonCompilerArguments? {
|
|
||||||
return when (this) {
|
|
||||||
is TargetPlatformKind.Jvm -> Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings
|
|
||||||
is TargetPlatformKind.JavaScript -> Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val TargetPlatformKind<*>.mavenLibraryIds: List<String>
|
|
||||||
get() = when (this) {
|
|
||||||
is TargetPlatformKind.Jvm -> listOf(
|
|
||||||
MAVEN_STDLIB_ID,
|
|
||||||
MAVEN_STDLIB_ID_JRE7,
|
|
||||||
MAVEN_STDLIB_ID_JDK7,
|
|
||||||
MAVEN_STDLIB_ID_JRE8,
|
|
||||||
MAVEN_STDLIB_ID_JDK8
|
|
||||||
)
|
|
||||||
is TargetPlatformKind.JavaScript -> listOf(MAVEN_JS_STDLIB_ID, MAVEN_OLD_JS_STDLIB_ID)
|
|
||||||
is TargetPlatformKind.Common -> listOf(MAVEN_COMMON_STDLIB_ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
val mavenLibraryIdToPlatform: Map<String, TargetPlatformKind<*>> by lazy {
|
|
||||||
TargetPlatformKind.ALL_PLATFORMS
|
|
||||||
.flatMap { platform -> platform.mavenLibraryIds.map { it to platform } }
|
|
||||||
.sortedByDescending { it.first.length }
|
|
||||||
.toMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Module.getOrCreateFacet(
|
|
||||||
modelsProvider: IdeModifiableModelsProvider,
|
|
||||||
useProjectSettings: Boolean,
|
|
||||||
commitModel: Boolean = false
|
|
||||||
): KotlinFacet {
|
|
||||||
val facetModel = modelsProvider.getModifiableFacetModel(this)
|
|
||||||
|
|
||||||
val facet = facetModel.findFacet(KotlinFacetType.TYPE_ID, KotlinFacetType.INSTANCE.defaultFacetName)
|
|
||||||
?: with(KotlinFacetType.INSTANCE) { createFacet(this@getOrCreateFacet, defaultFacetName, createDefaultConfiguration(), null) }
|
|
||||||
.apply { facetModel.addFacet(this) }
|
|
||||||
facet.configuration.settings.useProjectSettings = useProjectSettings
|
|
||||||
if (commitModel) {
|
|
||||||
runWriteAction {
|
|
||||||
facetModel.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return facet
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinFacet.configureFacet(
|
|
||||||
compilerVersion: String,
|
|
||||||
coroutineSupport: LanguageFeature.State,
|
|
||||||
platformKind: TargetPlatformKind<*>?, // if null, detect by module dependencies
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
val module = module
|
|
||||||
with(configuration.settings) {
|
|
||||||
compilerArguments = null
|
|
||||||
compilerSettings = null
|
|
||||||
initializeIfNeeded(
|
|
||||||
module,
|
|
||||||
modelsProvider.getModifiableRootModel(module),
|
|
||||||
platformKind,
|
|
||||||
compilerVersion
|
|
||||||
)
|
|
||||||
val apiLevel = apiLevel
|
|
||||||
val languageLevel = languageLevel
|
|
||||||
if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) {
|
|
||||||
this.apiLevel = languageLevel
|
|
||||||
}
|
|
||||||
this.coroutineSupport = coroutineSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
module.externalCompilerVersion = compilerVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinFacet.noVersionAutoAdvance() {
|
|
||||||
configuration.settings.compilerArguments?.let {
|
|
||||||
it.autoAdvanceLanguageVersion = false
|
|
||||||
it.autoAdvanceApiVersion = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Primary" fields are written to argument beans directly and thus not presented in the "additional arguments" string
|
|
||||||
// Update these lists when facet/project settings UI changes
|
|
||||||
val commonUIExposedFields = listOf(
|
|
||||||
CommonCompilerArguments::languageVersion.name,
|
|
||||||
CommonCompilerArguments::apiVersion.name,
|
|
||||||
CommonCompilerArguments::suppressWarnings.name,
|
|
||||||
CommonCompilerArguments::coroutinesState.name
|
|
||||||
)
|
|
||||||
private val commonUIHiddenFields = listOf(
|
|
||||||
CommonCompilerArguments::pluginClasspaths.name,
|
|
||||||
CommonCompilerArguments::pluginOptions.name
|
|
||||||
)
|
|
||||||
private val commonPrimaryFields = commonUIExposedFields + commonUIHiddenFields
|
|
||||||
|
|
||||||
private val jvmSpecificUIExposedFields = listOf(
|
|
||||||
K2JVMCompilerArguments::jvmTarget.name,
|
|
||||||
K2JVMCompilerArguments::destination.name,
|
|
||||||
K2JVMCompilerArguments::classpath.name
|
|
||||||
)
|
|
||||||
val jvmUIExposedFields = commonUIExposedFields + jvmSpecificUIExposedFields
|
|
||||||
private val jvmPrimaryFields = commonPrimaryFields + jvmSpecificUIExposedFields
|
|
||||||
|
|
||||||
private val jsSpecificUIExposedFields = listOf(
|
|
||||||
K2JSCompilerArguments::sourceMap.name,
|
|
||||||
K2JSCompilerArguments::sourceMapPrefix.name,
|
|
||||||
K2JSCompilerArguments::sourceMapEmbedSources.name,
|
|
||||||
K2JSCompilerArguments::outputPrefix.name,
|
|
||||||
K2JSCompilerArguments::outputPostfix.name,
|
|
||||||
K2JSCompilerArguments::moduleKind.name
|
|
||||||
)
|
|
||||||
val jsUIExposedFields = commonUIExposedFields + jsSpecificUIExposedFields
|
|
||||||
private val jsPrimaryFields = commonPrimaryFields + jsSpecificUIExposedFields
|
|
||||||
|
|
||||||
private val metadataSpecificUIExposedFields = listOf(
|
|
||||||
K2MetadataCompilerArguments::destination.name,
|
|
||||||
K2MetadataCompilerArguments::classpath.name
|
|
||||||
)
|
|
||||||
val metadataUIExposedFields = commonUIExposedFields + metadataSpecificUIExposedFields
|
|
||||||
private val metadataPrimaryFields = commonPrimaryFields + metadataSpecificUIExposedFields
|
|
||||||
|
|
||||||
private val CommonCompilerArguments.primaryFields: List<String>
|
|
||||||
get() = when (this) {
|
|
||||||
is K2JVMCompilerArguments -> jvmPrimaryFields
|
|
||||||
is K2JSCompilerArguments -> jsPrimaryFields
|
|
||||||
is K2MetadataCompilerArguments -> metadataPrimaryFields
|
|
||||||
else -> commonPrimaryFields
|
|
||||||
}
|
|
||||||
|
|
||||||
private val CommonCompilerArguments.ignoredFields: List<String>
|
|
||||||
get() = when (this) {
|
|
||||||
is K2JVMCompilerArguments -> listOf(K2JVMCompilerArguments::noJdk.name, K2JVMCompilerArguments::jdkHome.name)
|
|
||||||
else -> emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.configureSdkIfPossible(compilerArguments: CommonCompilerArguments, modelsProvider: IdeModifiableModelsProvider) {
|
|
||||||
val allSdks = ProjectJdkTable.getInstance().allJdks
|
|
||||||
val sdk = if (compilerArguments is K2JVMCompilerArguments) {
|
|
||||||
val jdkHome = compilerArguments.jdkHome ?: return
|
|
||||||
allSdks.firstOrNull { it.sdkType is JavaSdk && FileUtil.comparePaths(it.homePath, jdkHome) == 0 } ?: return
|
|
||||||
} else {
|
|
||||||
allSdks.firstOrNull { it.sdkType is KotlinSdkType }
|
|
||||||
?: modelsProvider
|
|
||||||
.modifiableModuleModel
|
|
||||||
.modules
|
|
||||||
.asSequence()
|
|
||||||
.mapNotNull { modelsProvider.getModifiableRootModel(it).sdk }
|
|
||||||
.firstOrNull { it.sdkType is KotlinSdkType }
|
|
||||||
?: KotlinSdkType.INSTANCE.createSdkWithUniqueName(allSdks.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
modelsProvider.getModifiableRootModel(this).sdk = sdk
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseCompilerArgumentsToFacet(
|
|
||||||
arguments: List<String>,
|
|
||||||
defaultArguments: List<String>,
|
|
||||||
kotlinFacet: KotlinFacet,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider?
|
|
||||||
) {
|
|
||||||
with(kotlinFacet.configuration.settings) {
|
|
||||||
val compilerArguments = this.compilerArguments ?: return
|
|
||||||
|
|
||||||
val defaultCompilerArguments = compilerArguments::class.java.newInstance()
|
|
||||||
parseCommandLineArguments(defaultArguments, defaultCompilerArguments)
|
|
||||||
defaultCompilerArguments.convertPathsToSystemIndependent()
|
|
||||||
|
|
||||||
parseCommandLineArguments(arguments, compilerArguments)
|
|
||||||
|
|
||||||
compilerArguments.convertPathsToSystemIndependent()
|
|
||||||
|
|
||||||
// Retain only fields exposed (and not explicitly ignored) in facet configuration editor.
|
|
||||||
// The rest is combined into string and stored in CompilerSettings.additionalArguments
|
|
||||||
|
|
||||||
if (modelsProvider != null)
|
|
||||||
kotlinFacet.module.configureSdkIfPossible(compilerArguments, modelsProvider)
|
|
||||||
|
|
||||||
val primaryFields = compilerArguments.primaryFields
|
|
||||||
val ignoredFields = compilerArguments.ignoredFields
|
|
||||||
|
|
||||||
fun exposeAsAdditionalArgument(property: KProperty1<CommonCompilerArguments, Any?>) =
|
|
||||||
property.name !in primaryFields && property.get(compilerArguments) != property.get(defaultCompilerArguments)
|
|
||||||
|
|
||||||
val additionalArgumentsString = with(compilerArguments::class.java.newInstance()) {
|
|
||||||
copyFieldsSatisfying(compilerArguments, this) { exposeAsAdditionalArgument(it) && it.name !in ignoredFields }
|
|
||||||
ArgumentUtils.convertArgumentsToStringList(this).joinToString(separator = " ") {
|
|
||||||
if (StringUtil.containsWhitespaces(it) || it.startsWith('"')) {
|
|
||||||
StringUtil.wrapWithDoubleQuote(StringUtil.escapeQuotes(it))
|
|
||||||
} else it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compilerSettings?.additionalArguments =
|
|
||||||
if (additionalArgumentsString.isNotEmpty()) additionalArgumentsString else CompilerSettings.DEFAULT_ADDITIONAL_ARGUMENTS
|
|
||||||
|
|
||||||
with(compilerArguments::class.java.newInstance()) {
|
|
||||||
copyFieldsSatisfying(this, compilerArguments) { exposeAsAdditionalArgument(it) || it.name in ignoredFields }
|
|
||||||
}
|
|
||||||
|
|
||||||
val languageLevel = languageLevel
|
|
||||||
val apiLevel = apiLevel
|
|
||||||
if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) {
|
|
||||||
this.apiLevel = languageLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMergedArguments()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,302 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2017 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.idea.facet
|
|
||||||
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.projectRoots.JavaSdk
|
|
||||||
import com.intellij.openapi.projectRoots.JavaSdkVersion
|
|
||||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
|
||||||
import com.intellij.openapi.roots.ModuleRootManager
|
|
||||||
import com.intellij.openapi.roots.ModuleRootModel
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
|
||||||
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
|
|
||||||
import org.jetbrains.kotlin.config.*
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
|
||||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
|
|
||||||
import org.jetbrains.kotlin.idea.configuration.externalCompilerVersion
|
|
||||||
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
|
||||||
import org.jetbrains.kotlin.idea.versions.*
|
|
||||||
import kotlin.reflect.KProperty1
|
|
||||||
|
|
||||||
private fun getDefaultTargetPlatform(module: Module, rootModel: ModuleRootModel?): TargetPlatformKind<*> {
|
|
||||||
for (platform in TargetPlatformKind.ALL_PLATFORMS) {
|
|
||||||
if (getRuntimeLibraryVersions(module, rootModel, platform).isNotEmpty()) {
|
|
||||||
return platform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val sdk = ((rootModel ?: ModuleRootManager.getInstance(module))).sdk
|
|
||||||
val sdkVersion = (sdk?.sdkType as? JavaSdk)?.getVersion(sdk!!)
|
|
||||||
return when {
|
|
||||||
sdkVersion == null || sdkVersion >= JavaSdkVersion.JDK_1_8 -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_8]
|
|
||||||
else -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinFacetSettings.initializeIfNeeded(
|
|
||||||
module: Module,
|
|
||||||
rootModel: ModuleRootModel?,
|
|
||||||
platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies
|
|
||||||
compilerVersion: String? = null
|
|
||||||
) {
|
|
||||||
val project = module.project
|
|
||||||
|
|
||||||
val shouldInferLanguageLevel = languageLevel == null
|
|
||||||
val shouldInferAPILevel = apiLevel == null
|
|
||||||
|
|
||||||
if (compilerSettings == null) {
|
|
||||||
compilerSettings = KotlinCompilerSettings.getInstance(project).settings
|
|
||||||
}
|
|
||||||
|
|
||||||
val commonArguments = KotlinCommonCompilerArgumentsHolder.getInstance(module.project).settings
|
|
||||||
|
|
||||||
if (compilerArguments == null) {
|
|
||||||
val targetPlatformKind = platformKind ?: getDefaultTargetPlatform(module, rootModel)
|
|
||||||
compilerArguments = targetPlatformKind.createCompilerArguments {
|
|
||||||
targetPlatformKind.getPlatformCompilerArgumentsByProject(module.project)?.let { mergeBeans(it, this) }
|
|
||||||
mergeBeans(commonArguments, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldInferLanguageLevel) {
|
|
||||||
languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null)
|
|
||||||
?: getDefaultLanguageLevel(module, compilerVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldInferAPILevel) {
|
|
||||||
apiLevel = if (useProjectSettings) {
|
|
||||||
LanguageVersion.fromVersionString(commonArguments.apiVersion) ?: languageLevel
|
|
||||||
} else {
|
|
||||||
languageLevel!!.coerceAtMost(getLibraryLanguageLevel(module, rootModel, targetPlatformKind))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TargetPlatformKind<*>.getPlatformCompilerArgumentsByProject(project: Project): CommonCompilerArguments? {
|
|
||||||
return when (this) {
|
|
||||||
is TargetPlatformKind.Jvm -> Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings
|
|
||||||
is TargetPlatformKind.JavaScript -> Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val TargetPlatformKind<*>.mavenLibraryIds: List<String>
|
|
||||||
get() = when (this) {
|
|
||||||
is TargetPlatformKind.Jvm -> listOf(
|
|
||||||
MAVEN_STDLIB_ID,
|
|
||||||
MAVEN_STDLIB_ID_JRE7,
|
|
||||||
MAVEN_STDLIB_ID_JDK7,
|
|
||||||
MAVEN_STDLIB_ID_JRE8,
|
|
||||||
MAVEN_STDLIB_ID_JDK8
|
|
||||||
)
|
|
||||||
is TargetPlatformKind.JavaScript -> listOf(MAVEN_JS_STDLIB_ID, MAVEN_OLD_JS_STDLIB_ID)
|
|
||||||
is TargetPlatformKind.Common -> listOf(MAVEN_COMMON_STDLIB_ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
val mavenLibraryIdToPlatform: Map<String, TargetPlatformKind<*>> by lazy {
|
|
||||||
TargetPlatformKind.ALL_PLATFORMS
|
|
||||||
.flatMap { platform -> platform.mavenLibraryIds.map { it to platform } }
|
|
||||||
.sortedByDescending { it.first.length }
|
|
||||||
.toMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Module.getOrCreateFacet(
|
|
||||||
modelsProvider: IdeModifiableModelsProvider,
|
|
||||||
useProjectSettings: Boolean,
|
|
||||||
commitModel: Boolean = false
|
|
||||||
): KotlinFacet {
|
|
||||||
val facetModel = modelsProvider.getModifiableFacetModel(this)
|
|
||||||
|
|
||||||
val facet = facetModel.findFacet(KotlinFacetType.TYPE_ID, KotlinFacetType.INSTANCE.defaultFacetName)
|
|
||||||
?: with(KotlinFacetType.INSTANCE) { createFacet(this@getOrCreateFacet, defaultFacetName, createDefaultConfiguration(), null) }
|
|
||||||
.apply { facetModel.addFacet(this) }
|
|
||||||
facet.configuration.settings.useProjectSettings = useProjectSettings
|
|
||||||
if (commitModel) {
|
|
||||||
runWriteAction {
|
|
||||||
facetModel.commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return facet
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinFacet.configureFacet(
|
|
||||||
compilerVersion: String,
|
|
||||||
coroutineSupport: LanguageFeature.State,
|
|
||||||
platformKind: TargetPlatformKind<*>?, // if null, detect by module dependencies
|
|
||||||
modelsProvider: IdeModifiableModelsProvider
|
|
||||||
) {
|
|
||||||
val module = module
|
|
||||||
with(configuration.settings) {
|
|
||||||
compilerArguments = null
|
|
||||||
compilerSettings = null
|
|
||||||
initializeIfNeeded(
|
|
||||||
module,
|
|
||||||
modelsProvider.getModifiableRootModel(module),
|
|
||||||
platformKind,
|
|
||||||
compilerVersion
|
|
||||||
)
|
|
||||||
val apiLevel = apiLevel
|
|
||||||
val languageLevel = languageLevel
|
|
||||||
if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) {
|
|
||||||
this.apiLevel = languageLevel
|
|
||||||
}
|
|
||||||
this.coroutineSupport = coroutineSupport
|
|
||||||
}
|
|
||||||
|
|
||||||
module.externalCompilerVersion = compilerVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinFacet.noVersionAutoAdvance() {
|
|
||||||
configuration.settings.compilerArguments?.let {
|
|
||||||
it.autoAdvanceLanguageVersion = false
|
|
||||||
it.autoAdvanceApiVersion = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Primary" fields are written to argument beans directly and thus not presented in the "additional arguments" string
|
|
||||||
// Update these lists when facet/project settings UI changes
|
|
||||||
val commonUIExposedFields = listOf(
|
|
||||||
CommonCompilerArguments::languageVersion.name,
|
|
||||||
CommonCompilerArguments::apiVersion.name,
|
|
||||||
CommonCompilerArguments::suppressWarnings.name,
|
|
||||||
CommonCompilerArguments::coroutinesState.name
|
|
||||||
)
|
|
||||||
private val commonUIHiddenFields = listOf(
|
|
||||||
CommonCompilerArguments::pluginClasspaths.name,
|
|
||||||
CommonCompilerArguments::pluginOptions.name
|
|
||||||
)
|
|
||||||
private val commonPrimaryFields = commonUIExposedFields + commonUIHiddenFields
|
|
||||||
|
|
||||||
private val jvmSpecificUIExposedFields = listOf(
|
|
||||||
K2JVMCompilerArguments::jvmTarget.name,
|
|
||||||
K2JVMCompilerArguments::destination.name,
|
|
||||||
K2JVMCompilerArguments::classpath.name
|
|
||||||
)
|
|
||||||
val jvmUIExposedFields = commonUIExposedFields + jvmSpecificUIExposedFields
|
|
||||||
private val jvmPrimaryFields = commonPrimaryFields + jvmSpecificUIExposedFields
|
|
||||||
|
|
||||||
private val jsSpecificUIExposedFields = listOf(
|
|
||||||
K2JSCompilerArguments::sourceMap.name,
|
|
||||||
K2JSCompilerArguments::sourceMapPrefix.name,
|
|
||||||
K2JSCompilerArguments::sourceMapEmbedSources.name,
|
|
||||||
K2JSCompilerArguments::outputPrefix.name,
|
|
||||||
K2JSCompilerArguments::outputPostfix.name,
|
|
||||||
K2JSCompilerArguments::moduleKind.name
|
|
||||||
)
|
|
||||||
val jsUIExposedFields = commonUIExposedFields + jsSpecificUIExposedFields
|
|
||||||
private val jsPrimaryFields = commonPrimaryFields + jsSpecificUIExposedFields
|
|
||||||
|
|
||||||
private val metadataSpecificUIExposedFields = listOf(
|
|
||||||
K2MetadataCompilerArguments::destination.name,
|
|
||||||
K2MetadataCompilerArguments::classpath.name
|
|
||||||
)
|
|
||||||
val metadataUIExposedFields = commonUIExposedFields + metadataSpecificUIExposedFields
|
|
||||||
private val metadataPrimaryFields = commonPrimaryFields + metadataSpecificUIExposedFields
|
|
||||||
|
|
||||||
private val CommonCompilerArguments.primaryFields: List<String>
|
|
||||||
get() = when (this) {
|
|
||||||
is K2JVMCompilerArguments -> jvmPrimaryFields
|
|
||||||
is K2JSCompilerArguments -> jsPrimaryFields
|
|
||||||
is K2MetadataCompilerArguments -> metadataPrimaryFields
|
|
||||||
else -> commonPrimaryFields
|
|
||||||
}
|
|
||||||
|
|
||||||
private val CommonCompilerArguments.ignoredFields: List<String>
|
|
||||||
get() = when (this) {
|
|
||||||
is K2JVMCompilerArguments -> listOf(K2JVMCompilerArguments::noJdk.name, K2JVMCompilerArguments::jdkHome.name)
|
|
||||||
else -> emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Module.configureSdkIfPossible(compilerArguments: CommonCompilerArguments, modelsProvider: IdeModifiableModelsProvider) {
|
|
||||||
val allSdks = ProjectJdkTable.getInstance().allJdks
|
|
||||||
val sdk = if (compilerArguments is K2JVMCompilerArguments) {
|
|
||||||
val jdkHome = compilerArguments.jdkHome ?: return
|
|
||||||
allSdks.firstOrNull { it.sdkType is JavaSdk && FileUtil.comparePaths(it.homePath, jdkHome) == 0 } ?: return
|
|
||||||
} else {
|
|
||||||
allSdks.firstOrNull { it.sdkType is KotlinSdkType }
|
|
||||||
?: modelsProvider
|
|
||||||
.modifiableModuleModel
|
|
||||||
.modules
|
|
||||||
.asSequence()
|
|
||||||
.mapNotNull { modelsProvider.getModifiableRootModel(it).sdk }
|
|
||||||
.firstOrNull { it.sdkType is KotlinSdkType }
|
|
||||||
?: KotlinSdkType.INSTANCE.createSdkWithUniqueName(allSdks.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
modelsProvider.getModifiableRootModel(this).sdk = sdk
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseCompilerArgumentsToFacet(
|
|
||||||
arguments: List<String>,
|
|
||||||
defaultArguments: List<String>,
|
|
||||||
kotlinFacet: KotlinFacet,
|
|
||||||
modelsProvider: IdeModifiableModelsProvider?
|
|
||||||
) {
|
|
||||||
with(kotlinFacet.configuration.settings) {
|
|
||||||
val compilerArguments = this.compilerArguments ?: return
|
|
||||||
|
|
||||||
val defaultCompilerArguments = compilerArguments::class.java.newInstance()
|
|
||||||
parseCommandLineArguments(defaultArguments, defaultCompilerArguments)
|
|
||||||
defaultCompilerArguments.convertPathsToSystemIndependent()
|
|
||||||
|
|
||||||
parseCommandLineArguments(arguments, compilerArguments)
|
|
||||||
|
|
||||||
compilerArguments.convertPathsToSystemIndependent()
|
|
||||||
|
|
||||||
// Retain only fields exposed (and not explicitly ignored) in facet configuration editor.
|
|
||||||
// The rest is combined into string and stored in CompilerSettings.additionalArguments
|
|
||||||
|
|
||||||
if (modelsProvider != null)
|
|
||||||
kotlinFacet.module.configureSdkIfPossible(compilerArguments, modelsProvider)
|
|
||||||
|
|
||||||
val primaryFields = compilerArguments.primaryFields
|
|
||||||
val ignoredFields = compilerArguments.ignoredFields
|
|
||||||
|
|
||||||
fun exposeAsAdditionalArgument(property: KProperty1<CommonCompilerArguments, Any?>) =
|
|
||||||
property.name !in primaryFields && property.get(compilerArguments) != property.get(defaultCompilerArguments)
|
|
||||||
|
|
||||||
val additionalArgumentsString = with(compilerArguments::class.java.newInstance()) {
|
|
||||||
copyFieldsSatisfying(compilerArguments, this) { exposeAsAdditionalArgument(it) && it.name !in ignoredFields }
|
|
||||||
ArgumentUtils.convertArgumentsToStringList(this).joinToString(separator = " ") {
|
|
||||||
if (StringUtil.containsWhitespaces(it) || it.startsWith('"')) {
|
|
||||||
StringUtil.wrapWithDoubleQuote(StringUtil.escapeQuotes(it))
|
|
||||||
} else it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compilerSettings?.additionalArguments =
|
|
||||||
if (additionalArgumentsString.isNotEmpty()) additionalArgumentsString else CompilerSettings.DEFAULT_ADDITIONAL_ARGUMENTS
|
|
||||||
|
|
||||||
with(compilerArguments::class.java.newInstance()) {
|
|
||||||
copyFieldsSatisfying(this, compilerArguments) { exposeAsAdditionalArgument(it) || it.name in ignoredFields }
|
|
||||||
}
|
|
||||||
|
|
||||||
val languageLevel = languageLevel
|
|
||||||
val apiLevel = apiLevel
|
|
||||||
if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) {
|
|
||||||
this.apiLevel = languageLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMergedArguments()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user