diff --git a/docs/plugin-graph/plugin-graph.json b/docs/plugin-graph/plugin-graph.json index 07bb3ca64864..a547168f56f0 100644 --- a/docs/plugin-graph/plugin-graph.json +++ b/docs/plugin-graph/plugin-graph.json @@ -2100,18 +2100,6 @@ "parent": "cT41", "type": 0 } -}, { - "group": "nodes", - "data": { - "id": "kP", - "name": "intellij.performanceTesting.gradle", - "n": "i.performanceTesting.gradle", - "package": "com.jetbrains.performancePlugin.gradle", - "sourceModule": "intellij.performanceTesting.gradle", - "descriptor": "plugins/performanceTesting/gradle/resources/META-INF/plugin.xml", - "pluginId": "com.jetbrains.performancePlugin.gradle", - "type": 1 - } }, { "group": "nodes", "data": { diff --git a/native/XPlatLauncher/resources/product_info_linux.json b/native/XPlatLauncher/resources/product_info_linux.json index f4fe3e1a0c2c..3ef41bf9b273 100644 --- a/native/XPlatLauncher/resources/product_info_linux.json +++ b/native/XPlatLauncher/resources/product_info_linux.json @@ -160,7 +160,6 @@ "com.jetbrains.packagesearch.intellij-plugin", "com.jetbrains.performancePlugin", "com.jetbrains.performancePlugin.dynamicPlugins", - "com.jetbrains.performancePlugin.gradle", "com.jetbrains.performancePlugin.java", "com.jetbrains.performancePlugin.kotlin", "com.jetbrains.performancePlugin.workspaceModel", diff --git a/native/XPlatLauncher/resources/product_info_macos.json b/native/XPlatLauncher/resources/product_info_macos.json index e0114979173b..811680cc6ea2 100644 --- a/native/XPlatLauncher/resources/product_info_macos.json +++ b/native/XPlatLauncher/resources/product_info_macos.json @@ -162,7 +162,6 @@ "com.jetbrains.packagesearch.intellij-plugin", "com.jetbrains.performancePlugin", "com.jetbrains.performancePlugin.dynamicPlugins", - "com.jetbrains.performancePlugin.gradle", "com.jetbrains.performancePlugin.java", "com.jetbrains.performancePlugin.kotlin", "com.jetbrains.performancePlugin.workspaceModel", diff --git a/native/XPlatLauncher/resources/product_info_windows.json b/native/XPlatLauncher/resources/product_info_windows.json index 10691b4db44a..e61335654443 100644 --- a/native/XPlatLauncher/resources/product_info_windows.json +++ b/native/XPlatLauncher/resources/product_info_windows.json @@ -159,7 +159,6 @@ "com.jetbrains.packagesearch.intellij-plugin", "com.jetbrains.performancePlugin", "com.jetbrains.performancePlugin.dynamicPlugins", - "com.jetbrains.performancePlugin.gradle", "com.jetbrains.performancePlugin.java", "com.jetbrains.performancePlugin.kotlin", "com.jetbrains.performancePlugin.workspaceModel", diff --git a/plugins/gradle/java/intellij.gradle.java.iml b/plugins/gradle/java/intellij.gradle.java.iml index c2739d65546a..e3e200b0bda2 100644 --- a/plugins/gradle/java/intellij.gradle.java.iml +++ b/plugins/gradle/java/intellij.gradle.java.iml @@ -45,5 +45,6 @@ + \ No newline at end of file diff --git a/plugins/gradle/java/resources/META-INF/gradle-performance-plugin-support.xml b/plugins/gradle/java/resources/META-INF/gradle-performance-plugin-support.xml new file mode 100644 index 000000000000..a76521eddf7e --- /dev/null +++ b/plugins/gradle/java/resources/META-INF/gradle-performance-plugin-support.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/plugins/gradle/java/resources/META-INF/plugin.xml b/plugins/gradle/java/resources/META-INF/plugin.xml index 4abb66902ce2..90020c85e002 100644 --- a/plugins/gradle/java/resources/META-INF/plugin.xml +++ b/plugins/gradle/java/resources/META-INF/plugin.xml @@ -18,6 +18,7 @@ org.intellij.groovy com.intellij.properties org.toml.lang + com.jetbrains.performancePlugin messages.GradleInspectionBundle diff --git a/plugins/gradle/java/resources/commandDescriptions/importGradleProjectCommand.html b/plugins/gradle/java/resources/commandDescriptions/importGradleProjectCommand.html new file mode 100644 index 000000000000..f299434e93ae --- /dev/null +++ b/plugins/gradle/java/resources/commandDescriptions/importGradleProjectCommand.html @@ -0,0 +1,14 @@ + +Command imports gradle project + +

+ Simulate invocation of reimport of gradle project. +

+

+ Syntax: %importGradleProject +

+

+ Example: %importGradleProject +

+ + \ No newline at end of file diff --git a/plugins/gradle/java/src/performanceTesting/GradleCommandProvider.java b/plugins/gradle/java/src/performanceTesting/GradleCommandProvider.java new file mode 100644 index 000000000000..d4059d8f0d30 --- /dev/null +++ b/plugins/gradle/java/src/performanceTesting/GradleCommandProvider.java @@ -0,0 +1,15 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gradle.performanceTesting; + +import com.jetbrains.performancePlugin.CommandProvider; +import com.jetbrains.performancePlugin.CreateCommand; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +final class GradleCommandProvider implements CommandProvider { + @Override + public @NotNull Map getCommands() { + return Map.of(ImportGradleProjectCommand.PREFIX, ImportGradleProjectCommand::new); + } +} diff --git a/plugins/gradle/java/src/performanceTesting/ImportGradleProjectCommand.java b/plugins/gradle/java/src/performanceTesting/ImportGradleProjectCommand.java new file mode 100644 index 000000000000..5c0d632af042 --- /dev/null +++ b/plugins/gradle/java/src/performanceTesting/ImportGradleProjectCommand.java @@ -0,0 +1,217 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gradle.performanceTesting; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings; +import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType; +import com.intellij.openapi.externalSystem.service.internal.ExternalSystemProcessingManager; +import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback; +import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener; +import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; +import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; +import com.intellij.openapi.ui.playback.PlaybackContext; +import com.intellij.openapi.ui.playback.commands.AbstractCommand; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.DisposeAwareRunnable; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.messages.SimpleMessageBusConnection; +import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper; +import org.gradle.util.GradleVersion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.concurrency.AsyncPromise; +import org.jetbrains.concurrency.Promise; +import org.jetbrains.concurrency.Promises; +import org.jetbrains.plugins.gradle.service.project.open.GradleProjectImportUtil; +import org.jetbrains.plugins.gradle.settings.GradleDefaultProjectSettings; +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; +import org.jetbrains.plugins.gradle.settings.GradleSettings; +import org.jetbrains.plugins.gradle.util.GradleConstants; +import org.jetbrains.plugins.gradle.util.GradleJvmResolutionUtil; +import org.jetbrains.plugins.gradle.util.GradleJvmValidationUtil; +import org.jetbrains.plugins.gradle.util.SuggestGradleVersionOptions; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.jetbrains.plugins.gradle.util.GradleJvmSupportMatrices.suggestGradleVersion; + +public final class ImportGradleProjectCommand extends AbstractCommand { + public static final String PREFIX = "%importGradleProject"; + + public ImportGradleProjectCommand(@NotNull String text, int line) { + super(text, line); + } + + @NotNull + @Override + protected Promise _execute(@NotNull PlaybackContext context) { + ActionCallback actionCallback = new ActionCallbackProfilerStopper(); + runWhenGradleImportAndIndexingFinished(context, actionCallback); + return Promises.toPromise(actionCallback); + } + + private void runWhenGradleImportAndIndexingFinished(@NotNull PlaybackContext context, @NotNull ActionCallback callback) { + Project project = context.getProject(); + ExternalSystemProjectTrackerSettings projectTrackerSettings = ExternalSystemProjectTrackerSettings.getInstance(project); + ExternalSystemProjectTrackerSettings.AutoReloadType currentAutoReloadType = projectTrackerSettings.getAutoReloadType(); + projectTrackerSettings.setAutoReloadType(ExternalSystemProjectTrackerSettings.AutoReloadType.NONE); + context.message("Waiting for open and initialized Gradle project", getLine()); + ExternalProjectsManagerImpl.getInstance(project).runWhenInitialized(() -> DumbService.getInstance(project).runWhenSmart(() -> { + ApplicationManager.getApplication().executeOnPooledThread(() -> { + waitForCurrentResolveTasks(context, project) + .thenAsync(o -> { + context.message("Import of the project has been started", getLine()); + AsyncPromise promise = new AsyncPromise<>(); + GradleSettings gradleSettings = GradleSettings.getInstance(project); + linkGradleProjectIfNeeded(project, context, gradleSettings) + .onError(throwable -> callback.reject("Link of a gradle project failed. Not a gradle project")) + .onSuccess(unused -> doGradleSync(project, context, promise, gradleSettings, callback)); + return promise; + }) + .onProcessed(promise -> { + context.message("Import has been finished", getLine()); + projectTrackerSettings.setAutoReloadType(currentAutoReloadType); + DumbService.getInstance(project).runWhenSmart(DisposeAwareRunnable.create(() -> callback.setDone(), project)); + }); + }); + })); + } + + private Promise waitForCurrentResolveTasks(@NotNull PlaybackContext context, + @NotNull Project project) { + AsyncPromise promise = new AsyncPromise<>(); + context.message("Waiting for current import resolve tasks", getLine()); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + ExternalSystemProcessingManager processingManager = + ApplicationManager.getApplication().getService(ExternalSystemProcessingManager.class); + while (processingManager.hasTaskOfTypeInProgress(ExternalSystemTaskType.RESOLVE_PROJECT, project)) { + final Object lock = new Object(); + synchronized (lock) { + try { + lock.wait(100); + } + catch (InterruptedException ignore) { + } + } + } + promise.setResult(null); + }); + return promise.onProcessed(o -> { + context.message("Import resolve tasks has been completed", getLine()); + }); + } + + private void doGradleSync(@NotNull Project project, + @NotNull PlaybackContext context, + @NotNull AsyncPromise promise, + @NotNull GradleSettings gradleSettings, + @NotNull ActionCallback callback) { + Collection projectsSettings = gradleSettings.getLinkedProjectsSettings(); + List projectsPaths = ContainerUtil.map(projectsSettings, ExternalProjectSettings::getExternalProjectPath); + AtomicInteger gradleProjectsToRefreshCount = new AtomicInteger(projectsSettings.size()); + StringBuilder projectsWithResolveErrors = new StringBuilder(); + for (GradleProjectSettings settings : projectsSettings) { + ImportSpecBuilder importSpecBuilder = new ImportSpecBuilder(project, GradleConstants.SYSTEM_ID); + importSpecBuilder.callback(new ExternalProjectRefreshCallback() { + private final ImportSpecBuilder.DefaultProjectRefreshCallback + defaultCallback = new ImportSpecBuilder.DefaultProjectRefreshCallback(importSpecBuilder.build()); + + @Override + public void onSuccess(@Nullable DataNode externalProject) { + assert externalProject != null; + context.message("Gradle resolve finished for: " + externalProject.getData().getLinkedExternalProjectPath(), getLine()); + SimpleMessageBusConnection connection = project.getMessageBus().simpleConnect(); + connection.subscribe(ProjectDataImportListener.TOPIC, new ProjectDataImportListener() { + @Override + public void onImportFinished(String projectPath) { + _onImportFinished(projectPath); + } + + @Override + public void onImportFailed(String projectPath, @NotNull Throwable failure) { + _onImportFinished(projectPath); + } + + private void _onImportFinished(String projectPath) { + if (!projectsPaths.contains(projectPath)) return; + connection.disconnect(); + if (gradleProjectsToRefreshCount.decrementAndGet() == 0) { + ApplicationManager.getApplication().invokeLater(() -> promise.setResult(null)); + } + } + }); + + defaultCallback.onSuccess(externalProject); + callback.setDone(); + } + + @Override + public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) { + context.error("Gradle resolve failed for: " + settings.getExternalProjectPath() + ":" + errorMessage + ":" + errorDetails, + getLine()); + synchronized (projectsWithResolveErrors) { + if (projectsWithResolveErrors.length() != 0) { + projectsWithResolveErrors.append(", "); + } + projectsWithResolveErrors.append(String.format("'%s'", new File(settings.getExternalProjectPath()).getName())); + } + defaultCallback.onFailure(errorMessage, errorDetails); + if (gradleProjectsToRefreshCount.decrementAndGet() == 0) { + ApplicationManager.getApplication().invokeLater(() -> promise.setError(projectsWithResolveErrors.toString())); + callback.reject("Gradle sync failed"); + } + } + }); + ExternalSystemUtil.refreshProject(settings.getExternalProjectPath(), importSpecBuilder); + } + } + + private Promise linkGradleProjectIfNeeded(@NotNull Project project, + @NotNull PlaybackContext context, + @NotNull GradleSettings gradleSettings) { + if (gradleSettings.getLinkedProjectsSettings().isEmpty()) { + VirtualFile projectDir = ProjectUtil.guessProjectDir(project); + assert projectDir != null; + VirtualFile[] children = projectDir.getChildren(); + boolean isGradleProject = ContainerUtil.exists(children, file -> GradleConstants.KNOWN_GRADLE_FILES.contains(file.getName())); + if (!isGradleProject) { + context.error("Unable to find Gradle project at " + projectDir.getPath(), getLine()); + context.message("Files found at the path: " + Arrays.toString(ContainerUtil.map2Array(children, VirtualFile::getName)), getLine()); + return Promises.rejectedPromise(); + } + else { + GradleProjectSettings projectSettings = GradleDefaultProjectSettings.createProjectSettings(projectDir.getPath()); + GradleProjectImportUtil.setupGradleSettings(gradleSettings); + GradleVersion gradleVersion = suggestGradleVersion( + new SuggestGradleVersionOptions() + .withProject(project) + .withProjectJdkVersionFilter(project) + ); + if (gradleVersion != null) { + GradleJvmResolutionUtil.setupGradleJvm(project, projectSettings, gradleVersion); + GradleJvmValidationUtil.validateJavaHome(project, projectDir.toNioPath(), gradleVersion); + } + + AsyncPromise promise = new AsyncPromise<>(); + ApplicationManager.getApplication().invokeLater(() -> { + gradleSettings.linkProject(projectSettings); + promise.setResult(null); + }); + return promise; + } + } + return Promises.resolvedPromise(); + } +} diff --git a/plugins/maven/src/main/resources/META-INF/performance-plugin-support.xml b/plugins/maven/src/main/resources/META-INF/maven-performance-plugin-support.xml similarity index 100% rename from plugins/maven/src/main/resources/META-INF/performance-plugin-support.xml rename to plugins/maven/src/main/resources/META-INF/maven-performance-plugin-support.xml diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index cc881b63e257..c078681aa3e9 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -63,7 +63,7 @@ org.jetbrains.idea.maven.server.api org.intellij.groovy Error-prone plugin - com.jetbrains.performancePlugin + com.jetbrains.performancePlugin