Skip to content

Commit

Permalink
IDEA-358562 Configure Daemon JVM criteria when creating project via NPW
Browse files Browse the repository at this point in the history
  • Loading branch information
vmadalin committed Dec 5, 2024
1 parent 140a5ea commit f27aba3
Show file tree
Hide file tree
Showing 18 changed files with 151 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.intellij.openapi.externalSystem.model.project.ModuleSdkData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.model.project.ProjectId;
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl;
import com.intellij.openapi.externalSystem.service.project.wizard.AbstractExternalModuleBuilder;
Expand Down Expand Up @@ -57,6 +58,7 @@
import org.jetbrains.plugins.gradle.frameworkSupport.buildscript.GradleBuildScriptBuilder;
import org.jetbrains.plugins.gradle.frameworkSupport.settingsScript.GradleSettingScriptBuilder;
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper;
import org.jetbrains.plugins.gradle.service.project.wizard.util.GradleWrapperUtil;
import org.jetbrains.plugins.gradle.settings.DistributionType;
import org.jetbrains.plugins.gradle.settings.GradleDefaultProjectSettings;
Expand Down Expand Up @@ -257,9 +259,14 @@ private void finishModuleSetup(@Nullable VirtualFile buildScriptFile, @NotNull P
}
ExternalProjectsManagerImpl.getInstance(project).runWhenInitialized(() -> {
if (isUsingDaemonToolchain()) {
SimpleMigration.INSTANCE.startMigration(project, getExternalProjectSettings().getExternalProjectPath()).whenComplete((result, exception) -> {
reloadProject(project);
});
GradleDaemonJvmHelper.setUpProjectDaemonJvmCriteria(project, getExternalProjectSettings().getExternalProjectPath(), null,
ProgressExecutionMode.IN_BACKGROUND_ASYNC, false)
.whenComplete((result, exception) -> {
if (exception != null) {
LOG.warn(exception);
}
reloadProject(project);
});
} else {
reloadProject(project);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ abstract class GradleNewProjectWizardStep<ParentStep>(parent: ParentStep) :
builder.contentEntryPath = parentStep.path + "/" + parentStep.name

builder.isCreatingNewProject = context.isCreatingNewProject
builder.isUsingDaemonToolchain = Registry.`is`("gradle.daemon.jvm.criteria.new.projects") && isDaemonJvmCriteriaSupported(gradleVersion)
builder.isUsingDaemonToolchain = context.isCreatingNewProject && Registry.`is`("gradle.daemon.jvm.criteria.new.projects") && isDaemonJvmCriteriaSupported(gradleVersion)

builder.parentProject = parentData
builder.projectId = ProjectId(groupId, artifactId, version)
Expand Down
2 changes: 2 additions & 0 deletions plugins/gradle/resources/messages/GradleBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ column.name.daemon.timestamp=Timestamp
column.name.daemon.info=Info
gradle.execution.name.build.project.=Build {0}
grable.execution.name.upgrade.wrapper=Upgrade Gradle wrapper
gradle.execution.name.update.daemon.jvm.criteria=Update Daemon JVM Criteria
gradle.execution.name.config.daemon.jvm.criteria=Configure Daemon JVM Criteria
gradle.target.configure.label=Gradle configuration
gradle.target.run.label=Run Gradle task
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.gradle.extensions
package org.jetbrains.plugins.gradle.extensions

import org.jetbrains.jps.model.java.JdkVersionDetector.Variant

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ const val GRADLE_FOLDER = "gradle"

object GradleDaemonJvmPropertiesFile {
fun getProperties(externalProjectPath: Path): GradleDaemonJvmProperties? {
val propertiesPath = getGradleDaemonJvmPropertiesPath(externalProjectPath)
val propertiesPath = getPropertiesPath(externalProjectPath)
return loadGradleDaemonJvmProperties(propertiesPath)
}

fun getPropertiesPath(externalProjectPath: Path) =
externalProjectPath.resolve(Paths.get(GRADLE_FOLDER, GRADLE_DAEMON_JVM_PROPERTIES_FILE_NAME)).toAbsolutePath().normalize()

private fun loadGradleDaemonJvmProperties(propertiesPath: Path): GradleDaemonJvmProperties? {
val properties = GradleUtil.readGradleProperties(propertiesPath) ?: return null
return GradleDaemonJvmPropertiesImpl(
version = properties.getStringProperty(GRADLE_DAEMON_JVM_VERSION_PROPERTY, propertiesPath),
vendor = properties.getStringProperty(GRADLE_DAEMON_JVM_VENDOR_PROPERTY, propertiesPath)
)
}

private fun getGradleDaemonJvmPropertiesPath(externalProjectPath: Path): Path {
return externalProjectPath.resolve(Paths.get(GRADLE_FOLDER, GRADLE_DAEMON_JVM_PROPERTIES_FILE_NAME)).toAbsolutePath().normalize()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ import com.intellij.openapi.util.registry.Registry
import org.gradle.internal.buildconfiguration.DaemonJvmPropertiesConfigurator
import org.gradle.util.GradleVersion
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.jps.model.java.JdkVersionDetector
import org.jetbrains.plugins.gradle.extensions.nameSupportedByFoojayPlugin
import org.jetbrains.plugins.gradle.properties.GradleDaemonJvmPropertiesFile
import org.jetbrains.plugins.gradle.service.GradleInstallationManager
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import org.jetbrains.plugins.gradle.util.GradleBundle
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
import kotlin.io.path.Path
import kotlin.io.path.exists

private val MIN_VERSION_SUPPORTING_DAEMON_TOOLCHAIN_VERSION_CRITERIA: GradleVersion = GradleVersion.version("8.8")
private val MIN_VERSION_SUPPORTING_DAEMON_TOOLCHAIN_VENDOR_CRITERIA: GradleVersion = GradleVersion.version("8.10")
Expand Down Expand Up @@ -78,6 +85,7 @@ object GradleDaemonJvmHelper {
daemonJvmCriteria: GradleDaemonJvmCriteria?,
taskCallback: TaskCallback? = null,
executionMode: ProgressExecutionMode = ProgressExecutionMode.START_IN_FOREGROUND_ASYNC,
runInToolWindow: Boolean = true
) {
val taskSettings = ExternalSystemTaskExecutionSettings().apply {
this.externalProjectPath = externalProjectPath
Expand All @@ -86,6 +94,11 @@ object GradleDaemonJvmHelper {
daemonJvmCriteria?.version?.let { add("$UPDATE_DAEMON_JVM_TASK_VERSION_OPTION=$it") }
daemonJvmCriteria?.vendor?.let { add("$UPDATE_DAEMON_JVM_TASK_VENDOR_OPTION=$it") }
}
executionName = if (GradleDaemonJvmPropertiesFile.getPropertiesPath(Path(externalProjectPath)).exists()) {
GradleBundle.message("gradle.execution.name.update.daemon.jvm.criteria")
} else {
GradleBundle.message("gradle.execution.name.config.daemon.jvm.criteria")
}
}

val taskUserData = UserDataHolderBase().apply {
Expand All @@ -94,11 +107,52 @@ object GradleDaemonJvmHelper {

val executionSpec = TaskExecutionSpec.create(project, GradleConstants.SYSTEM_ID, DefaultRunExecutor.EXECUTOR_ID, taskSettings)
.withProgressExecutionMode(executionMode)
.withActivateToolWindowBeforeRun(true)
.withActivateToolWindowBeforeRun(runInToolWindow)
.withUserData(taskUserData)
.withCallback(taskCallback)
.build()

ExternalSystemUtil.runTask(executionSpec)
}

/**
* Set up project [Daemon JVM criteria](https://docs.gradle.org/current/userguide/gradle_daemon.html#daemon_jvm_criteria) based on
* existing Gradle JDK configuration.
*/
@JvmStatic
fun setUpProjectDaemonJvmCriteria(
project: Project,
externalProjectPath: String,
onStartingSetUpCriteria: (() -> Unit)? = null,
executionMode: ProgressExecutionMode = ProgressExecutionMode.START_IN_FOREGROUND_ASYNC,
runInToolWindow: Boolean = true
): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
val currentJdkInfo = GradleInstallationManager.getInstance().getGradleJvmPath(project, externalProjectPath)?.let {
JdkVersionDetector.getInstance().detectJdkVersionInfo(it)
}
if (currentJdkInfo?.version == null) {
future.completeExceptionally(RuntimeException("Unable to obtain current Gradle JDK configuration to set up Daemon JVM criteria"))
return future
}

onStartingSetUpCriteria?.invoke()
updateProjectDaemonJvmCriteria(
project,
externalProjectPath,
GradleDaemonJvmCriteria(
version = currentJdkInfo.version.feature.toString(),
vendor = currentJdkInfo.variant.nameSupportedByFoojayPlugin
), object : TaskCallback {
override fun onSuccess() {
future.complete(Unit)
}

override fun onFailure() {
future.completeExceptionally(RuntimeException("Unable to set up Daemon JVM criteria based on current Gradle JDK configuration"))
}
}, executionMode, runInToolWindow)

return future
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class GradleDaemonJvmPropertiesFileTest : LightPlatformTestCase() {

fun testNotPresentProjectGradleDaemonJvmPropertiesFile() {
assertNull(GradleDaemonJvmPropertiesFile.getProperties(externalProjectPath))
assertEquals(gradleDaemonJvmPropertiesPath, GradleDaemonJvmPropertiesFile.getPropertiesPath(externalProjectPath).toString())
}

fun testEmptyProjectGradleDaemonJvmPropertiesFile() = testGradleDaemonJvmProperties(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ abstract class GradleBaseTestCase {

val testRoot: VirtualFile get() = gradleTestFixture.testRoot
val gradleJvm: String get() = gradleTestFixture.gradleJvm
val gradleJvmPath: String get() = gradleTestFixture.gradleJvmPath
val gradleVersion: GradleVersion get() = gradleTestFixture.gradleVersion

@BeforeEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface GradleTestFixture : IdeaTestFixture {

val gradleJvm: String

val gradleJvmPath: String

val gradleVersion: GradleVersion

suspend fun openProject(relativePath: String, numProjectSyncs: Int = 1): Project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class GradleJvmTestFixture(
val gradleJvm: String
get() = sdk.name

val gradleJvmPath: String
get() = sdk.homePath.orEmpty()

override fun setUp() {
fixtureDisposable = Disposer.newDisposable()
sdk = GradleJvmResolver.resolveGradleJvm(gradleVersion, fixtureDisposable, javaVersionRestriction)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class GradleTestFixtureImpl(

override lateinit var gradleJvm: String

override lateinit var gradleJvmPath: String

override fun setUp() {
reloadLeakTracker = OperationLeakTracker { getGradleProjectReloadOperation(it) }
reloadLeakTracker.setUp()
Expand All @@ -57,6 +59,7 @@ class GradleTestFixtureImpl(
gradleJvmFixture.setUp()
gradleJvmFixture.installProjectSettingsConfigurator()
gradleJvm = gradleJvmFixture.gradleJvm
gradleJvmPath = gradleJvmFixture.gradleJvmPath

fileFixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture()
fileFixture.setUp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.components.Service
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil.USE_PROJECT_JDK
import com.intellij.openapi.externalSystem.task.TaskCallback
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import org.jetbrains.jps.model.java.JdkVersionDetector
import org.jetbrains.kotlin.idea.gradle.extensions.nameSupportedByFoojayPlugin
import org.jetbrains.kotlin.idea.gradleCodeInsightCommon.GradleBuildScriptSupport
import org.jetbrains.kotlin.idea.gradleCodeInsightCommon.getTopLevelBuildScriptSettingsPsiFile
import org.jetbrains.plugins.gradle.service.GradleInstallationManager
import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmCriteria
import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper
import org.jetbrains.plugins.gradle.settings.GradleSettings
import org.jetbrains.plugins.gradle.util.GradleBundle
Expand All @@ -22,32 +17,19 @@ import org.jetbrains.plugins.gradle.util.GradleBundle
class GradleDaemonToolchainMigrationService(private val project: Project) {

fun startMigration(externalProjectPath: String) {
val currentJdkInfo = GradleInstallationManager.getInstance().getGradleJvmPath(project, externalProjectPath)?.let {
JdkVersionDetector.getInstance().detectJdkVersionInfo(it)
}
if (currentJdkInfo?.version == null) {
displayMigrationFailureMessage()
return
}

applyDefaultToolchainResolverPlugin()

GradleDaemonJvmHelper.updateProjectDaemonJvmCriteria(
GradleDaemonJvmHelper.setUpProjectDaemonJvmCriteria(
project,
externalProjectPath,
GradleDaemonJvmCriteria(
version = currentJdkInfo.version.feature.toString(),
vendor = currentJdkInfo.variant.nameSupportedByFoojayPlugin
), object : TaskCallback {
override fun onSuccess() {
overrideGradleJvmReferenceWithDefault(externalProjectPath)
}

override fun onFailure() {
displayMigrationFailureMessage()
}
onStartingSetUpCriteria = {
applyDefaultToolchainResolverPlugin()
}
)
).whenComplete { _, exception ->
if (exception != null) {
displayMigrationFailureMessage()
} else {
overrideGradleJvmReferenceWithDefault(externalProjectPath)
}
}
}

private fun applyDefaultToolchainResolverPlugin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUt
import com.intellij.openapi.roots.ui.configuration.SdkTestCase.Companion.withRegisteredSdks
import kotlinx.coroutines.runBlocking
import org.jetbrains.jps.model.java.JdkVersionDetector
import org.jetbrains.kotlin.idea.gradle.extensions.nameSupportedByFoojayPlugin
import org.jetbrains.kotlin.idea.gradleCodeInsightCommon.getTopLevelBuildScriptSettingsPsiFile
import org.jetbrains.kotlin.idea.gradleJava.toolchain.GradleDaemonToolchainMigrationService
import org.jetbrains.plugins.gradle.extensions.nameSupportedByFoojayPlugin
import org.jetbrains.plugins.gradle.importing.GradleProjectSdkResolverTestCase
import org.jetbrains.plugins.gradle.properties.GradleDaemonJvmPropertiesFile
import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.jetbrains.kotlin.idea.gradle.externsions

import com.intellij.testFramework.junit5.TestApplication
import org.jetbrains.jps.model.java.JdkVersionDetector.Variant
import org.jetbrains.kotlin.idea.gradle.extensions.nameSupportedByFoojayPlugin
import org.jetbrains.plugins.gradle.extensions.nameSupportedByFoojayPlugin
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import com.intellij.testFramework.common.runAll
import com.intellij.testFramework.useProjectAsync
import com.intellij.testFramework.utils.module.assertModules
import kotlinx.coroutines.runBlocking
import org.jetbrains.jps.model.java.JdkVersionDetector
import org.jetbrains.kotlin.idea.base.test.TestRoot
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.tools.projectWizard.BuildSystemKotlinNewProjectWizardData.Companion.kotlinBuildSystemData
import org.jetbrains.kotlin.tools.projectWizard.gradle.GradleKotlinNewProjectWizardData.Companion.kotlinGradleData
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
import org.jetbrains.plugins.gradle.properties.GRADLE_FOLDER
import org.jetbrains.plugins.gradle.service.project.wizard.GradleNewProjectWizardStep.GradleDsl
import org.jetbrains.plugins.gradle.setup.GradleCreateProjectTestCase
import org.jetbrains.plugins.gradle.testFramework.util.ProjectInfo
Expand Down Expand Up @@ -52,7 +54,7 @@ class GradleKotlinNewProjectWizardTest : GradleCreateProjectTestCase(), NewKotli

@AfterEach
fun tearDown() {
runAll({ KotlinSdkType.removeKotlinSdkInTests() })
runAll({ KotlinSdkType.removeKotlinSdkInTests() }, { Registry.getInstance().restoreDefaults() })
}

override fun getTestFolderName(): String {
Expand All @@ -75,12 +77,25 @@ class GradleKotlinNewProjectWizardTest : GradleCreateProjectTestCase(), NewKotli
return kotlinVersionRegex.find(buildFile!!.readText())?.groupValues?.get(1)
}

private fun Project.findDaemonJvmVersion(): Int? {
val projectPath = File(basePath.orEmpty())
val daemonJvmPropertiesFile = projectPath.resolve(File(GRADLE_FOLDER, "gradle-daemon-jvm.properties"))
val daemonJvmPropertiesText = daemonJvmPropertiesFile.readText()
val toolchainVersionRegex = Regex("""toolchainVersion=(\d+)""")

return toolchainVersionRegex.find(daemonJvmPropertiesText)?.groupValues?.get(1)?.toIntOrNull()
}

private fun Project.assertKotlinVersion(expectedVersion: String, useKotlinDsl: Boolean, modulePath: String? = null) {
val kotlinVersion = findKotlinVersion(useKotlinDsl, modulePath)
Assertions.assertNotNull(kotlinVersion, "Could not find Kotlin version in build file")
Assertions.assertEquals(expectedVersion, kotlinVersion)
}

private fun Project.assertDaemonJvmCriteria(expectedVersion: Int) {
Assertions.assertEquals(expectedVersion, findDaemonJvmVersion())
}

private val kotlinVersionRegex = Regex("""kotlin.*version.*["']([\w-.]*)["']""")
override fun substituteArtifactsVersions(str: String): String {
return str.replaceFirstGroup(kotlinVersionRegex, "KOTLIN_VERSION")
Expand All @@ -101,6 +116,11 @@ class GradleKotlinNewProjectWizardTest : GradleCreateProjectTestCase(), NewKotli
return str.replaceFirstGroup(tomlVersionRegex(libraryName), libraryReplacementName)
}

private val daemonJvmCriteriaRegex = Regex("""toolchainVersion=(\d+)""")
private fun substituteDaemonJvmCriteriaVersions(str: String): String {
return str.replaceFirstGroup(daemonJvmCriteriaRegex, "TOOLCHAIN_VERSION")
}

// We replace dynamic values like the Kotlin version that is used with placeholders.
// That way we do not have to update tests every time a new Kotlin version releases.
override fun postprocessOutputFile(relativePath: String, fileContents: String): String {
Expand Down Expand Up @@ -187,6 +207,15 @@ class GradleKotlinNewProjectWizardTest : GradleCreateProjectTestCase(), NewKotli
runNewProjectTestCase(useKotlinDsl = true)
}

@Test
fun testSimpleProjectUsingDaemonJvmCriteria() {
Registry.get("gradle.daemon.jvm.criteria.new.project").setValue(true)
val gradleJdkVersionInfo = JdkVersionDetector.getInstance().detectJdkVersionInfo(gradleJvmPath)
runNewProjectTestCase(additionalAssertions = {
it.assertDaemonJvmCriteria(gradleJdkVersionInfo!!.version.feature)
})
}

@Test
fun testSampleCode() {
runNewProjectTestCase(addSampleCode = true)
Expand Down
Loading

0 comments on commit f27aba3

Please sign in to comment.