[kotlin] KTIJ-35769 add support for GradleDslBaseScriptModel

Merge-request: IJ-MR-173210
Merged-by: Vlad Koshkin <Vladislav.Koshkin@jetbrains.com>

GitOrigin-RevId: c143b1fa121abff9feb27d81af7632dfdd607292
This commit is contained in:
Vlad Koshkin
2025-11-12 16:00:27 +00:00
committed by intellij-monorepo-bot
parent ecb6d0fc54
commit c304e71274
40 changed files with 652 additions and 273 deletions
@@ -167,6 +167,48 @@ public final class GradleExecutionHelper {
return getBuildEnvironment(connection, context);
}
public static <Model> @NotNull Model getModel(
@NotNull ProjectConnection connection,
@NotNull GradleExecutionContext context,
@NotNull Class<Model> modelClass
) {
Span span = ExternalSystemTelemetryUtil.getTracer(GradleConstants.SYSTEM_ID)
.spanBuilder("GetModel")
.setAttribute("modelClass", modelClass.getName())
.startSpan();
try (Scope ignore = span.makeCurrent()) {
ExternalSystemTaskId taskId = context.getTaskId();
ExternalSystemTaskNotificationListener listener = context.getListener();
ModelBuilder<Model> modelBuilder = connection.model(modelClass);
modelBuilder.withCancellationToken(context.getCancellationToken());
setupJavaHome(modelBuilder, context.getSettings(), taskId, listener, null);
// do not use connection.getModel methods since it doesn't allow to handle progress events
// and we can miss gradle tooling client side events like distribution download.
GradleProgressListener gradleProgressListener = new GradleProgressListener(listener, taskId);
modelBuilder.addProgressListener((ProgressListener)gradleProgressListener);
modelBuilder.addProgressListener((org.gradle.tooling.events.ProgressListener)gradleProgressListener);
modelBuilder.setStandardOutput(new OutputWrapper(listener, taskId, true));
modelBuilder.setStandardError(new OutputWrapper(listener, taskId, false));
return modelBuilder.get();
}
catch (CancellationException ce) {
throw ce;
}
catch (Exception ex) {
span.recordException(ex);
span.setStatus(StatusCode.ERROR);
throw new RuntimeException(String.format("Failed to obtain model %s from Gradle daemon.", modelClass.getSimpleName()), ex);
}
finally {
span.end();
}
}
/**
* @deprecated use the {@link GradleExecutionHelper#execute} function with {@link GradleExecutionContext} instead
*/
@@ -53,7 +53,8 @@ class GradleIdeaModelHolder(
}
fun <T> getRootModel(modelClazz: Class<T>): T? {
return getBuildModel(getRootBuild(), modelClazz)
return getModel(getRootModelId(modelClazz), modelClazz)
?: rootBuild?.let { getBuildModel(it, modelClazz) }
}
fun <T> getBuildModel(buildModel: BuildModel, modelClass: Class<T>): T? {
@@ -99,6 +100,7 @@ class GradleIdeaModelHolder(
}
}
@ApiStatus.Internal
private fun <T : Any> deserializeModel(model: Any, modelId: GradleModelId, modelClass: Class<T>): T? {
if (model !is ByteArray) {
return null
@@ -116,6 +118,12 @@ class GradleIdeaModelHolder(
return deserializedModel
}
@ApiStatus.Internal
fun <T : Any> addRootModel(modelClass: Class<T>, model: T) {
val modelId = getRootModelId(modelClass)
models[modelId] = model
}
fun addState(state: GradleModelHolderState) {
val rootBuild = state.rootBuild
val nestedBuilds = state.nestedBuilds
@@ -166,6 +174,10 @@ class GradleIdeaModelHolder(
return GradleModelId(modelId.classId, originalBuildId, modelId.projectId)
}
private fun getRootModelId(modelClass: Class<*>): GradleModelId {
return GradleModelId.createRootModelId(modelClass)
}
private fun getBuildModelId(buildModel: BuildModel, modelClass: Class<*>): GradleModelId {
val modelId = GradleModelId.createBuildModelId(buildModel, modelClass)
return applyBuildIdMapping(modelId)
@@ -44,6 +44,7 @@ import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.model.ProjectModel;
import org.gradle.tooling.model.build.BuildEnvironment;
import org.gradle.tooling.model.dsl.GradleDslBaseScriptModel;
import org.gradle.tooling.model.idea.IdeaModule;
import org.gradle.tooling.model.idea.IdeaProject;
import org.jetbrains.annotations.*;
@@ -282,6 +283,12 @@ public final class GradleProjectResolver implements ExternalSystemProjectResolve
.spanBuilder("GradleCall")
.startSpan();
try (Scope ignore = gradleCallSpan.makeCurrent()) {
if (GradleVersionUtil.isGradleAtLeast(resolverContext.getGradleVersion(), "9.2")) {
var scriptModel = GradleExecutionHelper.getModel(connection, resolverContext, GradleDslBaseScriptModel.class);
models.addRootModel(GradleDslBaseScriptModel.class, scriptModel);
GradleSyncProjectConfigurator.runScriptBasePhase(resolverContext);
}
var modelFetchActionResultHandler = GradleSyncProjectConfigurator.createModelFetchResultHandler(resolverContext);
GradleModelFetchActionRunner.runAndTraceBuildAction(connection, resolverContext, buildAction, modelFetchActionResultHandler);
}
@@ -30,6 +30,9 @@ sealed interface GradleSyncPhase : Comparable<GradleSyncPhase> {
}
}
@ApiStatus.Internal
sealed interface BaseScript: GradleSyncPhase
/**
* In these phases, Gradle sync contributors are executed when models for [modelFetchPhase] are collected on the Gradle daemon side.
*/
@@ -61,6 +64,13 @@ sealed interface GradleSyncPhase : Comparable<GradleSyncPhase> {
@JvmField
val DECLARATIVE_PHASE: GradleSyncPhase = Static(1000, "DECLARATIVE_PHASE")
/**
*
*/
@JvmField
@ApiStatus.Internal
val BASE_SCRIPT_MODEL_PHASE: GradleSyncPhase = GradleBaseScriptSyncPhase
/**
* In this phase, Gradle sync contributors,
* contribute to the IDE module and content root structure for each Gradle project.
@@ -82,6 +92,12 @@ sealed interface GradleSyncPhase : Comparable<GradleSyncPhase> {
@JvmField
val DEPENDENCY_MODEL_PHASE: GradleSyncPhase = GradleModelFetchPhase.PROJECT_SOURCE_SET_DEPENDENCY_PHASE.asSyncPhase()
/**
*
*/
@JvmField
val SCRIPT_MODEL_PHASE: GradleSyncPhase = GradleModelFetchPhase.SCRIPT_MODEL_PHASE.asSyncPhase()
/**
* In this phase, Gradle sync contributors,
* contribute to the IDE project model for a rich experience in IntelliJ IDEA.
@@ -105,6 +121,7 @@ private class GradleStaticSyncPhase(
override fun compareTo(other: GradleSyncPhase): Int {
return when (other) {
is GradleStaticSyncPhase -> order.compareTo(other.order)
is GradleBaseScriptSyncPhase -> -1
is GradleDynamicSyncPhase -> -1
}
}
@@ -134,6 +151,7 @@ private class GradleDynamicSyncPhase(
override fun compareTo(other: GradleSyncPhase): Int {
return when (other) {
is GradleStaticSyncPhase -> 1
is GradleBaseScriptSyncPhase -> 1
is GradleDynamicSyncPhase -> modelFetchPhase.compareTo(other.modelFetchPhase)
}
}
@@ -150,4 +168,17 @@ private class GradleDynamicSyncPhase(
override fun hashCode(): Int {
return modelFetchPhase.hashCode()
}
}
private data object GradleBaseScriptSyncPhase: GradleSyncPhase.BaseScript {
override val name: String = "BASE_SCRIPT_MODEL"
override fun compareTo(other: GradleSyncPhase): Int {
return when (other) {
is GradleStaticSyncPhase -> 1
is GradleBaseScriptSyncPhase -> 0
is GradleDynamicSyncPhase -> -1
}
}
}
@@ -40,6 +40,16 @@ object GradleSyncProjectConfigurator {
}
}
@JvmStatic
fun runScriptBasePhase(context: ProjectResolverContext) {
require(!application.isWriteAccessAllowed) {
"Must not execute inside write action"
}
runBlockingCancellable {
GradleSyncActionRunner().performSyncContributors(context) { it is GradleSyncPhase.BaseScript }
}
}
@JvmStatic
fun createModelFetchResultHandler(context: ProjectResolverContext): GradleModelFetchActionListener {
return object : GradleModelFetchActionListener {
@@ -490,6 +490,8 @@ class GradleOutputParsersMessagesImportingTest : GradleOutputParsersMessagesImpo
val text = it.lineSequence()
.dropWhile { s -> s == "Starting Gradle Daemon..."
|| s.startsWith("Gradle Daemon started in")
|| s == ""
|| s.startsWith("CONFIGURE SUCCESSFUL")
|| s.startsWith("Download ") }
.joinToString(separator = "\n")
@@ -174,6 +174,7 @@ class GradlePhasedSyncTest : GradlePhasedSyncTestCase() {
val syncPhaseCompletionAssertions = ListenerAssertion()
val allPhases = DEFAULT_SYNC_PHASES
val allStaticPhases = allPhases.filterIsInstance<GradleSyncPhase.Static>()
val allDynamicPhases = allPhases.filterIsInstance<GradleSyncPhase.Dynamic>()
val completedPhases = CopyOnWriteArrayList<GradleSyncPhase>()
@@ -192,12 +193,17 @@ class GradlePhasedSyncTest : GradlePhasedSyncTestCase() {
whenSyncPhaseCompleted(phase, disposable) { _ ->
syncPhaseCompletionAssertions.trace {
val completedStaticPhases = completedPhases.filterIsInstance<GradleSyncPhase.Static>()
val completedBaseScriptPhases = completedPhases.filterIsInstance<GradleSyncPhase.BaseScript>()
val completedDynamicPhases = completedPhases.filterIsInstance<GradleSyncPhase.Dynamic>()
val expectedEntities = when (phase) {
is GradleSyncPhase.Static -> when (isSecondarySync) {
true -> completedStaticPhases + allDynamicPhases
else -> completedStaticPhases
}
is GradleSyncPhase.BaseScript -> when (isSecondarySync) {
true -> allStaticPhases + completedBaseScriptPhases + allDynamicPhases
else -> completedBaseScriptPhases
}
is GradleSyncPhase.Dynamic -> when (isSecondarySync) {
true -> allDynamicPhases
else -> completedDynamicPhases
@@ -259,9 +265,11 @@ class GradlePhasedSyncTest : GradlePhasedSyncTestCase() {
whenSyncPhaseCompleted(phase, disposable) { _ ->
syncPhaseCompletionAssertions.trace {
val completedStaticPhases = completedPhases.filterIsInstance<GradleSyncPhase.Static>()
val completedBaseScriptPhases = completedPhases.filterIsInstance<GradleSyncPhase.BaseScript>()
val completedDynamicPhases = completedPhases.filterIsInstance<GradleSyncPhase.Dynamic>()
val expectedEntities = when (phase) {
is GradleSyncPhase.Static -> completedStaticPhases
is GradleSyncPhase.BaseScript -> completedBaseScriptPhases
is GradleSyncPhase.Dynamic -> completedDynamicPhases
}
WorkspaceAssertions.assertEntities(myProject, expectedEntities.map { GradleTestEntityId(it) }) {
@@ -79,13 +79,19 @@ sealed interface GradleModelFetchPhase : Comparable<GradleModelFetchPhase>, Seri
@JvmField
val PROJECT_SOURCE_SET_DEPENDENCY_PHASE: GradleModelFetchPhase = BuildFinished(2000, "DEPENDENCY_MODEL_PHASE")
/**
*
*/
@JvmField
val SCRIPT_MODEL_PHASE: GradleModelFetchPhase = BuildFinished(3000, "SCRIPT_MODEL_PHASE")
/**
* In this phase, Gradle model providers fetch rest of Gradle models, which needed for rich experience in IntelliJ IDEA.
* It is a code insight in Gradle scripts, data for run configuration creation and for code completion in him,
* data for code profiling, etc.
*/
@JvmField
val ADDITIONAL_MODEL_PHASE: GradleModelFetchPhase = BuildFinished(3000, "ADDITIONAL_MODEL_PHASE")
val ADDITIONAL_MODEL_PHASE: GradleModelFetchPhase = BuildFinished(4000, "ADDITIONAL_MODEL_PHASE")
}
}
@@ -70,6 +70,12 @@ public final class GradleModelId implements Serializable {
return new GradleModelId(classId, buildId, projectId);
}
public static @NotNull GradleModelId createRootModelId(@NotNull Class<?> modelClass) {
String classId = createClassId(modelClass);
String projectId = ":";
return new GradleModelId(classId, "", projectId);
}
public static @NotNull GradleModelId createProjectModelId(@NotNull ProjectModel projectModel, @NotNull Class<?> modelClass) {
ProjectIdentifier projectIdentifier = getProjectIdentifier(projectModel);
BuildIdentifier buildIdentifier = projectIdentifier.getBuildIdentifier();
@@ -63,6 +63,7 @@ jvm_library(
"//plugins/kotlin/base/scripting/scripting.shared",
"//plugins/kotlin/highlighting/highlighting-shared:kotlin-highlighting-shared",
"//platform/lang-core",
"//platform/backend/observation",
]
)
@@ -130,6 +131,8 @@ jvm_library(
"//platform/testFramework",
"//platform/testFramework:testFramework_test_lib",
"//plugins/kotlin/base/test:test_test_lib",
"//platform/backend/observation",
"//platform/backend/observation:observation_test_lib",
]
)
### auto-generated section `build intellij.kotlin.base.scripting` end
@@ -63,5 +63,6 @@
<orderEntry type="module" module-name="intellij.platform.lang.core" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="kotlin.base.test" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.backend.observation" />
</component>
</module>
@@ -8,17 +8,19 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.relativizeToClosestAncestor
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.platform.backend.observation.launchTracked
import com.intellij.util.application
import com.intellij.util.concurrency.ThreadingAssertions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.jetbrains.kotlin.analysis.api.platform.modification.publishGlobalModuleStateModificationEvent
import org.jetbrains.kotlin.analysis.api.platform.modification.publishGlobalScriptModuleStateModificationEvent
import org.jetbrains.kotlin.idea.core.script.k2.configurations.getConfigurationResolver
import org.jetbrains.kotlin.idea.core.script.k2.configurations.getWorkspaceModelManager
import org.jetbrains.kotlin.idea.core.script.k2.highlighting.KotlinScriptResolutionService.Companion.dropKotlinScriptCaches
import org.jetbrains.kotlin.idea.core.script.k2.highlighting.KotlinScriptResolutionService.Companion.dropKotlinScriptCachesUnderWriteAction
import org.jetbrains.kotlin.idea.core.script.shared.KotlinScriptProcessingFilter
import org.jetbrains.kotlin.idea.core.script.v1.ScriptDependenciesModificationTracker
import org.jetbrains.kotlin.idea.core.script.v1.alwaysVirtualFile
import org.jetbrains.kotlin.idea.core.script.v1.awaitExternalSystemInitialization
import org.jetbrains.kotlin.idea.core.script.v1.scriptingDebugLog
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@@ -56,7 +58,8 @@ class KotlinScriptResolutionService(
* @param ktFile the Kotlin script to be processed.
*/
fun launchProcessing(ktFile: KtFile) {
coroutineScope.launch {
coroutineScope.launchTracked {
project.awaitExternalSystemInitialization()
process(listOf(ktFile))
}
}
@@ -112,7 +115,7 @@ class KotlinScriptResolutionService(
* - Tries to get an existing configuration for the file; if absent, creates it.
* - Accumulates configurations and applies them to the workspace model in a single batch update.
*
* After updating the model, Kotlin script-related caches are dropped via [dropKotlinScriptCaches].
* After updating the model, Kotlin script-related caches are dropped via [dropKotlinScriptCachesUnderWriteAction].
*
* @param definitionByFile mapping of script files to their resolved [ScriptDefinition].
*/
@@ -128,23 +131,29 @@ class KotlinScriptResolutionService(
}
assert(!application.isWriteAccessAllowed)
projectModelUpdater.updateWorkspaceModel(configurationPerVirtualFile)
dropKotlinScriptCaches(project)
dropKotlinScriptCachesUnderWriteAction(project)
projectModelUpdater.updateWorkspaceModel(configurationPerVirtualFile)
}
companion object {
@JvmStatic
fun getInstance(project: Project): KotlinScriptResolutionService = project.service()
suspend fun dropKotlinScriptCaches(project: Project) {
suspend fun dropKotlinScriptCachesUnderWriteAction(project: Project) {
edtWriteAction {
dropKotlinScriptCaches(project)
}
}
fun dropKotlinScriptCaches(project: Project) {
ThreadingAssertions.assertWriteAccess()
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
HighlightingSettingsPerFile.getInstance(project).incModificationCount()
edtWriteAction {
project.publishGlobalModuleStateModificationEvent()
project.publishGlobalScriptModuleStateModificationEvent()
}
project.publishGlobalModuleStateModificationEvent()
project.publishGlobalScriptModuleStateModificationEvent()
}
}
}
@@ -41,6 +41,8 @@ jvm_library(
"//plugins/kotlin/highlighting/highlighting-shared:kotlin-highlighting-shared",
"//java/java-psi-impl:psi-impl",
"//platform/indexing-api:indexing",
"//platform/external-system-impl:externalSystem-impl",
"//platform/external-system-api:externalSystem",
]
)
### auto-generated section `build intellij.kotlin.base.scripting.shared` end
@@ -35,5 +35,7 @@
<orderEntry type="module" module-name="kotlin.highlighting.shared" />
<orderEntry type="module" module-name="intellij.java.psi.impl" />
<orderEntry type="module" module-name="intellij.platform.indexing" />
<orderEntry type="module" module-name="intellij.platform.externalSystem.impl" />
<orderEntry type="module" module-name="intellij.platform.externalSystem" />
</component>
</module>
@@ -5,11 +5,14 @@ package org.jetbrains.kotlin.idea.core.script.v1
import com.intellij.openapi.application.Application
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import kotlinx.coroutines.suspendCancellableCoroutine
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.psi.KtFile
@@ -67,4 +70,12 @@ fun Project.getKtFile(virtualFile: VirtualFile?, ktFile: KtFile? = null): KtFile
} else {
return runReadAction { PsiManager.getInstance(this).findFile(virtualFile) as? KtFile }
}
}
suspend fun Project.awaitExternalSystemInitialization() {
suspendCancellableCoroutine { continuation ->
ExternalProjectsManagerImpl.getInstance(this).runWhenInitialized {
continuation.resumeWith(Result.success(Unit))
}
}
}
@@ -0,0 +1,12 @@
// 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.idea.k2.codeInsight.gradle.navigation
import org.jetbrains.kotlin.gradle.AbstractKotlinGradleResilientHighlightingTest
import org.jetbrains.kotlin.idea.test.AssertKotlinPluginMode
import org.jetbrains.kotlin.idea.test.UseK2PluginMode
import org.jetbrains.plugins.gradle.testFramework.fixtures.application.GradleProjectTestApplication
@UseK2PluginMode
@GradleProjectTestApplication
@AssertKotlinPluginMode
class K2GradleResilientHighlightingTest : AbstractKotlinGradleResilientHighlightingTest()
@@ -0,0 +1,92 @@
// 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
import com.intellij.testFramework.TestDataPath
import com.intellij.testFramework.runInEdtAndWait
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.idea.base.plugin.useK2Plugin
import org.jetbrains.kotlin.idea.base.test.IgnoreTests
import org.jetbrains.kotlin.idea.base.test.TestRoot
import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.HighlightingConfiguration
import org.jetbrains.kotlin.idea.highlighter.CHECK_SYMBOL_NAMES
import org.jetbrains.kotlin.idea.highlighter.checkHighlighting
import org.jetbrains.kotlin.idea.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestMetadata
import org.jetbrains.plugins.gradle.frameworkSupport.GradleDsl
import org.jetbrains.plugins.gradle.testFramework.GradleTestFixtureBuilder
import org.jetbrains.plugins.gradle.testFramework.annotations.BaseGradleVersionSource
import org.jetbrains.plugins.gradle.testFramework.util.withBuildFile
import org.jetbrains.plugins.gradle.testFramework.util.withSettingsFile
import org.junit.jupiter.params.ParameterizedTest
import java.io.File
@TestRoot("idea/tests/testData/")
@TestDataPath($$"$CONTENT_ROOT")
@TestMetadata("../../../idea/tests/testData/gradle/highlighting/")
abstract class AbstractKotlinGradleResilientHighlightingTest : AbstractGradleCodeInsightTest() {
@ParameterizedTest
@BaseGradleVersionSource
@TestMetadata("resilient/simple.test")
fun testSimple(gradleVersion: GradleVersion) {
verifyHighlighting(gradleVersion)
}
private val outputFileExtensions: List<String> = listOfNotNull(".highlighting.k2".takeIf { useK2Plugin == true }, ".highlighting")
private fun verifyHighlighting(gradleVersion: GradleVersion) {
test(gradleVersion, GRADLE_KOTLIN_FIXTURE_WITH_ERROR) {
val mainFile = mainTestDataPsiFile
val ktsFileUnderTest = mainFile.virtualFile.toNioPath().toFile()
val path = ktsFileUnderTest.path
val ktsFileHighlighting = outputFileExtensions.firstNotNullOfOrNull { ext ->
val resolveSibling = ktsFileUnderTest.resolveSibling("$path$ext")
resolveSibling.takeIf(File::exists)
} ?: error("highlighting file does not exist for ${ktsFileUnderTest.path}")
val directives = KotlinTestUtils.parseDirectives(mainTestDataFile.content).also {
it.put(CHECK_SYMBOL_NAMES, true.toString())
}
runInEdtAndWait {
IgnoreTests.runTestIfNotDisabledByFileDirective(
mainFile.virtualFile.toNioPath(),
if (useK2Plugin == true) IgnoreTests.DIRECTIVES.IGNORE_K2 else IgnoreTests.DIRECTIVES.IGNORE_K1
) {
checkHighlighting(
mainFile,
ktsFileHighlighting,
directives,
project,
highlightWarnings = true,
severityOption = HighlightingConfiguration.SeverityRenderingOption.ALWAYS
)
}
}
}
}
companion object {
val GRADLE_KOTLIN_FIXTURE_WITH_ERROR: GradleTestFixtureBuilder = GradleTestFixtureBuilder.create("GradleKotlinFixture") { gradleVersion ->
withSettingsFile(gradleVersion, gradleDsl = GradleDsl.KOTLIN) {
setProjectName("GradleKotlinFixture")
include(":module1")
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
}
withBuildFile(gradleVersion, gradleDsl = GradleDsl.KOTLIN) {
call("some-error-code")
}
withBuildFile(gradleVersion, "module1", gradleDsl = GradleDsl.KOTLIN) {
withKotlinDsl()
withMavenCentral()
}
withFile(
"gradle.properties", """
kotlin.code.style=official
""".trimIndent()
)
}
}
}
@@ -2,6 +2,7 @@
package org.jetbrains.kotlin.idea.gradleTooling
import com.intellij.gradle.toolingExtension.modelAction.GradleModelFetchPhase
import org.gradle.tooling.BuildController
import org.gradle.tooling.model.gradle.GradleBuild
import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
@@ -9,8 +10,11 @@ import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider
import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider.GradleModelConsumer
class KotlinDslScriptModelProvider : ProjectImportModelProvider {
private val kotlinDslScriptModelClass: Class<*> = KotlinDslScriptsModel::class.java
override fun getPhase(): GradleModelFetchPhase = GradleModelFetchPhase.SCRIPT_MODEL_PHASE
override fun populateModels(
controller: BuildController,
buildModels: Collection<GradleBuild>,
@@ -6,9 +6,10 @@ 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.k1.roots.GradleBuildRootsLocatorImpl
import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle
import org.jetbrains.kotlin.gradle.scripting.shared.definition.ErrorGradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.definition.loadGradleDefinitions
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
@@ -124,7 +125,7 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
return emptyList()
}
return org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions(
return loadGradleDefinitions(
GradleDefinitionsParams(
workingDir = root.workingDir,
gradleHome = root.gradleHome,
@@ -3,26 +3,21 @@ package org.jetbrains.kotlin.gradle.scripting.k1.roots
import com.intellij.openapi.util.IntellijInternalApi
import com.intellij.util.io.DataExternalizer
import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs
import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel
import org.jetbrains.kotlin.gradle.scripting.shared.roots.*
import org.jetbrains.kotlin.idea.core.script.v1.readString
import org.jetbrains.kotlin.gradle.scripting.shared.roots.AbstractGradleBuildRootDataSerializer
import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootData
import org.jetbrains.kotlin.gradle.scripting.shared.roots.StringsPool
import org.jetbrains.kotlin.gradle.scripting.shared.roots.writeList
import org.jetbrains.kotlin.idea.core.script.v1.writeString
import java.io.DataInput
import java.io.DataOutput
class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() {
override val externalizer: DataExternalizer<GradleBuildRootData>
get() = object : DataExternalizer<GradleBuildRootData> {
override fun save(out: DataOutput, value: GradleBuildRootData) =
writeKotlinDslScriptModels(out, value)
override fun read(`in`: DataInput): GradleBuildRootData =
readKotlinDslScriptModels(`in`, currentBuildRoot.get().path)
override fun getExternalizer(): DataExternalizer<GradleBuildRootData> = object : DataExternalizer<GradleBuildRootData> {
override fun save(out: DataOutput, value: GradleBuildRootData) = writeKotlinDslScriptModels(out, value)
override fun read(`in`: DataInput): GradleBuildRootData = readKotlinDslScriptModels(`in`, currentBuildRoot.get().path)
}
companion object {
@IntellijInternalApi
fun writeKotlinDslScriptModels(output: DataOutput, data: GradleBuildRootData) {
val strings = StringsPool.writer(output)
@@ -49,28 +44,6 @@ class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() {
strings.writeStringIds(it.imports)
}
}
@IntellijInternalApi
fun readKotlinDslScriptModels(input: DataInput, buildRoot: String): GradleBuildRootData {
val strings = StringsPool.reader(input)
val importTs = input.readLong()
val projectRoots = strings.readStrings()
val gradleHome = strings.readString()
val javaHome = strings.readNullableString()
val models = input.readList {
KotlinDslScriptModel(
strings.readString(),
GradleKotlinScriptConfigurationInputs(input.readString(), input.readLong(), buildRoot),
strings.readStrings(),
strings.readStrings(),
strings.readStrings(),
listOf()
)
}
return GradleBuildRootData(importTs, projectRoots, gradleHome, javaHome, models)
}
}
}
@@ -16,7 +16,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.jetbrains.kotlin.gradle.scripting.k1.GradleScriptDefinitionsContributor
import org.jetbrains.kotlin.gradle.scripting.k1.roots.GradleScriptingSupport.Companion.isApplicable
import org.jetbrains.kotlin.gradle.scripting.shared.getDefinitionsTemplateClasspath
import org.jetbrains.kotlin.gradle.scripting.shared.definition.getFullDefinitionsClasspath
import org.jetbrains.kotlin.gradle.scripting.shared.definition.toGradleHomePath
import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel
import org.jetbrains.kotlin.gradle.scripting.shared.kotlinDslScriptsModelImportSupported
import org.jetbrains.kotlin.gradle.scripting.shared.roots.*
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.idea.core.script.k1.configuration.DefaultScriptingSu
import org.jetbrains.kotlin.idea.core.script.k1.configuration.ScriptingSupport
import org.jetbrains.kotlin.idea.core.script.k1.settings.KotlinScriptingSettingsImpl
import org.jetbrains.kotlin.idea.core.script.k1.ucache.ScriptClassRootsBuilder
import org.jetbrains.kotlin.idea.core.script.v1.scriptingInfoLog
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
@@ -35,6 +37,7 @@ import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import java.nio.file.Path
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.io.path.invariantSeparatorsPathString
/**
* [org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRoot] is a linked gradle build (don't confuse with gradle project and included build).
@@ -330,10 +333,18 @@ class GradleScriptingSupport(val project: Project) : ScriptingSupport {
return definitions.firstOrNull { it.isScript(scriptSource) }
}
private fun getDefinitionsTemplateClasspath(gradleHome: String?): List<String> = try {
getFullDefinitionsClasspath(gradleHome.toGradleHomePath()).map { it.invariantSeparatorsPathString }
} catch (e: Throwable) {
scriptingInfoLog("cannot get gradle classpath for Gradle Kotlin DSL scripts: ${e.message}")
emptyList()
}
companion object {
fun isApplicable(file: VirtualFile, project: Project): Boolean {
val support = ScriptingSupport.EP_NAME.findExtension(GradleScriptingSupport::class.java, project) ?: return false
return support.isApplicable(file)
}
}
}
}
@@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.scripting.k1.gradle
import org.jetbrains.kotlin.gradle.scripting.k1.roots.GradleBuildRootDataSerializer
import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs
import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel
import org.jetbrains.kotlin.gradle.scripting.shared.roots.AbstractGradleBuildRootDataSerializer
import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootData
import org.junit.Test
import java.io.ByteArrayInputStream
@@ -44,7 +45,7 @@ class GradleBuildRootDataSerializerTest {
val buffer = ByteArrayOutputStream()
GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), data)
val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a")
val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a")
assertEquals(data.toString(), restored.toString())
}
@@ -62,7 +63,7 @@ class GradleBuildRootDataSerializerTest {
val buffer = ByteArrayOutputStream()
GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), data)
val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a")
val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a")
assertEquals(data.toString(), restored.toString())
}
@@ -3,6 +3,7 @@
package org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel
import com.intellij.openapi.util.NlsSafe
import com.intellij.platform.workspace.storage.*
import org.jetbrains.kotlin.idea.core.script.k2.modules.ScriptCompilationConfigurationEntity
import org.jetbrains.kotlin.idea.core.script.k2.modules.ScriptEvaluationConfigurationEntity
@@ -1,6 +1,7 @@
// 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.workspaceModel.impl
import com.intellij.openapi.util.NlsSafe
import com.intellij.platform.workspace.storage.*
import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
@@ -16,6 +16,16 @@ internal object MetadataStorageImpl : MetadataStorageBase() {
var typeMetadata: StorageTypeMetadata
typeMetadata = FinalClassMetadata.ObjectMetadata(
fqName = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource", properties = listOf(
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "virtualFileUrl",
valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = true,
typeMetadata = FinalClassMetadata.KnownClass(
fqName = "com.intellij.platform.workspace.storage.url.VirtualFileUrl")),
withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.EntitySource"))
addMetadata(typeMetadata)
typeMetadata = FinalClassMetadata.ClassMetadata(
fqName = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.GradleScriptDefinitionEntityId", properties = listOf(
OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "id", valueType = primitiveTypeStringNotNullable,
@@ -128,6 +138,9 @@ internal object MetadataStorageImpl : MetadataStorageBase() {
metadataHash = -362496579)
addMetadataHash(typeFqn = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.GradleScriptDefinitionEntityId",
metadataHash = -1956757513)
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.EntitySource", metadataHash = 1287228111)
addMetadataHash(typeFqn = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource",
metadataHash = -532489477)
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.SymbolicEntityId", metadataHash = 1136338439)
}
@@ -16,6 +16,7 @@
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<syncExtension implementation="org.jetbrains.kotlin.gradle.scripting.k2.importing.KotlinDslScriptSyncExtension"/>
<syncContributor implementation="org.jetbrains.kotlin.gradle.scripting.k2.importing.KotlinDslBaseScriptSyncContributor"/>
<syncContributor implementation="org.jetbrains.kotlin.gradle.scripting.k2.importing.KotlinDslScriptSyncContributor"/>
</extensions>
@@ -18,16 +18,17 @@ import com.intellij.platform.workspace.storage.MutableEntityStorage
import com.intellij.platform.workspace.storage.VersionedStorageChange
import com.intellij.platform.workspace.storage.toBuilder
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.kotlin.gradle.scripting.k2.definition.withIdeKeys
import org.jetbrains.kotlin.gradle.scripting.k2.importing.GradleScriptData
import org.jetbrains.kotlin.gradle.scripting.k2.importing.GradleScriptModel
import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.GradleScriptDefinitionEntity
import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource
import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.asEntity
import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.deserialize
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptEntitySource
import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions
import org.jetbrains.kotlin.gradle.scripting.shared.definition.loadGradleDefinitions
import org.jetbrains.kotlin.idea.core.script.k2.configurations.sdkId
import org.jetbrains.kotlin.idea.core.script.k2.configurations.toVirtualFileUrl
import org.jetbrains.kotlin.idea.core.script.k2.definitions.ScriptDefinitionsModificationTracker
@@ -37,7 +38,6 @@ import org.jetbrains.kotlin.idea.core.script.v1.indexSourceRootsEagerly
import org.jetbrains.kotlin.idea.core.script.v1.scriptingDebugLog
import org.jetbrains.kotlin.idea.core.script.v1.scriptingWarnLog
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.*
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
@@ -48,65 +48,91 @@ import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.jdkHome
import kotlin.script.experimental.jvm.jvm
private sealed interface CachedCompilationConfigurationResult {
data class Some(val value: ScriptCompilationConfigurationResult) : CachedCompilationConfigurationResult
data object None : CachedCompilationConfigurationResult
}
private fun ScriptCompilationConfigurationResult?.cached(): CachedCompilationConfigurationResult =
if (this == null) CachedCompilationConfigurationResult.None else CachedCompilationConfigurationResult.Some(this)
@Service(Service.Level.PROJECT)
class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurationResolver, ScriptWorkspaceModelManager {
private val data = ConcurrentHashMap<VirtualFileUrl, ScriptCompilationConfigurationResult>()
private val data = ConcurrentHashMap<VirtualFileUrl, CachedCompilationConfigurationResult>()
override suspend fun create(virtualFile: VirtualFile, definition: ScriptDefinition): ScriptCompilationConfigurationResult? = null
private val urlManager: VirtualFileUrlManager
get() = project.workspaceModel.getVirtualFileUrlManager()
override fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? {
val url = virtualFile.toVirtualFileUrl(project.workspaceModel.getVirtualFileUrlManager())
val result = data.computeIfAbsent(url) { url ->
val entity = project.workspaceModel.currentSnapshot.getVirtualFileUrlIndex().findEntitiesByUrl(url)
.firstOrNull {
it.entitySource is KotlinGradleScriptEntitySource && it is KotlinScriptEntity
} as? KotlinScriptEntity
private val VirtualFile.virtualFileUrl: VirtualFileUrl
get() = toVirtualFileUrl(urlManager)
entity?.toConfigurationResult() ?: ResultWithDiagnostics.Failure()
}
override suspend fun create(virtualFile: VirtualFile, definition: ScriptDefinition): ScriptCompilationConfigurationResult {
val updatedConfiguration = refineScriptCompilationConfiguration(VirtualFileScriptSource(virtualFile), definition, project)
data[virtualFile.virtualFileUrl] = updatedConfiguration.cached()
return if (result == ResultWithDiagnostics.Failure()) null else result
return updatedConfiguration
}
fun processScripts(
override fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? {
val wrapped = data.computeIfAbsent(virtualFile.virtualFileUrl) { key ->
val entity = project.workspaceModel.currentSnapshot.getVirtualFileUrlIndex().findEntitiesByUrl(key)
.singleOrNull { it.entitySource is KotlinGradleScriptEntitySource && it is KotlinScriptEntity } as? KotlinScriptEntity
entity?.toConfigurationResult().cached()
}
return (wrapped as? CachedCompilationConfigurationResult.Some)?.value
}
fun updateStorage(
scriptData: GradleScriptData,
storageToUpdate: MutableEntityStorage
) {
val updatedStorage = MutableEntityStorage.create()
val javaHome = scriptData.definitionsParams.javaHome
val definitions = loadGradleDefinitions(scriptData.definitionsParams).map { it.withIdeKeys(project) }
updatedStorage.enrichWithDefinitions(definitions)
updatedStorage.enrichWithConfigurations(scriptData, definitions)
storageToUpdate.applyChangesFrom(updatedStorage)
val updatedStorage = MutableEntityStorage.create().apply {
updateStorage(this, scriptData.models, definitions, javaHome)
}
storageToUpdate.replaceBySource({ it is KotlinGradleScriptEntitySource }, updatedStorage)
}
private fun MutableEntityStorage.enrichWithConfigurations(
scriptsData: GradleScriptData,
definitions: List<GradleScriptDefinition>
fun updateStorage(
storage: MutableEntityStorage,
models: Collection<GradleScriptModel>,
definitions: Collection<GradleScriptDefinition>,
javaHome: String? = null
) {
val sdk = scriptsData.definitionsParams.javaHome.resolveSdk()
val javaHomePath = sdk?.homePath?.let { File(it) }
definitions.forEach {
storage addEntity GradleScriptDefinitionEntity(
it.definitionId,
it.compilationConfiguration.asEntity(),
it.hostConfiguration.asEntity(),
KotlinGradleScriptEntitySource
) {
evaluationConfiguration = it.evaluationConfiguration?.asEntity()
}
}
val configurations = scriptsData.models.associate { gradleScript: GradleScriptModel ->
val sourceCode = VirtualFileScriptSource(gradleScript.virtualFile)
val definition = definitions.firstOrNull { it.isScript(sourceCode) } ?: findScriptDefinition(project, sourceCode)
val javaHomePath = javaHome.resolveSdk()?.homePath?.let { File(it) }
val configurations = mutableMapOf<VirtualFile, ScriptCompilationConfigurationResult>()
for (model in models) {
val sourceCode = VirtualFileScriptSource(model.virtualFile)
val definition = definitions.firstOrNull { it.isScript(sourceCode) } ?: continue
val configuration = definition.compilationConfiguration.with {
if (javaHomePath != null) {
jvm.jdkHome(javaHomePath)
}
defaultImports(gradleScript.imports)
dependencies(JvmDependency(gradleScript.classPath.map { File(it) }))
ide.dependenciesSources(JvmDependency(gradleScript.sourcePath.map { File(it) }))
defaultImports(model.imports)
dependencies(JvmDependency(model.classPath.map { File(it) }))
ide.dependenciesSources(JvmDependency(model.sourcePath.map { File(it) }))
}.adjustByDefinition(definition)
val updatedConfiguration = refineScriptCompilationConfiguration(sourceCode, definition, project, configuration)
gradleScript.virtualFile to updatedConfiguration
configurations[model.virtualFile] = refineScriptCompilationConfiguration(sourceCode, definition, project, configuration)
}
enrichStorage(configurations)
storage.enrichStorage(configurations)
}
private fun String?.resolveSdk(): Sdk? {
@@ -120,46 +146,39 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
}
override suspend fun updateWorkspaceModel(configurationPerFile: Map<VirtualFile, ScriptCompilationConfigurationResult>) {
if (configurationPerFile.size == 1) {
val currentStorage = project.workspaceModel.currentSnapshot.toBuilder()
project.workspaceModel.update("updating .gradle.kts scripts") { storage ->
currentStorage.enrichStorage(configurationPerFile) // under writeAction from workspaceModel.update
storage.applyChangesFrom(currentStorage)
}
val currentStorage = project.workspaceModel.currentSnapshot.toBuilder()
project.workspaceModel.update("updating .gradle.kts scripts") { storage ->
currentStorage.enrichStorage(configurationPerFile) // under writeAction from workspaceModel.update
storage.applyChangesFrom(currentStorage)
}
}
private fun MutableEntityStorage.enrichStorage(
fun MutableEntityStorage.enrichStorage(
configurations: Map<VirtualFile, ScriptCompilationConfigurationResult>,
) {
val fileUrlManager = project.workspaceModel.getVirtualFileUrlManager()
for ((scriptFile, configurationResult) in configurations) {
val configurationWrapper = configurationResult.valueOrNull() ?: continue
if (getVirtualFileUrlIndex().findEntitiesByUrl(scriptFile.virtualFileUrl).any()) continue
val scriptUrl = scriptFile.toVirtualFileUrl(fileUrlManager)
val classes = configurationWrapper.dependenciesClassPath.sorted().map { it.path.toVirtualFileUrl(fileUrlManager) }.toMutableSet()
val sources = configurationWrapper.dependenciesSources.sorted().map { it.path.toVirtualFileUrl(fileUrlManager) }.toMutableSet()
val classes =
configurationWrapper.dependenciesClassPath.sorted().map { it.path.toVirtualFileUrl(urlManager) }.toMutableSet()
val sources = configurationWrapper.dependenciesSources.sorted().map { it.path.toVirtualFileUrl(urlManager) }.toMutableSet()
val dependencies = buildList {
addIfNotNull(
extractRootsByPredicate(classes, sources) {
it.url.contains("kotlin-stdlib")
}
)
})
addIfNotNull(
extractRootsByPredicate(classes, sources) {
it.url.contains("accessors")
}
)
})
addIfNotNull(
extractRootsByPredicate(classes, sources) {
it.url.contains("kotlin-gradle-plugin")
}
)
})
if (indexSourceRootsEagerly() || AdvancedSettings.getBoolean("gradle.attach.scripts.dependencies.sources")) {
addAll(extractDependenciesWithSources(classes, sources))
@@ -169,21 +188,17 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
addAll(
classes.map {
getOrCreateScriptLibrary(it, sources)
}
)
})
} else {
addAll(
classes.map {
getOrCreateScriptLibrary(it)
}
)
})
}
}
this addEntity KotlinScriptEntity(
scriptUrl,
dependencies,
KotlinGradleScriptEntitySource
scriptFile.virtualFileUrl, dependencies, KotlinGradleScriptEntitySource
) {
this.configuration = configurationWrapper.configuration?.asEntity()
this.reports = configurationResult.reports.map { it.toData() }.toMutableList()
@@ -193,16 +208,13 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
}
private fun MutableEntityStorage.getOrCreateScriptLibrary(
jar: VirtualFileUrl,
sources: Collection<VirtualFileUrl>
jar: VirtualFileUrl, sources: Collection<VirtualFileUrl>
): KotlinScriptLibraryEntityId {
val id = KotlinScriptLibraryEntityId(listOf(jar), sources.toList())
if (!contains(id)) {
this addEntity KotlinScriptLibraryEntity(
id.classes,
id.sources,
KotlinGradleScriptEntitySource
id.classes, id.sources, KotlinGradleScriptEntitySource
)
}
@@ -216,9 +228,7 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
if (!contains(id)) {
this addEntity KotlinScriptLibraryEntity(
id.classes,
id.sources,
KotlinGradleScriptEntitySource
id.classes, id.sources, KotlinGradleScriptEntitySource
)
}
@@ -275,9 +285,7 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
}
private fun MutableEntityStorage.extractRootsByPredicate(
classes: MutableSet<VirtualFileUrl>,
sources: MutableSet<VirtualFileUrl>,
predicate: Predicate<VirtualFileUrl>
classes: MutableSet<VirtualFileUrl>, sources: MutableSet<VirtualFileUrl>, predicate: Predicate<VirtualFileUrl>
): KotlinScriptLibraryEntityId? {
val groupedClasses = classes.removeOnMatch(predicate)
if (groupedClasses.isEmpty()) return null
@@ -305,29 +313,28 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
return removed
}
private fun MutableEntityStorage.enrichWithDefinitions(definitions: List<GradleScriptDefinition>) {
definitions.forEach {
this addEntity GradleScriptDefinitionEntity(
it.definitionId,
it.compilationConfiguration.asEntity(),
it.hostConfiguration.asEntity(),
KotlinGradleScriptEntitySource
) {
evaluationConfiguration = it.evaluationConfiguration?.asEntity()
}
}
}
@Suppress("unused")
private class GradleWorkspaceModelListener(val project: Project, val scope: CoroutineScope) : WorkspaceModelChangeListener {
override fun changed(event: VersionedStorageChange) = scope.launchTracked {
override fun beforeChanged(event: VersionedStorageChange) {
val definitionChanges = event.getChanges(GradleScriptDefinitionEntity::class.java)
val configurationChanges = event.getChanges(KotlinScriptEntity::class.java)
if (event.getChanges(GradleScriptDefinitionEntity::class.java).any()) {
if (definitionChanges.any() || configurationChanges.any()) {
KotlinScriptResolutionService.dropKotlinScriptCaches(project)
}
}
override fun changed(event: VersionedStorageChange) = scope.launchTracked {
val definitionChanges = event.getChanges(GradleScriptDefinitionEntity::class.java)
val configurationChanges = event.getChanges(KotlinScriptEntity::class.java)
if (definitionChanges.none() && configurationChanges.none()) return@launchTracked
if (definitionChanges.any()) {
ScriptDefinitionsModificationTracker.getInstance(project).incModificationCount()
}
val updatedData = mutableMapOf<VirtualFileUrl, ScriptCompilationConfigurationResult>()
for (entityChange in event.getChanges(KotlinScriptEntity::class.java)) {
for (entityChange in configurationChanges) {
if (entityChange is EntityChange.Removed) continue
val entity = entityChange.newEntity ?: continue
if (entity.entitySource !is KotlinGradleScriptEntitySource) continue
@@ -340,7 +347,6 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati
if (updatedData.any()) {
getInstance(project).data.clear()
KotlinScriptResolutionService.dropKotlinScriptCaches(project)
}
}
}
@@ -360,10 +366,8 @@ fun KotlinScriptEntity.toConfigurationResult(): ScriptCompilationConfigurationRe
} else {
ResultWithDiagnostics.Success<ScriptCompilationConfigurationWrapper>(
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(
VirtualFileScriptSource(virtualFile),
configuration?.deserialize()
), reports.map { report -> report.toScriptDiagnostic() }
)
VirtualFileScriptSource(virtualFile), configuration?.deserialize()
), reports.map { report -> report.toScriptDiagnostic() })
}
return result
@@ -13,13 +13,13 @@ import com.intellij.platform.backend.workspace.WorkspaceModel
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotificationPanel.Status
import com.intellij.ui.EditorNotificationProvider
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptEntitySource
import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle
import org.jetbrains.kotlin.idea.core.script.k2.modules.KotlinScriptLibraryEntity
import java.util.function.Function
import javax.swing.JComponent
class OpenAdvancedSettingsNotificationProvider() : EditorNotificationProvider {
class OpenAdvancedSettingsNotificationProvider : EditorNotificationProvider {
override fun collectNotificationData(
project: Project,
file: VirtualFile
@@ -2,13 +2,12 @@
package org.jetbrains.kotlin.gradle.scripting.k2
import com.intellij.openapi.application.edtWriteAction
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
import com.intellij.openapi.project.Project
import com.intellij.platform.backend.observation.launchTracked
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.suspendCancellableCoroutine
import org.jetbrains.kotlin.gradle.scripting.shared.getGradleVersion
import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootsLocator
import org.jetbrains.kotlin.idea.core.script.v1.awaitExternalSystemInitialization
import org.jetbrains.plugins.gradle.service.GradleInstallationManager
import org.jetbrains.plugins.gradle.settings.DistributionType
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
@@ -23,7 +22,7 @@ class ProjectGradleSettingsListener(
override fun onProjectsLinked(settings: MutableCollection<GradleProjectSettings>) {
val buildRootsManager = GradleBuildRootsLocator.getInstance(project)
coroutineScope.launchTracked {
awaitExternalSystemInitialization()
project.awaitExternalSystemInitialization()
settings.forEach {
val gradleVersion = getGradleVersion(project, it)
edtWriteAction {
@@ -37,7 +36,7 @@ class ProjectGradleSettingsListener(
override fun onProjectsLoaded(settings: Collection<GradleProjectSettings>) {
val buildRootsManager = GradleBuildRootsLocator.getInstance(project)
coroutineScope.launchTracked {
awaitExternalSystemInitialization()
project.awaitExternalSystemInitialization()
settings.forEach {
val gradleVersion = getGradleVersion(project, it)
edtWriteAction {
@@ -47,14 +46,6 @@ class ProjectGradleSettingsListener(
}
}
private suspend fun awaitExternalSystemInitialization() {
suspendCancellableCoroutine { continuation ->
ExternalProjectsManagerImpl.getInstance(project).runWhenInitialized {
continuation.resumeWith(Result.success(Unit))
}
}
}
override fun onProjectsUnlinked(linkedProjectPaths: MutableSet<String>) {
val buildRootsManager = GradleBuildRootsLocator.getInstance(project)
@@ -0,0 +1,63 @@
// 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.application.readAction
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.platform.workspace.storage.ImmutableEntityStorage
import com.intellij.platform.workspace.storage.toBuilder
import org.gradle.tooling.model.dsl.GradleDslBaseScriptModel
import org.jetbrains.kotlin.gradle.scripting.k2.GradleKotlinScriptService
import org.jetbrains.kotlin.gradle.scripting.shared.definition.BaseScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.definition.ErrorGradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.definition.getGradleTemplatesNames
import org.jetbrains.kotlin.idea.core.script.shared.definition.loadDefinitionsFromTemplates
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncContributor
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncPhase
import kotlin.script.experimental.api.defaultImports
import kotlin.script.experimental.api.dependencies
import kotlin.script.experimental.jvm.JvmDependency
internal class KotlinDslBaseScriptSyncContributor : GradleSyncContributor {
override val name: String = "Kotlin DSL Base Script"
override val phase: GradleSyncPhase = GradleSyncPhase.BASE_SCRIPT_MODEL_PHASE
override suspend fun createProjectModel(
context: ProjectResolverContext, storage: ImmutableEntityStorage
): ImmutableEntityStorage {
val baseModel = context.getRootModel(GradleDslBaseScriptModel::class.java)?.kotlinDslBaseScriptModel ?: return storage
val templateClasspath = (baseModel.scriptTemplatesClassPath + baseModel.compileClassPath).map { it.toPath() }
val templateClasses = getGradleTemplatesNames(context.gradleVersion)
val definitions = loadDefinitionsFromTemplates(templateClasses, templateClasspath).map {
BaseScriptDefinition(it)
}.ifEmpty { sequenceOf(ErrorGradleScriptDefinition()) }.toList().map {
it.with {
dependencies(JvmDependency(baseModel.compileClassPath))
defaultImports(baseModel.implicitImports)
}
}
val builder = storage.toBuilder()
builder.also { storage ->
val models = readAction {
FileEditorManager.getInstance(context.project).allEditors
}.filter {
it.file.name.endsWith(".gradle.kts")
}.map { fileEditor ->
GradleScriptModel(
fileEditor.file, baseModel.compileClassPath.map { it.path }, listOf(), baseModel.implicitImports
)
}
GradleKotlinScriptService.getInstance(context.project).updateStorage(
storage,
models,
definitions,
)
}
return builder.toSnapshot()
}
}
@@ -6,11 +6,9 @@ import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.platform.eel.provider.getEelDescriptor
import com.intellij.platform.eel.provider.utils.asNio
import com.intellij.platform.workspace.storage.ImmutableEntityStorage
import com.intellij.platform.workspace.storage.MutableEntityStorage
import com.intellij.platform.workspace.storage.toBuilder
import org.jetbrains.kotlin.gradle.scripting.k2.GradleKotlinScriptService
import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptEntitySource
import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleDefinitionsParams
import org.jetbrains.kotlin.gradle.scripting.shared.importing.collectErrors
import org.jetbrains.kotlin.gradle.scripting.shared.importing.getKotlinDslScripts
import org.jetbrains.kotlin.gradle.scripting.shared.importing.kotlinDslSyncListenerInstance
@@ -18,24 +16,11 @@ import org.jetbrains.kotlin.gradle.scripting.shared.importing.saveGradleBuildEnv
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.GradleSyncExtension
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncPhase
import java.nio.file.Path
import kotlin.io.path.pathString
internal class KotlinDslScriptSyncExtension : GradleSyncExtension {
override fun updateProjectModel(
context: ProjectResolverContext, syncStorage: MutableEntityStorage, projectStorage: MutableEntityStorage, phase: GradleSyncPhase
) {
if (phase == GradleSyncPhase.ADDITIONAL_MODEL_PHASE) {
projectStorage.replaceBySource({ it is KotlinGradleScriptEntitySource }, syncStorage)
}
}
}
internal class KotlinDslScriptSyncContributor : GradleSyncContributor {
override val name: String = "Kotlin DSL Script"
override val phase: GradleSyncPhase = GradleSyncPhase.ADDITIONAL_MODEL_PHASE
@@ -95,7 +80,7 @@ internal class KotlinDslScriptSyncContributor : GradleSyncContributor {
)
)
GradleKotlinScriptService.getInstance(project).processScripts(scriptData, builder)
GradleKotlinScriptService.getInstance(project).updateStorage(scriptData, builder)
return builder.toSnapshot()
}
@@ -0,0 +1,19 @@
// 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.platform.workspace.storage.MutableEntityStorage
import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncExtension
import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncPhase
internal class KotlinDslScriptSyncExtension : GradleSyncExtension {
override fun updateProjectModel(
context: ProjectResolverContext, syncStorage: MutableEntityStorage, projectStorage: MutableEntityStorage, phase: GradleSyncPhase
) {
if (phase == GradleSyncPhase.BASE_SCRIPT_MODEL_PHASE || phase == GradleSyncPhase.ADDITIONAL_MODEL_PHASE) {
projectStorage.replaceBySource({ it is KotlinGradleScriptEntitySource }, syncStorage)
}
}
}
@@ -10,13 +10,9 @@ import java.io.DataInput
import java.io.DataOutput
class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() {
override val externalizer: DataExternalizer<GradleBuildRootData>
get() = object : DataExternalizer<GradleBuildRootData> {
override fun save(out: DataOutput, value: GradleBuildRootData) =
writeKotlinDslScriptModels(out, value)
override fun read(`in`: DataInput): GradleBuildRootData =
readKotlinDslScriptModels(`in`)
override fun getExternalizer(): DataExternalizer<GradleBuildRootData> = object : DataExternalizer<GradleBuildRootData> {
override fun save(out: DataOutput, value: GradleBuildRootData) = writeKotlinDslScriptModels(out, value)
override fun read(`in`: DataInput): GradleBuildRootData = readKotlinDslScriptModels(`in`, currentBuildRoot.get().path)
}
companion object {
@@ -31,18 +27,7 @@ class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() {
strings.writeStringIds(data.projectRoots)
strings.writeStringId(data.gradleHome)
strings.writeStringId(data.javaHome)
}
@IntellijInternalApi
fun readKotlinDslScriptModels(input: DataInput): GradleBuildRootData {
val strings = StringsPool.reader(input)
val importTs = input.readLong()
val projectRoots = strings.readStrings()
val gradleHome = strings.readString()
val javaHome = strings.readNullableString()
return GradleBuildRootData(importTs, projectRoots, gradleHome, javaHome, listOf())
output.writeInt(0)
}
}
}
@@ -20,3 +20,5 @@ interface GradleScriptDefinitionEntity : WorkspaceEntityWithSymbolicId {
override val symbolicId: GradleScriptDefinitionEntityId
get() = GradleScriptDefinitionEntityId(definitionId)
}
object KotlinGradleScriptEntitySource : EntitySource
@@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.scripting.k2
import org.jetbrains.kotlin.gradle.scripting.k2.roots.GradleBuildRootDataSerializer
import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs
import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel
import org.jetbrains.kotlin.gradle.scripting.shared.roots.AbstractGradleBuildRootDataSerializer
import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootData
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
@@ -52,7 +53,7 @@ class GradleBuildRootDataSerializerTest {
val buffer = ByteArrayOutputStream()
GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), dataToWrite)
val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())))
val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a")
assertEquals(dataToRead.toString(), restored.toString())
}
@@ -70,7 +71,7 @@ class GradleBuildRootDataSerializerTest {
val buffer = ByteArrayOutputStream()
GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), data)
val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())))
val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a")
assertEquals(data.toString(), restored.toString())
}
@@ -2,8 +2,6 @@
package org.jetbrains.kotlin.gradle.scripting.shared.definition
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle
import org.jetbrains.kotlin.gradle.scripting.shared.externalProjectPath
import org.jetbrains.kotlin.gradle.scripting.shared.gradle
import org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
@@ -13,7 +11,7 @@ import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.standard.ScriptTemplateWithArgs
open class GradleScriptDefinition(
compilationConfiguration: ScriptCompilationConfiguration,
private val initialConfiguration: ScriptCompilationConfiguration,
override val hostConfiguration: ScriptingHostConfiguration,
override val evaluationConfiguration: ScriptEvaluationConfiguration?,
override val defaultCompilerOptions: Iterable<String> = emptyList(),
@@ -27,15 +25,16 @@ open class GradleScriptDefinition(
override val canDefinitionBeSwitchedOff: Boolean = false
override val compilationConfiguration: ScriptCompilationConfiguration by lazy {
compilationConfiguration.with {
mutator()
gradle {
externalProjectPath(_externalProjectPath)
initialConfiguration
.with(configurationBody)
.with {
gradle {
externalProjectPath(_externalProjectPath)
}
ide {
acceptedLocations.put(listOf(ScriptAcceptedLocation.Project))
}
}
ide {
acceptedLocations.put(listOf(ScriptAcceptedLocation.Project))
}
}
}
fun with(body: ScriptCompilationConfiguration.Builder.() -> Unit): GradleScriptDefinition {
@@ -45,7 +44,20 @@ open class GradleScriptDefinition(
)
}
open val mutator: ScriptCompilationConfiguration.Builder.() -> Unit = {}
protected open val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = {}
}
class BaseScriptDefinition(private val definition: ScriptDefinition) : GradleScriptDefinition(
definition.compilationConfiguration,
definition.hostConfiguration,
definition.evaluationConfiguration,
definition.defaultCompilerOptions,
) {
override val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = {
displayName("${definition.name} (Base)")
}
override val definitionId: String = "${super.definitionId}_base"
}
class LegacyGradleScriptDefinition(
@@ -59,7 +71,7 @@ class LegacyGradleScriptDefinition(
hostConfiguration, _legacyDefinition
), hostConfiguration, evaluationConfiguration, defaultCompilerOptions, externalProjectPath
) {
override val mutator: ScriptCompilationConfiguration.Builder.() -> Unit = {
override val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = {
@Suppress("DEPRECATION_ERROR") fileNamePattern.put(_legacyDefinition.scriptFilePattern.pattern)
}
}
@@ -71,7 +83,7 @@ class ErrorGradleScriptDefinition : GradleScriptDefinition(
hostConfiguration(ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration))
}) {
override val mutator: ScriptCompilationConfiguration.Builder.() -> Unit = {
override val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = {
fileExtension("gradle.kts")
baseClass(KotlinType(ScriptTemplateWithArgs::class))
displayName(KotlinGradleScriptingBundle.message("text.default.kotlin.gradle.script"))
@@ -1,22 +1,22 @@
// 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.shared
package org.jetbrains.kotlin.gradle.scripting.shared.definition
import com.intellij.gradle.toolingExtension.util.GradleVersionUtil.isGradleAtLeast
import com.intellij.platform.workspace.storage.EntitySource
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.gradle.scripting.shared.definition.ErrorGradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.definition.LegacyGradleScriptDefinition
import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle
import org.jetbrains.kotlin.idea.core.script.shared.definition.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.idea.core.script.v1.scriptingDebugLog
import org.jetbrains.kotlin.idea.core.script.v1.scriptingInfoLog
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import java.io.File
import java.nio.file.DirectoryStream
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.*
import kotlin.io.path.Path
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
import kotlin.io.path.name
import kotlin.script.experimental.api.ScriptCompilationConfigurationKeys
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
@@ -39,7 +39,28 @@ fun loadGradleDefinitions(params: GradleDefinitionsParams): List<GradleScriptDef
val kotlinLibsClassPath = kotlinStdlibAndCompiler(gradleLibDir)
val languageVersionCompilerOptions = findStdLibLanguageVersion(kotlinLibsClassPath)
val templateClasses = if (params.gradleVersion != null && isGradleAtLeast(params.gradleVersion, GRADLE_WITH_NEW_SCRIPTING_TEMPLATES)) {
val templateClasses = getGradleTemplatesNames(params.gradleVersion?.let { GradleVersion.version(it) })
val hostConfiguration = params.toHostConfiguration()
return loadDefinitionsFromTemplates(
templateClassNames = templateClasses,
templateClasspath = templateClasspath,
additionalResolverClasspath = kotlinLibsClassPath,
baseHostConfiguration = hostConfiguration,
defaultCompilerOptions = languageVersionCompilerOptions
).map {
it.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let { legacyDefinition ->
LegacyGradleScriptDefinition(
legacyDefinition, it.hostConfiguration, it.evaluationConfiguration, it.defaultCompilerOptions, params.workingDir
)
} ?: GradleScriptDefinition(
it.compilationConfiguration, it.hostConfiguration, it.evaluationConfiguration, it.defaultCompilerOptions, params.workingDir
)
}.ifEmpty { sequenceOf(ErrorGradleScriptDefinition()) }.toList()
}
fun getGradleTemplatesNames(gradleVersion: GradleVersion?): List<String> {
val templateClasses = if (gradleVersion != null && isGradleAtLeast(gradleVersion, GRADLE_WITH_NEW_SCRIPTING_TEMPLATES)) {
listOf(
"org.gradle.kotlin.dsl.KotlinGradleScriptTemplate",
"org.gradle.kotlin.dsl.KotlinSettingsScriptTemplate",
@@ -52,33 +73,7 @@ fun loadGradleDefinitions(params: GradleDefinitionsParams): List<GradleScriptDef
"org.gradle.kotlin.dsl.KotlinBuildScript",
)
}
val hostConfiguration = params.toHostConfiguration()
return loadDefinitionsFromTemplates(
templateClassNames = templateClasses,
templateClasspath = templateClasspath,
additionalResolverClasspath = kotlinLibsClassPath,
baseHostConfiguration = hostConfiguration,
defaultCompilerOptions = languageVersionCompilerOptions
).map {
it.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let { legacyDefinition ->
LegacyGradleScriptDefinition(
legacyDefinition,
it.hostConfiguration,
it.evaluationConfiguration,
it.defaultCompilerOptions,
params.workingDir
)
} ?: GradleScriptDefinition(
it.compilationConfiguration,
it.hostConfiguration,
it.evaluationConfiguration,
it.defaultCompilerOptions,
params.workingDir
)
}.ifEmpty {
sequenceOf(ErrorGradleScriptDefinition())
}.toList()
return templateClasses
}
private fun findStdLibLanguageVersion(kotlinLibsClassPath: List<Path>): List<String> {
@@ -89,8 +84,7 @@ private fun findStdLibLanguageVersion(kotlinLibsClassPath: List<Path>): List<Str
if (result.groupValues.size < 3) return emptyList()
val version = result.groupValues[2]
return LanguageVersion.fromVersionString(version)?.let { listOf("-language-version", it.versionString) }
?: emptyList()
return LanguageVersion.fromVersionString(version)?.let { listOf("-language-version", it.versionString) } ?: emptyList()
}
private fun GradleDefinitionsParams.toHostConfiguration(): ScriptingHostConfiguration =
@@ -107,12 +101,16 @@ private fun GradleDefinitionsParams.toHostConfiguration(): ScriptingHostConfigur
}
}
private fun String.toGradleHomePath(): Path = Path(this, "lib").let {
it.takeIf { it.exists() && it.isDirectory() }
?: error(KotlinGradleScriptingBundle.message("error.text.invalid.gradle.libraries.directory", it))
fun String?.toGradleHomePath(): Path {
if (this == null) error(KotlinGradleScriptingBundle.message("error.text.unable.to.get.gradle.home.directory"))
return Path(this, "lib").let {
it.takeIf { it.exists() && it.isDirectory() }
?: error(KotlinGradleScriptingBundle.message("error.text.invalid.gradle.libraries.directory", it))
}
}
private fun getFullDefinitionsClasspath(gradleLibDir: Path): List<Path> {
fun getFullDefinitionsClasspath(gradleLibDir: Path): List<Path> {
val templateClasspath = Files.newDirectoryStream(gradleLibDir) { kotlinDslDependencySelector.matches(it.name) }
.use(DirectoryStream<Path>::toList)
.ifEmpty { error(KotlinGradleScriptingBundle.message("error.text.missing.jars.in.gradle.directory")) }
@@ -123,8 +121,7 @@ private fun getFullDefinitionsClasspath(gradleLibDir: Path): List<Path> {
}
private fun kotlinStdlibAndCompiler(gradleLibDir: Path): List<Path> {
val stdlibPath = gradleLibDir.findFirst("kotlin-stdlib-[1-9]*")
// additionally need compiler jar to load gradle resolver
val stdlibPath = gradleLibDir.findFirst("kotlin-stdlib-[1-9]*") // additionally need compiler jar to load gradle resolver
val compilerPath = gradleLibDir.findFirst("kotlin-compiler-embeddable*")
return listOfNotNull(stdlibPath, compilerPath)
}
@@ -133,25 +130,14 @@ private fun Path.findFirst(pattern: String): Path? = Files.newDirectoryStream(th
private val kotlinDslDependencySelector = Regex("^gradle-(?:kotlin-dsl|core|base-services).*\\.jar\$")
fun getDefinitionsTemplateClasspath(gradleHome: String): List<String> = try {
getFullDefinitionsClasspath(gradleHome.toGradleHomePath()).map { it.invariantSeparatorsPathString }
} catch (e: Throwable) {
scriptingInfoLog("cannot get classpath for Gradle Kotlin DSL scripts: ${e.message}")
emptyList()
}
interface GradleScriptCompilationConfigurationKeys
open class GradleScriptCompilationConfigurationBuilder : PropertiesCollection.Builder(),
GradleScriptCompilationConfigurationKeys {
open class GradleScriptCompilationConfigurationBuilder : PropertiesCollection.Builder(), GradleScriptCompilationConfigurationKeys {
companion object : GradleScriptCompilationConfigurationKeys
}
@Suppress("UnusedReceiverParameter")
val ScriptCompilationConfigurationKeys.gradle: GradleScriptCompilationConfigurationBuilder
get() = GradleScriptCompilationConfigurationBuilder()
val GradleScriptCompilationConfigurationKeys.externalProjectPath: PropertiesCollection.Key<String?> by PropertiesCollection.key()
object KotlinGradleScriptEntitySource : EntitySource
@@ -3,16 +3,19 @@
package org.jetbrains.kotlin.gradle.scripting.shared.roots
import com.intellij.openapi.components.service
import com.intellij.openapi.util.IntellijInternalApi
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.gist.storage.GistStorage
import com.intellij.util.io.DataExternalizer
import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs
import org.jetbrains.kotlin.gradle.scripting.shared.LastModifiedFiles
import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel
import org.jetbrains.kotlin.idea.core.script.v1.readString
import org.jetbrains.kotlin.idea.core.script.v1.writeString
import java.io.DataInput
import java.io.DataOutput
private const val BINARY_FORMAT_VERSION = 1
private const val BINARY_FORMAT_VERSION = 2
private const val NO_TRACK_GIST_STAMP = 0
abstract class AbstractGradleBuildRootDataSerializer {
@@ -20,9 +23,9 @@ abstract class AbstractGradleBuildRootDataSerializer {
protected val currentBuildRoot: ThreadLocal<VirtualFile> = ThreadLocal()
private val buildRootDataGist =
GistStorage.getInstance().newGist("GradleBuildRootData", BINARY_FORMAT_VERSION, externalizer)
GistStorage.getInstance().newGist("GradleBuildRootData", BINARY_FORMAT_VERSION, getExternalizer())
protected abstract val externalizer: DataExternalizer<GradleBuildRootData>
protected abstract fun getExternalizer(): DataExternalizer<GradleBuildRootData>
fun read(buildRoot: VirtualFile): GradleBuildRootData? {
currentBuildRoot.set(buildRoot)
@@ -40,6 +43,28 @@ abstract class AbstractGradleBuildRootDataSerializer {
}
companion object {
@IntellijInternalApi
fun readKotlinDslScriptModels(input: DataInput, buildRoot: String): GradleBuildRootData {
val strings = StringsPool.reader(input)
val importTs = input.readLong()
val projectRoots = strings.readStrings()
val gradleHome = strings.readString()
val javaHome = strings.readNullableString()
val models = input.readList {
KotlinDslScriptModel(
strings.readString(),
GradleKotlinScriptConfigurationInputs(input.readString(), input.readLong(), buildRoot),
strings.readStrings(),
strings.readStrings(),
strings.readStrings(),
listOf()
)
}
return GradleBuildRootData(importTs, projectRoots, gradleHome, javaHome, models)
}
@JvmStatic
fun getInstance(): AbstractGradleBuildRootDataSerializer = service()
}
@@ -4,10 +4,12 @@ package org.jetbrains.kotlin.gradle.scripting.shared
import com.intellij.gradle.toolingExtension.util.GradleVersionUtil
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.LeafPsiElement
import kotlinx.coroutines.suspendCancellableCoroutine
import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootsLocator
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile
@@ -0,0 +1,42 @@
// FILE: build.gradle.kts
// ALLOW_ERRORS
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
dependencies {
implementation(project(":module1"))
}
// FILE: settings.gradle.kts
include("module1")
// FILE: build.gradle.kts.highlighting.k2
// ALLOW_ERRORS
<!HIGHLIGHTING("severity='DSL_TYPE_SEVERITY'")!>plugins<!> {
<!HIGHLIGHTING("severity='SYMBOL_TYPE_SEVERITY'")!>`kotlin-dsl`<!>
}
<!HIGHLIGHTING("severity='SYMBOL_TYPE_SEVERITY'")!>repositories<!> {
<!HIGHLIGHTING("severity='SYMBOL_TYPE_SEVERITY'")!>mavenCentral<!>()
}
<!HIGHLIGHTING("severity='SYMBOL_TYPE_SEVERITY'")!>dependencies<!> {
<!HIGHLIGHTING("severity='ERROR'; descr='[UNRESOLVED_REFERENCE] Unresolved reference 'implementation'.'")!>implementation<!>(<!HIGHLIGHTING("severity='SYMBOL_TYPE_SEVERITY'")!>project<!>(":module1"))
}
// FILE: settings.gradle.kts
include("module1")
// FILE: module1/build.gradle.kts
plugins {
java
}
repositories {
mavenCentral()
}