as32: Update to AS 3.2 C10

This commit is contained in:
Vyacheslav Gerasimov
2018-04-12 15:19:46 +03:00
parent 6fab21f916
commit 4299455dc1
98 changed files with 9321 additions and 7 deletions
+67
View File
@@ -0,0 +1,67 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
testRuntime(intellijDep())
compileOnly(project(":idea"))
compileOnly(project(":idea:idea-jvm"))
compile(project(":idea:kotlin-gradle-tooling"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:frontend.script"))
compile(project(":js:js.frontend"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("gradle"))
compileOnly(intellijPluginDep("Groovy"))
compileOnly(intellijPluginDep("junit"))
testCompile(projectTests(":idea"))
testCompile(projectTests(":idea:idea-test-framework"))
testCompile(intellijPluginDep("gradle"))
testCompileOnly(intellijPluginDep("Groovy"))
testCompileOnly(intellijDep())
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":idea:idea-android"))
testRuntime(project(":plugins:kapt3-idea"))
testRuntime(project(":plugins:android-extensions-ide"))
testRuntime(project(":plugins:lint"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
// TODO: the order of the plugins matters here, consider avoiding order-dependency
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("testng"))
testRuntime(intellijPluginDep("properties"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
//testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
}
sourceSets {
"main" {
projectDefault()
resources.srcDir("res").apply { include("**") }
}
"test" { projectDefault() }
}
testsJar()
projectTest {
workingDir = rootDir
useAndroidSdk()
}
configureInstrumentation()
@@ -0,0 +1,277 @@
/*
* 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.getResolvedKotlinStdlibVersionByModuleData
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.getResolvedKotlinStdlibVersionByModuleData(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) {
implementedModuleNames = getImplementedModuleNames(moduleNode, sourceSetName, 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)
}
@@ -0,0 +1,375 @@
/*
* 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 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"
private fun isFileConfigured(buildScript: PsiFile): Boolean = getManipulator(buildScript).isConfigured(kotlinPluginName)
@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,
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 open fun addElementsToFile(
file: PsiFile,
isTopLevelProjectFile: Boolean,
version: String
): Boolean {
if (!isTopLevelProjectFile) {
var wasModified = configureProjectFile(file, 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): GradleBuildScriptManipulator = when (file) {
is KtFile -> KotlinBuildScriptManipulator(file)
is GroovyFile -> GroovyBuildScriptManipulator(file)
else -> error("Unknown build script file type!")
}
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"
fun getGroovyDependencySnippet(artifactName: String, scope: String) =
"$scope \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
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()
}
}
fun configureProjectFile(file: PsiFile, version: String): Boolean = getManipulator(file).configureProjectBuildScript(version)
private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project)
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { findBuildGradleFile(it)?.getPsiFile(this) }
private fun Module.getBuildScriptFile(): File? {
val moduleDir = File(moduleFilePath).parent
findBuildGradleFile(moduleDir)?.let {
return it
}
ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
findBuildGradleFile(root.path)?.let {
return it
}
}
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
findBuildGradleFile(externalProjectPath)?.let {
return it
}
}
return null
}
private fun findBuildGradleFile(path: String): File? =
File(path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME).takeIf { it.exists() }
?: File(path + "/" + KOTLIN_BUILD_SCRIPT_NAME).takeIf { 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"
)
}
}
}