From 54d3c5fb0a9f3c8d2a367cbff242cbf134161d3a Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Wed, 26 Aug 2020 14:09:58 +0000 Subject: [PATCH] Pick up script language level from used stdlib in a gradle's classpath ^KT-41283 Fixed --- .../compiler/IDELanguageSettingsProvider.kt | 49 +++++++++++++------ .../jetbrains/kotlin/idea/project/Platform.kt | 12 +++-- .../core/script/ScriptDefinitionsManager.kt | 7 +-- .../GradleKotlinScriptDefinitionWrapper.kt | 3 +- ...GradleKotlinScriptDefinitionWrapper.kt.193 | 3 +- .../gradle/GradleScriptDefinitionsProvider.kt | 23 ++++++++- .../scripting/definitions/ScriptDefinition.kt | 25 +++++++--- 7 files changed, 87 insertions(+), 35 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt index 62f3a21a10d..85c52fdb5bf 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.idea.compiler -import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectFileIndex @@ -32,20 +31,14 @@ import org.jetbrains.kotlin.cli.common.arguments.Jsr305Parser import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.config.JvmTarget -import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider -import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.idea.caches.project.* import org.jetbrains.kotlin.idea.core.script.scriptRelatedModuleName -import org.jetbrains.kotlin.idea.project.TargetPlatformDetector -import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings -import org.jetbrains.kotlin.idea.project.languageVersionSettings -import org.jetbrains.kotlin.idea.project.platform +import org.jetbrains.kotlin.idea.project.* import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatformVersion import org.jetbrains.kotlin.platform.jvm.JdkPlatform import org.jetbrains.kotlin.platform.subplatformsOfType -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.utils.Jsr305State @@ -122,34 +115,58 @@ private fun detectDefaultTargetPlatformVersion(platform: TargetPlatform?): Targe private fun getLanguageSettingsForScripts(project: Project, file: VirtualFile, scriptDefinition: ScriptDefinition): ScriptLanguageSettings { val scriptModule = file.let { - it.scriptRelatedModuleName?.let { ModuleManager.getInstance(project).findModuleByName(it) } + it.scriptRelatedModuleName?.let { module -> ModuleManager.getInstance(project).findModuleByName(module) } ?: ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(it) } + val environmentCompilerOptions = scriptDefinition.defaultCompilerOptions val args = scriptDefinition.compilerOptions - return if (args == null || args.none()) { - ScriptLanguageSettings(project.getLanguageVersionSettings(), detectDefaultTargetPlatformVersion(scriptModule?.platform)) + return if (environmentCompilerOptions.none() && args.none()) { + ScriptLanguageSettings( + project.getLanguageVersionSettings(contextModule = scriptModule), + detectDefaultTargetPlatformVersion(scriptModule?.platform) + ) } else { val settings = scriptDefinition.getUserData(SCRIPT_LANGUAGE_SETTINGS) ?: createCachedValue(project) { val compilerArguments = K2JVMCompilerArguments() + parseCommandLineArguments(environmentCompilerOptions.toList(), compilerArguments) parseCommandLineArguments(args.toList(), compilerArguments) // TODO: reporting val verSettings = compilerArguments.toLanguageVersionSettings(MessageCollector.NONE) - val jvmTarget = compilerArguments.jvmTarget?.let { JvmTarget.fromString(it) } ?: detectDefaultTargetPlatformVersion(scriptModule?.platform) - ScriptLanguageSettings(verSettings, jvmTarget) + val jvmTarget = + compilerArguments.jvmTarget?.let { JvmTarget.fromString(it) } ?: detectDefaultTargetPlatformVersion(scriptModule?.platform) + + val languageVersionSettings = project.getLanguageVersionSettings(contextModule = scriptModule) + val versionSettings = if (languageVersionSettings.languageVersion.isLess(verSettings.languageVersion)) { + languageVersionSettings + } else { + verSettings + } + + ScriptLanguageSettings(versionSettings, jvmTarget) }.also { scriptDefinition.putUserData(SCRIPT_LANGUAGE_SETTINGS, it) } settings.value } } -private fun createCachedValue(project: Project, body: () -> ScriptLanguageSettings): CachedValue { +private fun LanguageVersion.isLess(languageVersion: LanguageVersion): Boolean = + if (major < languageVersion.major) { + true + } else { + minor < languageVersion.minor + } + +private inline fun createCachedValue( + project: Project, + crossinline body: () -> ScriptLanguageSettings +): CachedValue { return CachedValuesManager .getManager(project) .createCachedValue( { CachedValueProvider.Result( body(), - ProjectRootModificationTracker.getInstance(project) + ProjectRootModificationTracker.getInstance(project), ModuleManager.getInstance(project) ) }, false ) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt index 0b0c2da9725..ef3ad3fb421 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt @@ -119,11 +119,15 @@ fun Project.getLanguageVersionSettings( jsr305State: Jsr305State? = null, isReleaseCoroutines: Boolean? = null ): LanguageVersionSettings { - val arguments = KotlinCommonCompilerArgumentsHolder.getInstance(this).settings + val kotlinFacetSettings = contextModule?.let { + KotlinFacetSettingsProvider.getInstance(this)?.getInitializedSettings(it) + } + + val arguments = kotlinFacetSettings?.compilerArguments ?: KotlinCommonCompilerArgumentsHolder.getInstance(this).settings val languageVersion = - LanguageVersion.fromVersionString(arguments.languageVersion) - ?: contextModule?.getAndCacheLanguageLevelByDependencies() - ?: VersionView.RELEASED_VERSION + kotlinFacetSettings?.languageLevel ?: LanguageVersion.fromVersionString(arguments.languageVersion) + ?: contextModule?.getAndCacheLanguageLevelByDependencies() + ?: VersionView.RELEASED_VERSION val apiVersion = ApiVersion.createByLanguageVersion(LanguageVersion.fromVersionString(arguments.apiVersion) ?: languageVersion) val compilerSettings = KotlinCompilerSettings.getInstance(this).settings diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt index 088569cd657..1afa63894a5 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt @@ -264,7 +264,8 @@ fun loadDefinitionsFromTemplates( * Script template dependencies naturally become (part of) dependencies of the script which is not always desired for resolver dependencies. * i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on. */ - additionalResolverClasspath: List = emptyList() + additionalResolverClasspath: List = emptyList(), + defaultCompilerOptions: Iterable = emptyList() ): List { val classpath = templateClasspath + additionalResolverClasspath scriptingInfoLog("Loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}") @@ -281,10 +282,10 @@ fun loadDefinitionsFromTemplates( } when { template.annotations.firstIsInstanceOrNull() != null -> { - ScriptDefinition.FromLegacyTemplate(hostConfiguration, template, templateClasspath) + ScriptDefinition.FromLegacyTemplate(hostConfiguration, template, templateClasspath, defaultCompilerOptions) } template.annotations.firstIsInstanceOrNull() != null -> { - ScriptDefinition.FromTemplate(hostConfiguration, template, ScriptDefinition::class) + ScriptDefinition.FromTemplate(hostConfiguration, template, ScriptDefinition::class, defaultCompilerOptions) } else -> { scriptingWarnLog("Cannot find a valid script definition annotation on the class $template") diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt index 04c0961b005..d4b9f141a55 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt @@ -16,7 +16,8 @@ class GradleKotlinScriptDefinitionWrapper( hostConfiguration: ScriptingHostConfiguration, legacyDefinition: KotlinScriptDefinitionFromAnnotatedTemplate, gradleVersion: String, -) : ScriptDefinition.FromLegacy(hostConfiguration, legacyDefinition) { + defaultCompilerOptions: Iterable +) : ScriptDefinition.FromLegacy(hostConfiguration, legacyDefinition, defaultCompilerOptions) { override val compilationConfiguration by lazy { ScriptCompilationConfigurationFromDefinition( hostConfiguration, diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt.193 b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt.193 index fe57d04f814..c521e74cc0c 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt.193 +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptDefinitionWrapper.kt.193 @@ -15,7 +15,8 @@ class GradleKotlinScriptDefinitionWrapper( hostConfiguration: ScriptingHostConfiguration, legacyDefinition: KotlinScriptDefinitionFromAnnotatedTemplate, gradleVersion: String, -) : ScriptDefinition.FromLegacy(hostConfiguration, legacyDefinition) { + defaultCompilerOptions: Iterable +) : ScriptDefinition.FromLegacy(hostConfiguration, legacyDefinition, defaultCompilerOptions) { override val compilationConfiguration by lazy { ScriptCompilationConfigurationFromDefinition( hostConfiguration, diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptDefinitionsProvider.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptDefinitionsProvider.kt index 8c02110b445..2d7105e2509 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptDefinitionsProvider.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptDefinitionsProvider.kt @@ -11,6 +11,7 @@ import com.intellij.openapi.project.Project import com.intellij.util.EnvironmentUtil import com.intellij.util.PathUtil import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle import org.jetbrains.kotlin.idea.core.script.* import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslSyncListener @@ -125,6 +126,19 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD file?.name?.startsWith("kotlin-compiler-embeddable") == true || file?.name?.startsWith("kotlin-stdlib") == true }?.firstOrNull()?.let(::listOf).orEmpty() } + + private val kotlinStdLibSelector = Regex("^(kotlin-compiler-embeddable|kotlin-stdlib)-(\\d+\\.\\d+).*\\.jar\$") + + fun findStdLibLanguageVersion(classpath: List): LanguageVersion? { + return classpath.map { it.parentFile }.toSet().map { + it.listFiles { file -> + kotlinStdLibSelector.find(file.name) != null + }.firstOrNull()?.let { file -> + val matchResult = kotlinStdLibSelector.find(file.name) ?: return@let null + LanguageVersion.fromVersionString(matchResult.groupValues[2]) + } + }.firstOrNull() + } } override val id: String = "Gradle Kotlin DSL" @@ -236,12 +250,16 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD projectPath, GradleConstants.SYSTEM_ID ) + val defaultCompilerOptions = findStdLibLanguageVersion(templateClasspath)?.let { + listOf("-language-version", it.versionString) + } ?: emptyList() val hostConfiguration = createHostConfiguration(projectPath, gradleHome, javaHome, gradleExeSettings) return loadDefinitionsFromTemplates( listOf(templateClass), templateClasspath, hostConfiguration, - additionalClassPath + additionalClassPath, + defaultCompilerOptions ).map { it.asLegacyOrNull()?.let { legacyDef -> // Expand scope for old gradle script definition @@ -249,7 +267,8 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD GradleKotlinScriptDefinitionWrapper( it.hostConfiguration, legacyDef, - version + version, + defaultCompilerOptions ) } ?: it } diff --git a/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt b/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt index 45152808a71..aa59b105d3c 100644 --- a/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt +++ b/plugins/scripting/scripting-compiler-impl/src/org/jetbrains/kotlin/scripting/definitions/ScriptDefinition.kt @@ -30,6 +30,7 @@ abstract class ScriptDefinition : UserDataHolderBase() { abstract val fileExtension: String abstract val name: String open val defaultClassName: String = "Script" + // TODO: used in settings, find out the reason and refactor accordingly abstract val definitionId: String @@ -47,6 +48,7 @@ abstract class ScriptDefinition : UserDataHolderBase() { open val canDefinitionBeSwitchedOff: Boolean get() = true abstract val baseClassType: KotlinType + open val defaultCompilerOptions: Iterable = emptyList() abstract val compilerOptions: Iterable abstract val annotationsForSamWithReceivers: List @@ -61,7 +63,8 @@ abstract class ScriptDefinition : UserDataHolderBase() { @Suppress("OverridingDeprecatedMember", "DEPRECATION") open class FromLegacy( override val hostConfiguration: ScriptingHostConfiguration, - override val legacyDefinition: KotlinScriptDefinition + override val legacyDefinition: KotlinScriptDefinition, + override val defaultCompilerOptions: Iterable = emptyList() ) : ScriptDefinition() { override val compilationConfiguration: ScriptCompilationConfiguration by lazy { @@ -109,17 +112,19 @@ abstract class ScriptDefinition : UserDataHolderBase() { open class FromLegacyTemplate( hostConfiguration: ScriptingHostConfiguration, template: KClass<*>, - templateClasspath: List = emptyList() + templateClasspath: List = emptyList(), + defaultCompilerOptions: Iterable = emptyList() ) : FromLegacy( hostConfiguration, KotlinScriptDefinitionFromAnnotatedTemplate( template, hostConfiguration[ScriptingHostConfiguration.getEnvironment]?.invoke(), templateClasspath - ) + ), + defaultCompilerOptions ) - abstract class FromConfigurationsBase : ScriptDefinition() { + abstract class FromConfigurationsBase() : ScriptDefinition() { @Suppress("OverridingDeprecatedMember", "DEPRECATION") override val legacyDefinition by lazy { @@ -180,12 +185,14 @@ abstract class ScriptDefinition : UserDataHolderBase() { open class FromConfigurations( override val hostConfiguration: ScriptingHostConfiguration, override val compilationConfiguration: ScriptCompilationConfiguration, - override val evaluationConfiguration: ScriptEvaluationConfiguration? + override val evaluationConfiguration: ScriptEvaluationConfiguration?, + override val defaultCompilerOptions: Iterable = emptyList() ) : FromConfigurationsBase() open class FromNewDefinition( private val baseHostConfiguration: ScriptingHostConfiguration, - private val definition: kotlin.script.experimental.host.ScriptDefinition + private val definition: kotlin.script.experimental.host.ScriptDefinition, + override val defaultCompilerOptions: Iterable = emptyList() ) : FromConfigurationsBase() { override val hostConfiguration: ScriptingHostConfiguration get() = definition.compilationConfiguration[ScriptCompilationConfiguration.hostConfiguration] ?: baseHostConfiguration @@ -197,10 +204,12 @@ abstract class ScriptDefinition : UserDataHolderBase() { open class FromTemplate( baseHostConfiguration: ScriptingHostConfiguration, template: KClass<*>, - contextClass: KClass<*> = ScriptCompilationConfiguration::class + contextClass: KClass<*> = ScriptCompilationConfiguration::class, + defaultCompilerOptions: Iterable = emptyList() ) : FromNewDefinition( baseHostConfiguration, - createScriptDefinitionFromTemplate(KotlinType(template), baseHostConfiguration, contextClass) + createScriptDefinitionFromTemplate(KotlinType(template), baseHostConfiguration, contextClass), + defaultCompilerOptions ) companion object {