Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDEA-358562 Add error handling for Daemon toolchain #2881

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class JdkDownloadService(private val project: Project, private val coroutineScop
val model = ProjectStructureConfigurable.getInstance(project).projectJdksModel
coroutineScope.launch(Dispatchers.EDT) {
writeAction {
model.syncSdks()
vmadalin marked this conversation as resolved.
Show resolved Hide resolved
model.setupInstallableSdk(JavaSdkImpl.getInstance(), downloadTask) { sdk ->
ProjectJdkTable.getInstance().addJdk(sdk)
setSdk(sdk)
Expand All @@ -88,17 +89,7 @@ class JdkDownloadService(private val project: Project, private val coroutineScop
}
}

private fun doScheduleDownloadJdk(sdkDownloadTask: JdkDownloadTask, module: Module?, isCreatingNewProject: Boolean): CompletableFuture<Boolean> {
val setSdk: (Sdk?) -> Unit = { sdk ->
if (isCreatingNewProject) {
ProjectRootManager.getInstance(project).projectSdk = sdk
}
else {
ModuleRootManager.getInstance(module!!).modifiableModel.apply {
this.sdk = sdk
}.commit()
}
}
private fun doScheduleDownloadJdk(sdkDownloadTask: JdkDownloadTask, onInstalledSdk: (Sdk?) -> Unit = {}): CompletableFuture<Boolean> {
val jdkDownloader = (SdkDownload.EP_NAME.findFirstSafe { it is JdkDownloader } as? JdkDownloader)
?: return CompletableFuture.completedFuture(false)

Expand All @@ -109,7 +100,7 @@ class JdkDownloadService(private val project: Project, private val coroutineScop
val (selectedFile, error) = JdkInstaller.getInstance().validateInstallDir(sdkDownloadTask.request.installDir.toString())
if (selectedFile != null) {
jdkDownloader.prepareDownloadTask(project, sdkDownloadTask.jdkItem, selectedFile) { downloadTask ->
scheduleSetupInstallableSdk(project, downloadTask, sdkDownloadedFuture, setSdk)
scheduleSetupInstallableSdk(project, downloadTask, sdkDownloadedFuture, onInstalledSdk)
}
} else {
Messages.showErrorDialog(project, JavaUiBundle.message("jdk.download.error.message", error), JavaUiBundle.message("jdk.download.error.title"))
Expand All @@ -121,10 +112,28 @@ class JdkDownloadService(private val project: Project, private val coroutineScop
}

fun scheduleDownloadJdkForNewProject(sdkDownloadTask: JdkDownloadTask): CompletableFuture<Boolean> {
return doScheduleDownloadJdk(sdkDownloadTask, null, true)
return doScheduleDownloadJdk(sdkDownloadTask) {
ProjectRootManager.getInstance(project).projectSdk = it
}
}

fun scheduleDownloadJdk(sdkDownloadTask: JdkDownloadTask, module: Module, isCreatingNewProject: Boolean) {
doScheduleDownloadJdk(sdkDownloadTask, module, isCreatingNewProject)
if (isCreatingNewProject) {
scheduleDownloadJdkForNewProject(sdkDownloadTask)
} else {
scheduleDownloadJdk(sdkDownloadTask, module)
}
}

fun scheduleDownloadJdk(sdkDownloadTask: JdkDownloadTask, module: Module) {
doScheduleDownloadJdk(sdkDownloadTask) {
ModuleRootManager.getInstance(module).modifiableModel.apply {
this.sdk = it
}.commit()
}
}

fun scheduleDownloadJdk(sdkDownloadTask: JdkDownloadTask) {
doScheduleDownloadJdk(sdkDownloadTask)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ abstract class ConfigurableBuildIssue : BuildIssue {
return hyperlinkReference
}

fun addQuickFixes(vararg quickFixes: Pair<BuildIssueQuickFix, String>) {
quickFixes.forEach { (quickFix, quickFixText) ->
val hyperlinkReference = addQuickFix(quickFix)
addQuickFixPrompt("<a href=\"${hyperlinkReference}\">$quickFixText</a>")
}
}

private class QuickFix(
hyperlinkReference: String,
private val delegate: BuildIssueQuickFix
Expand Down
5 changes: 5 additions & 0 deletions plugins/gradle/plugin-resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,17 @@
<issueChecker implementation="org.jetbrains.plugins.gradle.issue.GradleDaemonStartupIssueChecker"/>
<issueChecker implementation="org.jetbrains.plugins.gradle.issue.GradleBuildCancelledIssueChecker"/>
<issueChecker implementation="org.jetbrains.plugins.gradle.issue.GradleOutOfMemoryIssueChecker"/>
<issueChecker implementation="org.jetbrains.plugins.gradle.issue.GradleToolchainDownloadedMismatchCriteriaIssueChecker"/>
<issueChecker implementation="org.jetbrains.plugins.gradle.issue.GradleUndefinedDaemonJvmCriteriaIssueChecker"/>
<issueChecker implementation="org.jetbrains.plugins.gradle.issue.GradleInvalidDaemonJvmCriteriaIssueChecker"/>
<executionHelperExtension implementation="org.jetbrains.plugins.gradle.service.execution.GradleTaskExecutionMeasuringExtension"/>
<executionHelperExtension
implementation="org.jetbrains.plugins.gradle.service.execution.telemetry.GradleTelemetryAgentProvidingExecutionHelperExtension"/>

<autoReloadSettingsCollector
implementation="org.jetbrains.plugins.gradle.service.project.GradleAutoImportAware$GradlePropertiesCollector"/>
<autoReloadSettingsCollector
implementation="org.jetbrains.plugins.gradle.service.project.GradleAutoImportAware$GradleDaemonJvmPropertiesCollector"/>
<autoReloadSettingsCollector
implementation="org.jetbrains.plugins.gradle.service.project.GradleAutoImportAware$VersionCatalogCollector"/>
<autoReloadSettingsCollector
Expand Down
19 changes: 19 additions & 0 deletions plugins/gradle/resources/messages/GradleBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,31 @@ gradle.build.issue.gradle.supported.description=The minimum supported Gradle ver
gradle.build.issue.gradle.deprecated.title=Deprecated Gradle Version
gradle.build.issue.gradle.deprecated.description=Gradle {0} support is deprecated and scheduled for removal.

gradle.build.issue.daemon.toolchain.not.found.title=Not Found Toolchain Matching Gradle Daemon JVM Criteria
gradle.build.issue.daemon.toolchain.invalid.criteria.title=Invalid Gradle Daemon JVM Criteria
gradle.build.issue.daemon.toolchain.undefined.criteria.title=Undefined Gradle Daemon JVM Criteria
gradle.build.issue.daemon.toolchain.download.error.title=Unable to Install Toolchain Matching Criteria
gradle.build.issue.daemon.toolchain.downloaded.mismatch.criteria.title=Unexpected Downloaded Toolchain Specs
gradle.build.issue.daemon.toolchain.repositories.undefined.title=Undefined Toolchain Download Repositories

gradle.toolchain.download.error.title=Unable to Download Toolchain
gradle.toolchain.download.error.message.without.vendor=Failed to locate and download a matching toolchain with version: ''{0}''. \
Consider installing it manually or modify Daemon JVM criteria.
gradle.toolchain.download.error.message.with.vendor=Failed to locate and download a matching toolchain with version: ''{0}'' and vendor: ''{1}''. \
Consider installing it manually or modify Daemon JVM criteria.

gradle.dependency.analyzer.loading=Load Gradle Dependencies
notification.group.gradle=Gradle

gradle.build.quick.fix.gradle.jvm=Use Java {1} as Gradle JVM: <a href="{0}">Open Gradle settings</a>
gradle.build.quick.fix.gradle.version=<a href="{0}">Upgrade to Gradle {1} and re-sync</a>

gradle.build.quick.fix.add.toolchain.repository=Add default toolchain download repository plugin
gradle.build.quick.fix.add.toolchain.criteria=Add default Daemon JVM criteria
gradle.build.quick.fix.modify.gradle.jvm.criteria=Modify Daemon JVM criteria
gradle.build.quick.fix.install.missing.toolchain=Install required toolchain
gradle.build.quick.fix.recreate.download.urls=Recreate toolchain download URLs

advanced.settings.gradle=Build Tools. Gradle
advanced.setting.gradle.download.sources=Download sources
advanced.setting.gradle.download.sources.description=Download sources for project dependencies during the project import. A project sync is required to download the sources after selecting this option.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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.plugins.gradle.issue

import com.intellij.build.FilePosition
import com.intellij.build.events.BuildEvent
import com.intellij.build.issue.BuildIssue
import org.gradle.internal.buildconfiguration.DaemonJvmPropertiesConfigurator
import org.jetbrains.plugins.gradle.issue.quickfix.GradleOpenDaemonJvmSettingsQuickFix
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionErrorHandler.getRootCauseAndLocation
import org.jetbrains.plugins.gradle.util.GradleBundle
import java.util.function.Consumer

/**
* An [GradleIssueChecker] handling Daemon JVM criteria exceptions like:
* "Value 'invalid version' given for toolchainVersion is an invalid Java version"
*/
class GradleInvalidDaemonJvmCriteriaIssueChecker : GradleIssueChecker {

override fun check(issueData: GradleIssueData): BuildIssue? {
val rootCause = getRootCauseAndLocation(issueData.error).first
if (rootCause?.toString()?.endsWith("toolchainVersion is an invalid Java version") == true) {
return GradleInvalidDaemonJvmCriteriaBuildIssue(rootCause)
}
return null
}

override fun consumeBuildOutputFailureMessage(
message: String,
failureCause: String,
stacktrace: String?,
location: FilePosition?,
parentEventId: Any,
messageConsumer: Consumer<in BuildEvent>
): Boolean {
return parentEventId == ":${DaemonJvmPropertiesConfigurator.TASK_NAME}"
}
}

private class GradleInvalidDaemonJvmCriteriaBuildIssue(
cause: Throwable
) : ConfigurableGradleBuildIssue() {
init {
setTitle(GradleBundle.message("gradle.build.issue.daemon.toolchain.invalid.criteria.title"))
addDescription(cause.message ?: title)
addQuickFixes(
GradleOpenDaemonJvmSettingsQuickFix to GradleBundle.message("gradle.build.quick.fix.modify.gradle.jvm.criteria")
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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.plugins.gradle.issue

import com.intellij.build.FilePosition
import com.intellij.build.events.BuildEvent
import com.intellij.build.issue.BuildIssue
import org.gradle.internal.buildconfiguration.DaemonJvmPropertiesConfigurator
import org.jetbrains.plugins.gradle.issue.quickfix.GradleOpenDaemonJvmSettingsQuickFix
import org.jetbrains.plugins.gradle.issue.quickfix.GradleRecreateToolchainDownloadUrlsQuickFix
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionErrorHandler.getRootCauseAndLocation
import org.jetbrains.plugins.gradle.util.GradleBundle
import java.util.function.Consumer

/**
* An [GradleIssueChecker] handling Daemon JVM criteria exceptions like:
* "Toolchain provisioned from '<URL>' doesn't satisfy the specification: {languageVersion=11, vendor=any, implementation=vendor-specific}"
*/
class GradleToolchainDownloadedMismatchCriteriaIssueChecker : GradleIssueChecker {

override fun check(issueData: GradleIssueData): BuildIssue? {
val rootCause = getRootCauseAndLocation(issueData.error).first
if (rootCause.message?.startsWith("Toolchain provisioned from") == true) {
return GradleToolchainDownloadedMismatchCriteriaBuildIssue(rootCause, issueData.projectPath)
}
return null
}

override fun consumeBuildOutputFailureMessage(
message: String,
failureCause: String,
stacktrace: String?,
location: FilePosition?,
parentEventId: Any,
messageConsumer: Consumer<in BuildEvent>
): Boolean {
return parentEventId == ":${DaemonJvmPropertiesConfigurator.TASK_NAME}"
}
}

private class GradleToolchainDownloadedMismatchCriteriaBuildIssue(
cause: Throwable,
externalProjectPath: String
) : ConfigurableGradleBuildIssue() {
init {
setTitle(GradleBundle.message("gradle.build.issue.daemon.toolchain.downloaded.mismatch.criteria.title"))
addDescription(cause.message ?: title)
addQuickFixes(
GradleRecreateToolchainDownloadUrlsQuickFix(externalProjectPath) to GradleBundle.message("gradle.build.quick.fix.recreate.download.urls"),
GradleOpenDaemonJvmSettingsQuickFix to GradleBundle.message("gradle.build.quick.fix.modify.gradle.jvm.criteria")
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.plugins.gradle.issue

import com.intellij.build.issue.BuildIssue
import org.jetbrains.plugins.gradle.issue.quickfix.GradleAddDaemonToolchainCriteriaQuickFix
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionErrorHandler.getRootCauseAndLocation
import org.jetbrains.plugins.gradle.util.GradleBundle

/**
* An [GradleIssueChecker] handling Daemon JVM criteria exception when isn't defined for the project
*/
class GradleUndefinedDaemonJvmCriteriaIssueChecker : GradleIssueChecker {

override fun check(issueData: GradleIssueData): BuildIssue? {
val rootCause = getRootCauseAndLocation(issueData.error).first
if (false) { // TODO: No known exception since right now is an opt-in feature
return GradleUndefinedDaemonJvmCriteriaBuildIssue(rootCause, issueData.projectPath)
}
return null
}
}

private class GradleUndefinedDaemonJvmCriteriaBuildIssue(
cause: Throwable,
externalProjectPath: String
) : ConfigurableGradleBuildIssue() {
init {
setTitle(GradleBundle.message("gradle.build.issue.daemon.toolchain.undefined.criteria.title"))
addDescription(cause.message ?: title)
addQuickFixes(
GradleAddDaemonToolchainCriteriaQuickFix(externalProjectPath) to GradleBundle.message("gradle.build.quick.fix.add.toolchain.criteria")
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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.plugins.gradle.issue.quickfix

import com.intellij.build.issue.BuildIssueQuickFix
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.project.Project
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import org.jetbrains.plugins.gradle.service.coroutine.GradleCoroutineScopeProvider
import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper.updateProjectDaemonJvmCriteria
import java.util.concurrent.CompletableFuture

class GradleAddDaemonToolchainCriteriaQuickFix(
private val externalProjectPath: String
) : BuildIssueQuickFix {

override val id: String = "add_daemon_toolchain_criteria"

override fun runQuickFix(project: Project, dataContext: DataContext): CompletableFuture<*> {
return GradleCoroutineScopeProvider.getInstance(project).cs
.launch {
updateProjectDaemonJvmCriteria(project, externalProjectPath, null)
}.asCompletableFuture()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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.plugins.gradle.issue.quickfix

import com.intellij.build.issue.BuildIssueQuickFix
import com.intellij.ide.actions.ShowSettingsUtilImpl
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.options.ex.ConfigurableVisitor
import com.intellij.openapi.options.newEditor.SettingsDialogFactory
import com.intellij.openapi.project.Project
import org.jetbrains.plugins.gradle.service.settings.GradleConfigurable
import org.jetbrains.plugins.gradle.util.GradleBundle
import java.util.concurrent.CompletableFuture

object GradleOpenDaemonJvmSettingsQuickFix : BuildIssueQuickFix {

override val id: String = "open_daemon_jvm_criteria_settings"

override fun runQuickFix(project: Project, dataContext: DataContext): CompletableFuture<*> {
ApplicationManager.getApplication().invokeLater {
val groups = ShowSettingsUtilImpl.getConfigurableGroups(project, true)
val configurable = ConfigurableVisitor.findByType(GradleConfigurable::class.java, groups.toList())
val gradleJvmSettingsFilter = GradleBundle.message("gradle.settings.text.jvm.path")
val dialogWrapper = SettingsDialogFactory.getInstance().create(project, groups, configurable, gradleJvmSettingsFilter)
dialogWrapper.show()
}
return CompletableFuture.completedFuture(null)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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.plugins.gradle.issue.quickfix

import com.intellij.build.issue.BuildIssueQuickFix
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.project.Project
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import org.jetbrains.plugins.gradle.properties.GradleDaemonJvmPropertiesFile
import org.jetbrains.plugins.gradle.service.coroutine.GradleCoroutineScopeProvider
import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmCriteria
import org.jetbrains.plugins.gradle.service.execution.GradleDaemonJvmHelper.updateProjectDaemonJvmCriteria
import java.nio.file.Path
import java.util.concurrent.CompletableFuture

class GradleRecreateToolchainDownloadUrlsQuickFix(
private val externalProjectPath: String
) : BuildIssueQuickFix {

override val id: String = "recreate_toolchain_download_urls"

override fun runQuickFix(project: Project, dataContext: DataContext): CompletableFuture<*> {
return GradleCoroutineScopeProvider.getInstance(project).cs
.launch {
val daemonJvmProperties = GradleDaemonJvmPropertiesFile.getProperties(Path.of(externalProjectPath))
updateProjectDaemonJvmCriteria(
project,
externalProjectPath,
daemonJvmCriteria = GradleDaemonJvmCriteria(
version = daemonJvmProperties?.version?.value,
vendor = daemonJvmProperties?.vendor?.value
),
)
}.asCompletableFuture()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData;
import org.jetbrains.plugins.gradle.properties.GradleDaemonJvmPropertiesFileKt;
import org.jetbrains.plugins.gradle.service.execution.GradleUserHomeUtil;
import org.jetbrains.plugins.gradle.settings.DistributionType;
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
Expand Down Expand Up @@ -166,6 +167,15 @@ public List<File> collectSettingsFiles(@NotNull Project project, @NotNull Gradle
}
}

public static final class GradleDaemonJvmPropertiesCollector implements GradleAutoReloadSettingsCollector {
@NotNull
@Override
public List<File> collectSettingsFiles(@NotNull Project project, @NotNull GradleProjectSettings projectSettings) {
String externalProjectPath = projectSettings.getExternalProjectPath();
return List.of(new File(externalProjectPath, "gradle/" + GradleDaemonJvmPropertiesFileKt.GRADLE_DAEMON_JVM_PROPERTIES_FILE_NAME));
}
}

public static final class GradleScriptCollector implements GradleAutoReloadSettingsCollector {

@NotNull
Expand Down
Loading