diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/SdkDataService.kt b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/SdkDataService.kt index d0fcede8fe0c3..f361e63921778 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/SdkDataService.kt +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/SdkDataService.kt @@ -39,7 +39,7 @@ internal class ProjectSdkDataService : AbstractProjectDataService, GradleExecutionSettings> getExecutionSett if (!StringUtil.isEmpty(javaHome)) { LOG.info("Instructing gradle to use java from " + javaHome); } - result.setJavaHome(javaHome); + + if (projectLevelSettings == null || !GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(projectLevelSettings)) { + result.setJavaHome(javaHome); + } + GradleSystemSettings systemSettings = GradleSystemSettings.getInstance(); String vmOptions = Objects.requireNonNullElse(settings.getGradleVmOptions(), ""); if (vmOptions.contains("-Didea.gradle.download.sources.force=false")) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAware.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAware.kt index c3bec94b52c86..f5d8b6f1c991e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAware.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAware.kt @@ -93,6 +93,9 @@ class LocalGradleExecutionAware : GradleExecutionAware { ): SdkInfo? { val settings = project.lock { GradleSettings.getInstance(it) } val projectSettings = settings.getLinkedProjectSettings(externalProjectPath) ?: return null + // Projects using Daemon JVM criteria with a compatible Gradle version will skip any + // Gradle JDK configuration validation since this will be delegated to Gradle + if (GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(projectSettings)) return null val originalGradleJvm = projectSettings.gradleJvm val provider = project.lock { getGradleJvmLookupProvider(it, projectSettings) } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleProjectSettingsUpdater.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleProjectSettingsUpdater.kt index 235782d5972ea..031d5e5c5dbfa 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleProjectSettingsUpdater.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/startup/GradleProjectSettingsUpdater.kt @@ -22,6 +22,7 @@ import com.intellij.openapi.util.registry.Registry import com.intellij.util.lang.JavaVersion import org.jetbrains.plugins.gradle.GradleManager import org.jetbrains.plugins.gradle.jvmcompat.GradleJvmSupportMatrix +import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper import org.jetbrains.plugins.gradle.service.project.GradleNotification import org.jetbrains.plugins.gradle.service.project.GradleNotificationIdsHolder import org.jetbrains.plugins.gradle.settings.GradleProjectSettings @@ -116,6 +117,7 @@ internal class GradleProjectSettingsUpdater : ExternalSystemSettingsListenerEx { if (manager !is GradleManager) return for (projectSettings in settings) { if (projectSettings !is GradleProjectSettings) continue + if (GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(projectSettings)) continue val statusFuture = Util.updateGradleJvm(project, projectSettings) statusFuture.thenAccept { if (it.updated && it.sdkName != null) notifyGradleJvmChangeInfo(project, projectSettings, it.sdkName, it.sdk) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/statistics/GradleSettingsCollector.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/statistics/GradleSettingsCollector.kt index 31380848932ab..19bc5a4b2ea27 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/statistics/GradleSettingsCollector.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/statistics/GradleSettingsCollector.kt @@ -15,12 +15,14 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.Version import org.gradle.util.GradleVersion import org.jetbrains.plugins.gradle.properties.GradlePropertiesFile +import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper import org.jetbrains.plugins.gradle.settings.DistributionType import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.settings.GradleSettings import org.jetbrains.plugins.gradle.settings.GradleSystemSettings import org.jetbrains.plugins.gradle.settings.TestRunner import org.jetbrains.plugins.gradle.util.GradleConstants +import java.nio.file.Path import java.nio.file.Paths internal class GradleSettingsCollector : ProjectUsagesCollector() { @@ -100,6 +102,7 @@ internal class GradleSettingsCollector : ProjectUsagesCollector() { ?.let { it.jsonString.length > 2 } ?: false usages.add(IDEA_SPECIFIC_CONFIGURATION_USED.metric(hasNonEmptyIntellijConfig)) + usages.add(GRADLE_DAEMON_JVM_CRITERIA_DEFINED.metric(GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(Path.of(projectPath), gradleVersion))) } private fun collectGradlePropertiesMetrics(usages: MutableSet, project: Project, externalProjectPath: String) { @@ -144,6 +147,7 @@ internal class GradleSettingsCollector : ProjectUsagesCollector() { EventFields.Enum("value", TestRunner::class.java) { it.name.lowercase() }) private val GRADLE_JVM_TYPE = GROUP.registerEvent("gradleJvmType", JRE_TYPE_FIELD) private val GRADLE_JVM_VERSION = GROUP.registerEvent("gradleJvmVersion", VERSION_FIELD) + private val GRADLE_DAEMON_JVM_CRITERIA_DEFINED = GROUP.registerEvent("gradleDaemonJvmCriteriaDefined", EventFields.Enabled) private val GRADLE_DOWNLOAD_DEPENDENCY_SOURCES = GROUP.registerEvent("gradleDownloadDependencySources", EventFields.Enabled) private val GRADLE_PARALLEL_MODEL_FETCH = GROUP.registerEvent("gradleParallelModelFetch", EventFields.Enabled) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmResolutionUtil.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmResolutionUtil.kt index e9cf8e000f7ec..c29877e692efc 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmResolutionUtil.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmResolutionUtil.kt @@ -16,6 +16,7 @@ import org.gradle.util.GradleVersion import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.gradle.jvmcompat.GradleJvmSupportMatrix import org.jetbrains.plugins.gradle.properties.GradlePropertiesFile +import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.settings.GradleSettings import org.jetbrains.plugins.gradle.util.JavaHomeValidationStatus.Success @@ -28,6 +29,10 @@ fun getGradleJvmLookupProvider(project: Project, projectSettings: GradleProjectS SdkLookupProvider.getInstance(project, GradleJvmProviderId(projectSettings)) fun setupGradleJvm(project: Project, projectSettings: GradleProjectSettings, gradleVersion: GradleVersion) { + // Projects using Daemon JVM criteria with a compatible Gradle version will skip the + // Gradle JVM setup since this will be delegated to Gradle + if (GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(projectSettings)) return + val resolutionContext = GradleJvmResolutionContext(project, Paths.get(projectSettings.externalProjectPath), gradleVersion) projectSettings.gradleJvm = resolutionContext.findGradleJvm() if (projectSettings.gradleJvm != null) { @@ -85,6 +90,7 @@ fun updateGradleJvm(project: Project, externalProjectPath: String) { val projectRootManager = ProjectRootManager.getInstance(project) val projectSdk = projectRootManager.projectSdk ?: return if (projectSdk.name != gradleJvm) return + if (GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(projectSettings)) return projectSettings.gradleJvm = ExternalSystemJdkUtil.USE_PROJECT_JDK } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmValidationUtil.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmValidationUtil.kt index 7b7c87c43a4c6..1721d87485860 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmValidationUtil.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleJvmValidationUtil.kt @@ -23,6 +23,7 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.gradle.jvmcompat.GradleJvmSupportMatrix import org.jetbrains.plugins.gradle.properties.GradlePropertiesFile import org.jetbrains.plugins.gradle.properties.models.Property +import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper import org.jetbrains.plugins.gradle.service.project.GradleNotification import org.jetbrains.plugins.gradle.service.project.GradleNotificationIdsHolder import java.io.File @@ -30,6 +31,10 @@ import java.nio.file.Path import javax.swing.event.HyperlinkEvent fun validateJavaHome(project: Project, externalProjectPath: Path, gradleVersion: GradleVersion) { + // Projects using Daemon JVM criteria with a compatible Gradle version + // will ignore Java Home from environment variables or Gradle Properties + if (GradleDaemonJvmHelper.isProjectUsingDaemonJvmCriteria(externalProjectPath, gradleVersion)) return + val gradleProperties = GradlePropertiesFile.getProperties(project, externalProjectPath) val javaHomeProperty = gradleProperties.javaHomeProperty if (javaHomeProperty != null) { diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTest.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTest.kt index 09d2988147282..e0f9c07288940 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTest.kt +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTest.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.roots.ui.configuration.SdkTestCase.Companion.assertU import com.intellij.openapi.roots.ui.configuration.SdkTestCase.Companion.withRegisteredSdks import com.intellij.openapi.roots.ui.configuration.SdkTestCase.TestSdkGenerator import kotlinx.coroutines.runBlocking +import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions import org.junit.Test class GradleProjectSdkResolverTest : GradleProjectSdkResolverTestCase() { @@ -63,4 +64,22 @@ class GradleProjectSdkResolverTest : GradleProjectSdkResolverTestCase() { } } } + + @Test + @TargetVersions("8.8+") + fun `test project using Daemon toolchain`() = runBlocking { + val jdk = resolveRealTestSdk() + val sdk = TestSdkGenerator.createNextSdk() + createGradleSubProject() + createDaemonJvmPropertiesFile(jdk) + + environment.withVariables(JAVA_HOME to sdk.homePath) { + withRegisteredSdks(jdk, sdk) { + assertUnexpectedSdksRegistration { + loadProject() + assertSdks(jdk, "project", "project.main", "project.test") + } + } + } + } } \ No newline at end of file diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTestCase.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTestCase.kt index 66f583a80c5dd..4eb14b36192b8 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTestCase.kt +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleProjectSdkResolverTestCase.kt @@ -19,7 +19,9 @@ import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.roots.ui.configuration.SdkTestCase import com.intellij.openapi.roots.ui.configuration.SdkTestCase.Companion.assertSdk import com.intellij.openapi.roots.ui.configuration.SdkTestCase.TestSdkGenerator +import com.intellij.testFramework.VfsTestUtil import com.intellij.testFramework.replaceService +import com.intellij.util.lang.JavaVersion import org.jetbrains.plugins.gradle.service.project.open.linkAndSyncGradleProject import org.jetbrains.plugins.gradle.testFramework.util.awaitGradleProjectConfiguration import org.jetbrains.plugins.gradle.testFramework.util.createBuildFile @@ -116,4 +118,9 @@ abstract class GradleProjectSdkResolverTestCase : GradleImportingTestCase() { withJavaPlugin() } } + + fun createDaemonJvmPropertiesFile(sdk: Sdk) { + val version = JavaVersion.tryParse(sdk.versionString!!) + VfsTestUtil.createFile(projectRoot, "gradle/gradle-daemon-jvm.properties", "toolchainVersion=$version") + } } diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAwareTest.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAwareTest.kt new file mode 100644 index 0000000000000..d7355911fe7da --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/service/execution/LocalGradleExecutionAwareTest.kt @@ -0,0 +1,79 @@ +package org.jetbrains.plugins.gradle.service.execution + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.externalSystem.model.ProjectSystemId +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType +import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkException +import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkProvider +import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil.USE_INTERNAL_JAVA +import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil.USE_JAVA_HOME +import com.intellij.openapi.externalSystem.service.internal.AbstractExternalSystemTask +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ui.configuration.SdkLookupProvider +import com.intellij.testFramework.LightPlatformTestCase +import com.intellij.testFramework.VfsTestUtil +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings +import org.jetbrains.plugins.gradle.settings.GradleSettings + +class LocalGradleExecutionAwareTest : LightPlatformTestCase() { + + fun `test Project without linked projectSettings When prepare JVM execution Then any validation is skipped`() { + prepareJvmForExecution().run { + assertNull(this) + } + } + + fun `test Given project using valid JAVA_INTERNAL When prepare JVM execution Then SDK info is returned as resolved`() { + GradleSettings.getInstance(project).linkedProjectsSettings = listOf(GradleProjectSettings().apply { + this.externalProjectPath = project.basePath + this.gradleJvm = USE_INTERNAL_JAVA + }) + (prepareJvmForExecution() as SdkLookupProvider.SdkInfo.Resolved).run { + assertEquals(ExternalSystemJdkProvider.getInstance().getInternalJdk().name, name) + assertEquals(ExternalSystemJdkProvider.getInstance().getInternalJdk().homePath, homePath) + } + } + + fun `test Given project using Daemon toolchain When prepare JVM execution Then throws expected exception`() { + GradleSettings.getInstance(project).linkedProjectsSettings = listOf(GradleProjectSettings().apply { + this.externalProjectPath = project.basePath + this.gradleJvm = "Invalid jdk.table entry" + }) + + assertThrows(ExternalSystemJdkException::class.java) { + ApplicationManager.getApplication().executeOnPooledThread { + prepareJvmForExecution() + } + } + } + + fun `test Given project using Daemon toolchain When prepare JVM execution Then any validation is skipped`() { + VfsTestUtil.createFile(project.baseDir, "gradle/gradle-daemon-jvm.properties", "toolchainVersion=17") + GradleSettings.getInstance(project).linkedProjectsSettings = listOf(GradleProjectSettings().apply { + this.externalProjectPath = project.basePath + this.gradleJvm = USE_JAVA_HOME + }) + prepareJvmForExecution().run { + assertNull(this) + } + } + + private fun prepareJvmForExecution() = + LocalGradleExecutionAware().prepareJvmForExecution(DummyTask(project), project.basePath!!, DummyTaskNotificationListener(), project) + + private class DummyTask(project: Project) : AbstractExternalSystemTask( + ProjectSystemId.IDE, ExternalSystemTaskType.EXECUTE_TASK, project, "" + ) { + override fun doCancel(): Boolean = true + + override fun doExecute() {} + } + + private class DummyTaskNotificationListener : ExternalSystemTaskNotificationListener { + override fun onStart(id: ExternalSystemTaskId, workingDir: String?) {} + + override fun onEnd(id: ExternalSystemTaskId) {} + } +} \ No newline at end of file