[kotlin] K2: stop resolving Gradle execution settings, use params from Gradle resolver context

#KTIJ-34871 Fixed

(cherry picked from commit f686ed3d385fded748dc8e5aaa9e530e9f5b2496)

GitOrigin-RevId: bbf3a0e6f4d41c058a9519966efb661e6ffbb9f0
This commit is contained in:
Vlad Koshkin
2025-07-14 17:44:50 +02:00
committed by intellij-monorepo-bot
parent 435ee804ab
commit e9f8329a5c
6 changed files with 137 additions and 125 deletions

View File

@@ -5,8 +5,10 @@ package org.jetbrains.kotlin.gradle.scripting.k1
import KotlinGradleScriptingBundle
import com.intellij.openapi.extensions.InternalIgnoreDependencyViolation
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.progress.runBlockingMaybeCancellable
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.gradle.scripting.shared.ErrorGradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootsLocator
import org.jetbrains.kotlin.gradle.scripting.shared.roots.Imported
import org.jetbrains.kotlin.gradle.scripting.shared.roots.WithoutScriptModels
@@ -15,6 +17,7 @@ import org.jetbrains.kotlin.idea.core.script.k1.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.scriptingInfoLog
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.scripting.definitions.runReadAction
import org.jetbrains.plugins.gradle.settings.DistributionType
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
import org.jetbrains.plugins.gradle.settings.GradleSettingsListener
@@ -107,12 +110,26 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
}
}
private fun loadGradleDefinitions(root: LightGradleBuildRoot) = org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions(
workingDir = root.workingDir,
gradleHome = root.gradleHome,
javaHome = root.javaHome,
project = project
)
private fun loadGradleDefinitions(root: LightGradleBuildRoot): List<ScriptDefinition> {
val settings = runReadAction {
runBlockingMaybeCancellable {
ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
project, root.workingDir, GradleConstants.SYSTEM_ID
)
}
}
return org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions(
project, GradleDefinitionsParams(
workingDir = root.workingDir,
gradleHome = root.gradleHome,
javaHome = root.javaHome,
gradleVersion = null,
jvmArguments = settings.jvmArguments,
environment = settings.env
)
)
}
private fun subscribeToGradleSettingChanges() {
val listener = object : GradleSettingsListener {
@@ -139,16 +156,14 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
// TODO: possibly combine exceptions from every loadGradleTemplates call, be mindful of KT-19276
override val definitions: Sequence<ScriptDefinition>
get() {
definitionsByRoots.keys().iterator().forEachRemaining { root ->
// reload definitions marked as error
definitionsByRoots.keys().iterator().forEachRemaining { root -> // reload definitions marked as error
if (root.isError()) {
definitionsByRoots[root] = loadGradleDefinitions(root)
}
}
if (definitionsByRoots.isEmpty()) {
// can be empty in case when import wasn't done from IDE start up,
if (definitionsByRoots.isEmpty()) { // can be empty in case when import wasn't done from IDE start up,
// otherwise KotlinDslSyncListener should run reloadIfNeeded for valid roots
GradleBuildRootsLocator.getInstance(project).getAllRoots().forEach {
for (it in GradleBuildRootsLocator.getInstance(project).getAllRoots()) {
val workingDir = it.pathPrefix
val (gradleHome, javaHome) = when (it) {
is Imported -> {
@@ -156,11 +171,14 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
}
is WithoutScriptModels -> {
val settings = ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
project,
workingDir,
GradleConstants.SYSTEM_ID
)
val settings = runReadAction {
runBlockingMaybeCancellable {
ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
project, workingDir, GradleConstants.SYSTEM_ID
)
}
} ?: continue
settings.gradleHome to settings.javaHome
}
}

View File

@@ -4,6 +4,7 @@ package org.jetbrains.kotlin.gradle.scripting.k2
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
import com.intellij.util.xmlb.annotations.Attribute
import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.GradleScriptDefinitionWrapper
import org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions
import org.jetbrains.kotlin.idea.core.script.NewScriptFileInfo
@@ -38,38 +39,27 @@ class GradleScriptDefinitionsStorage(val project: Project) :
if (initialized.get()) return
synchronized(this) {
if (!initialized.get()) {
val state = state
if (state.workingDir != null) {
loadDefinitions(state.workingDir, state.gradleHome, state.javaHome, state.gradleVersion)
}
initialized.set(true)
if (initialized.get()) return
val params = state.toParams()
if (params != null) {
loadDefinitions(params)
}
initialized.set(true)
}
}
fun loadDefinitions(workingDir: String, gradleHome: String?, javaHome: String?, gradleVersion: String?) {
_definitions.set(loadAndWrapDefinitions(workingDir, gradleHome, javaHome, gradleVersion))
fun loadDefinitions(params: GradleDefinitionsParams) {
_definitions.set(loadAndWrapDefinitions(params))
initialized.set(true)
updateState {
it.copy(workingDir = workingDir, gradleHome = gradleHome, javaHome = javaHome, gradleVersion = gradleVersion)
it.copyFrom(params)
}
ScriptDefinitionProviderImpl.getInstance(project).notifyDefinitionsChanged()
}
private fun loadAndWrapDefinitions(
workingDir: String,
gradleHome: String?,
javaHome: String?,
gradleVersion: String?
): List<ScriptDefinition> {
val definitions = loadGradleDefinitions(
workingDir = workingDir,
gradleHome = gradleHome,
javaHome = javaHome,
gradleVersion = gradleVersion,
project = project
)
private fun loadAndWrapDefinitions(params: GradleDefinitionsParams): List<ScriptDefinition> {
val definitions = loadGradleDefinitions(project, params)
return definitions.map {
if (it !is GradleScriptDefinitionWrapper) it
else {
@@ -92,12 +82,37 @@ class GradleScriptDefinitionsStorage(val project: Project) :
}
}
data class State(
@Attribute @JvmField val workingDir: String? = null,
@Attribute @JvmField val gradleHome: String? = null,
@Attribute @JvmField val javaHome: String? = null,
@Attribute @JvmField val gradleVersion: String? = null,
)
@JvmField val jvmArguments: List<String> = emptyList(),
@JvmField val environment: Map<String, String> = emptyMap(),
) {
fun copyFrom(params: GradleDefinitionsParams): State {
return copy(
workingDir = params.workingDir,
gradleHome = params.gradleHome,
javaHome = params.javaHome,
gradleVersion = params.gradleVersion,
jvmArguments = params.jvmArguments,
environment = params.environment
)
}
fun toParams(): GradleDefinitionsParams? {
return GradleDefinitionsParams(
workingDir ?: return null,
gradleHome ?: return null,
javaHome ?: return null,
gradleVersion ?: return null,
jvmArguments ?: return null,
environment ?: return null
)
}
}
companion object {
fun getInstance(project: Project): GradleScriptDefinitionsStorage = project.service<GradleScriptDefinitionsStorage>()

View File

@@ -8,6 +8,7 @@ import com.intellij.psi.PsiManager
import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
import org.jetbrains.kotlin.gradle.scripting.k2.GradleScriptDefinitionsStorage
import org.jetbrains.kotlin.gradle.scripting.k2.GradleScriptRefinedConfigurationProvider
import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.GradleScriptModel
import org.jetbrains.kotlin.gradle.scripting.shared.GradleScriptModelData
import org.jetbrains.kotlin.gradle.scripting.shared.importing.kotlinDslSyncListenerInstance
@@ -16,9 +17,9 @@ import org.jetbrains.kotlin.gradle.scripting.shared.importing.saveGradleBuildEnv
import org.jetbrains.kotlin.gradle.scripting.shared.kotlinDslScriptsModelImportSupported
import org.jetbrains.kotlin.idea.core.script.k2.highlighting.DefaultScriptResolutionStrategy
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.plugins.gradle.model.GradleBuildScriptClasspathModel
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncContributor
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncProjectConfigurator.project
import java.nio.file.Path
class KotlinDslScriptSyncContributor : GradleSyncContributor {
@@ -26,36 +27,46 @@ class KotlinDslScriptSyncContributor : GradleSyncContributor {
override val name: String = "Kotlin DSL Script"
override suspend fun onModelFetchCompleted(context: ProjectResolverContext, storage: MutableEntityStorage) {
val project = context.project()
val project = context.project
val taskId = context.externalSystemTaskId
val tasks = kotlinDslSyncListenerInstance?.tasks ?: return
val sync = synchronized(tasks) { tasks[taskId] }
for (buildModel in context.allBuilds) {
for (projectModel in buildModel.projects) {
val projectIdentifier = projectModel.projectIdentifier.projectPath
if (projectIdentifier == ":") {
if (kotlinDslScriptsModelImportSupported(context.projectGradleVersion)) {
val model = context.getProjectModel(projectModel, KotlinDslScriptsModel::class.java)
if (model != null) {
if (!processScriptModel(context, sync, model, projectIdentifier)) {
continue
for (buildModel in context.allBuilds) {
for (projectModel in buildModel.projects) {
val projectIdentifier = projectModel.projectIdentifier.projectPath
if (projectIdentifier == ":") {
if (kotlinDslScriptsModelImportSupported(context.projectGradleVersion)) {
val model = context.getProjectModel(projectModel, KotlinDslScriptsModel::class.java)
if (model != null) {
if (!processScriptModel(context, sync, model, projectIdentifier)) {
continue
}
}
}
saveGradleBuildEnvironment(context)
}
}
}
saveGradleBuildEnvironment(context)
}
}
}
if (sync == null || sync.models.isEmpty()) return
if (sync == null || sync.models.isEmpty()) return
val gradleHome = context.allBuilds.asSequence()
.flatMap { it.projects.asSequence() }
.mapNotNull { context.getProjectModel(it, GradleBuildScriptClasspathModel::class.java) }
.mapNotNull { it.gradleHomeDir?.absolutePath }
.firstOrNull() ?: context.settings.gradleHome
GradleScriptDefinitionsStorage.getInstance(project).loadDefinitions(
sync.workingDir,
sync.gradleHome,
sync.javaHome,
sync.gradleVersion
params = GradleDefinitionsParams(
context.projectPath,
gradleHome,
context.buildEnvironment.java.javaHome.absolutePath,
context.buildEnvironment.gradle.gradleVersion,
context.settings.jvmArguments,
context.settings.env
)
)
val gradleScripts = sync.models.mapNotNullTo(mutableSetOf()) {
val virtualFile = VirtualFileManager.getInstance().findFileByNioPath(Path.of(it.file)) ?: return@mapNotNullTo null
@@ -67,7 +78,8 @@ class KotlinDslScriptSyncContributor : GradleSyncContributor {
)
}
GradleScriptRefinedConfigurationProvider.getInstance(project).processScripts(GradleScriptModelData(gradleScripts, sync.javaHome), storage)
GradleScriptRefinedConfigurationProvider.getInstance(project)
.processScripts(GradleScriptModelData(gradleScripts, sync.javaHome), storage)
val ktFiles = gradleScripts.mapNotNull {
readAction { PsiManager.getInstance(project).findFile(it.virtualFile) as? KtFile }

View File

@@ -1,17 +1,6 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.gradle.scripting.k2.importing
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.gradle.scripting.k2.GradleScriptDefinitionsStorage
import org.jetbrains.kotlin.gradle.scripting.shared.importing.AbstractKotlinDslSyncListener
import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslGradleBuildSync
class KotlinDslSyncListener : AbstractKotlinDslSyncListener() {
override fun reloadDefinitions(
project: Project,
sync: KotlinDslGradleBuildSync
) {
if (sync.models.isEmpty()) return
GradleScriptDefinitionsStorage.getInstance(project).loadDefinitions(sync.workingDir, sync.gradleHome, sync.javaHome, sync.gradleVersion)
}
}
class KotlinDslSyncListener : AbstractKotlinDslSyncListener()

View File

@@ -3,11 +3,9 @@ package org.jetbrains.kotlin.gradle.scripting.shared
import KotlinGradleScriptingBundle
import com.intellij.gradle.toolingExtension.util.GradleVersionUtil.isGradleAtLeast
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
import com.intellij.util.EnvironmentUtil
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.core.script.KotlinScriptEntitySource
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplatesByPaths
@@ -17,8 +15,6 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfiguration
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
import java.nio.file.DirectoryStream
import java.nio.file.Files
@@ -32,15 +28,18 @@ import kotlin.script.templates.standard.ScriptTemplateWithArgs
private const val GRADLE_WITH_NEW_SCRIPTING_TEMPLATES = "9.1"
fun loadGradleDefinitions(
workingDir: String,
gradleHome: String?,
javaHome: String?,
project: Project,
gradleVersion: String? = null,
): List<ScriptDefinition> {
class GradleDefinitionsParams(
val workingDir: String,
val gradleHome: String?,
val javaHome: String?, // null due to k1
val gradleVersion: String?, // null due to k1
val jvmArguments: List<String>,
val environment: Map<String, String>,
)
fun loadGradleDefinitions(project: Project, params: GradleDefinitionsParams): List<ScriptDefinition> {
val loadedDefinitions = try {
val gradleLibDir = gradleHome.toGradleHomePath()
val gradleLibDir = params.gradleHome.toGradleHomePath()
val templateClasspath = getFullDefinitionsClasspath(gradleLibDir)
@@ -48,7 +47,7 @@ fun loadGradleDefinitions(
val languageVersionCompilerOptions = findStdLibLanguageVersion(kotlinLibsClassPath)
val templateClasses = if (gradleVersion != null && isGradleAtLeast(gradleVersion, GRADLE_WITH_NEW_SCRIPTING_TEMPLATES)) {
val templateClasses = if (params.gradleVersion != null && isGradleAtLeast(params.gradleVersion, GRADLE_WITH_NEW_SCRIPTING_TEMPLATES)) {
listOf(
"org.gradle.kotlin.dsl.KotlinGradleScriptTemplate",
"org.gradle.kotlin.dsl.KotlinSettingsScriptTemplate",
@@ -63,13 +62,10 @@ fun loadGradleDefinitions(
}
loadGradleTemplates(
workingDir,
params = params,
templateClasses = templateClasses,
gradleHome = gradleHome,
javaHome = javaHome,
templateClasspath = templateClasspath,
additionalClassPath = kotlinLibsClassPath,
project,
languageVersionCompilerOptions
).distinct()
} catch (t: Throwable) {
@@ -124,22 +120,13 @@ private fun findStdLibLanguageVersion(kotlinLibsClassPath: List<Path>): List<Str
}
private fun loadGradleTemplates(
projectPath: String,
params: GradleDefinitionsParams,
templateClasses: List<String>,
gradleHome: String?,
javaHome: String?,
templateClasspath: List<Path>,
additionalClassPath: List<Path>,
project: Project,
defaultCompilerOptions: List<String>
): List<ScriptDefinition> {
val gradleExeSettings = ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
project,
projectPath,
GradleConstants.SYSTEM_ID
)
val hostConfiguration = createHostConfiguration(projectPath, gradleHome, javaHome, gradleExeSettings)
val hostConfiguration = createHostConfiguration(params)
return loadDefinitionsFromTemplatesByPaths(
templateClasses,
@@ -154,40 +141,31 @@ private fun loadGradleTemplates(
it.hostConfiguration,
it.evaluationConfiguration,
it.defaultCompilerOptions,
projectPath
params.workingDir
)
} ?: GradleScriptDefinitionWrapper(
it.compilationConfiguration,
it.hostConfiguration,
it.evaluationConfiguration,
it.defaultCompilerOptions,
projectPath
params.workingDir
)
}
}
private fun createHostConfiguration(
projectPath: String,
gradleHome: String?,
javaHome: String?,
gradleExeSettings: GradleExecutionSettings
): ScriptingHostConfiguration {
val gradleJvmOptions = gradleExeSettings.jvmArguments
val environment = mapOf(
"gradleHome" to gradleHome?.let(::File),
"gradleJavaHome" to javaHome,
"projectRoot" to projectPath.let(::File),
"gradleOptions" to emptyList<String>(), // There is no option in UI to set project wide gradleOptions
"gradleJvmOptions" to gradleJvmOptions,
"gradleEnvironmentVariables" to if (gradleExeSettings.isPassParentEnvs) EnvironmentUtil.getEnvironmentMap() else emptyMap()
)
return ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment { environment }
private fun createHostConfiguration(params: GradleDefinitionsParams): ScriptingHostConfiguration =
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
getEnvironment {
mapOf(
"gradleHome" to params.gradleHome?.let(::File),
"gradleJavaHome" to params.javaHome,
"projectRoot" to params.workingDir.let(::File),
"gradleOptions" to emptyList<String>(), // There is no option in UI to set project wide gradleOptions
"gradleJvmOptions" to params.jvmArguments,
"gradleEnvironmentVariables" to params.environment
)
}
}
}
private fun String?.toGradleHomePath(): Path {
if (this == null) error(KotlinGradleScriptingBundle.message("error.text.unable.to.get.gradle.home.directory"))

View File

@@ -70,7 +70,7 @@ abstract class AbstractKotlinDslSyncListener : ExternalSystemTaskNotificationLis
saveScriptModels(project, sync)
}
abstract fun reloadDefinitions(project: Project, sync: KotlinDslGradleBuildSync)
protected open fun reloadDefinitions(project: Project, sync: KotlinDslGradleBuildSync): Unit = Unit
override fun onFailure(projectPath: String, id: ExternalSystemTaskId, exception: Exception) {
if (!id.isGradleRelatedTask()) return