Support debug of non-jvm Gradle tasks in External System
authorKirill Shmakov <kirill.shmakov@jetbrains.com>
Tue, 24 Sep 2019 11:38:45 +0000 (14:38 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Fri, 27 Sep 2019 08:31:42 +0000 (08:31 +0000)
GitOrigin-RevId: 2d9b816110abf38cbc9159558e696e729e0f4025

18 files changed:
java/execution/impl/intellij.java.execution.impl.iml
java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskDebugRunner.java
java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ForkedDebuggerThread.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/debugger/DebuggerBackendExtension.java [new file with mode: 0644]
platform/external-system-impl/intellij.platform.externalSystem.impl.iml
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunConfiguration.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunnableState.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskRunner.kt
platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerConfiguration.java
platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerHelper.java
platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml
plugins/gradle/java/resources/META-INF/plugin.xml
plugins/gradle/java/src/service/debugger/GradleJvmDebuggerBackend.kt [new file with mode: 0644]
plugins/gradle/java/src/service/project/JavaGradleProjectResolver.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleRunConfiguration.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java

index 0873bb1247e011cac76f84f827163c765af6f3c3..33b02cfea8577632dca21dc755849c26d21be786 100644 (file)
@@ -29,6 +29,7 @@
     <orderEntry type="module" module-name="intellij.platform.vcs.impl" />
     <orderEntry type="library" name="NanoXML" level="project" />
     <orderEntry type="library" name="jackson-databind" level="project" />
+    <orderEntry type="module" module-name="intellij.platform.debugger.impl" />
   </component>
   <component name="copyright">
     <Base>
index 193c79c002867fa24cd88b25c9c510abadbee3a2..1b9f7bc59b41f9a87f4c4d7ac84c40e70bee8e73 100644 (file)
 package com.intellij.openapi.externalSystem.service.execution;
 
 import com.intellij.build.BuildView;
+import com.intellij.debugger.DebugEnvironment;
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.DefaultDebugEnvironment;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.JavaDebugProcess;
+import com.intellij.debugger.impl.DebuggerSession;
 import com.intellij.debugger.impl.GenericDebuggerRunner;
+import com.intellij.execution.DefaultExecutionResult;
 import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionResult;
 import com.intellij.execution.configurations.RemoteConnection;
 import com.intellij.execution.configurations.RunProfile;
 import com.intellij.execution.configurations.RunProfileState;
@@ -26,8 +34,14 @@ import com.intellij.execution.process.ProcessHandler;
 import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.execution.ui.ExecutionConsole;
 import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.ide.ui.EditorOptionsTopHitProvider;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
+import com.intellij.xdebugger.XDebugProcess;
+import com.intellij.xdebugger.XDebugProcessStarter;
+import com.intellij.xdebugger.XDebugSession;
+import com.intellij.xdebugger.XDebuggerManager;
+import com.intellij.xdebugger.impl.XDebugSessionImpl;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -39,6 +53,8 @@ import java.net.ServerSocket;
 public class ExternalSystemTaskDebugRunner extends GenericDebuggerRunner {
   static final Logger LOG = Logger.getInstance(ExternalSystemTaskDebugRunner.class);
 
+  private static final String ATTACH_VM_FAILED = "ATTACH_VM_FAILED";
+
   @NotNull
   @Override
   public String getRunnerId() {
@@ -54,11 +70,11 @@ public class ExternalSystemTaskDebugRunner extends GenericDebuggerRunner {
   @Override
   protected RunContentDescriptor createContentDescriptor(@NotNull RunProfileState state, @NotNull ExecutionEnvironment environment)
     throws ExecutionException {
-    if (state instanceof ExternalSystemRunConfiguration.MyRunnableState) {
-      ExternalSystemRunConfiguration.MyRunnableState myRunnableState = (ExternalSystemRunConfiguration.MyRunnableState)state;
+    if (state instanceof ExternalSystemRunnableState) {
+      ExternalSystemRunnableState myRunnableState = (ExternalSystemRunnableState)state;
       int port = myRunnableState.getDebugPort();
       if (port > 0) {
-        RunContentDescriptor runContentDescriptor = doGetRunContentDescriptor(myRunnableState, environment, port);
+        RunContentDescriptor runContentDescriptor = doGetRunContentDescriptor(myRunnableState, environment);
         if (runContentDescriptor == null) return null;
 
         ProcessHandler processHandler = runContentDescriptor.getProcessHandler();
@@ -76,18 +92,16 @@ public class ExternalSystemTaskDebugRunner extends GenericDebuggerRunner {
       LOG.warn(String.format(
         "Can't attach debugger to external system task execution. Reason: invalid run profile state is provided"
         + "- expected '%s' but got '%s'",
-        ExternalSystemRunConfiguration.MyRunnableState.class.getName(), state.getClass().getName()
+        ExternalSystemRunnableState.class.getName(), state.getClass().getName()
       ));
     }
     return null;
   }
 
   @Nullable
-  private RunContentDescriptor doGetRunContentDescriptor(@NotNull ExternalSystemRunConfiguration.MyRunnableState state,
-                                                         @NotNull ExecutionEnvironment environment,
-                                                         int port) throws ExecutionException {
-    RemoteConnection connection = new RemoteConnection(true, "127.0.0.1", String.valueOf(port), true);
-    RunContentDescriptor runContentDescriptor = attachVirtualMachine(state, environment, connection, true);
+  private RunContentDescriptor doGetRunContentDescriptor(@NotNull ExternalSystemRunnableState state,
+                                                         @NotNull ExecutionEnvironment environment) throws ExecutionException {
+    RunContentDescriptor runContentDescriptor = createProcessToDebug(state, environment);
     if (runContentDescriptor == null) return null;
 
     state.setContentDescriptor(runContentDescriptor);
@@ -109,4 +123,55 @@ public class ExternalSystemTaskDebugRunner extends GenericDebuggerRunner {
     descriptor.setRunnerLayoutUi(runContentDescriptor.getRunnerLayoutUi());
     return descriptor;
   }
+
+  @NotNull
+  private XDebugProcess jvmProcessToDebug(@NotNull XDebugSession session,
+                                          ExternalSystemRunnableState state,
+                                          @NotNull ExecutionEnvironment env) throws ExecutionException {
+    RemoteConnection connection = new RemoteConnection(true, "127.0.0.1", String.valueOf(state.getDebugPort()), true);
+    DebugEnvironment environment = new DefaultDebugEnvironment(env, state, connection, DebugEnvironment.LOCAL_START_TIMEOUT);
+
+    final DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(env.getProject()).attachVirtualMachine(environment);
+
+    if (debuggerSession == null) {
+      throw new ExecutionException(ATTACH_VM_FAILED);
+    }
+
+    final DebugProcessImpl debugProcess = debuggerSession.getProcess();
+
+
+    XDebugSessionImpl sessionImpl = (XDebugSessionImpl)session;
+    ExecutionResult executionResult = debugProcess.getExecutionResult();
+    sessionImpl.addExtraActions(executionResult.getActions());
+    if (executionResult instanceof DefaultExecutionResult) {
+      sessionImpl.addRestartActions(((DefaultExecutionResult)executionResult).getRestartActions());
+    }
+    return JavaDebugProcess.create(session, debuggerSession);
+  }
+
+  @Nullable
+  private RunContentDescriptor createProcessToDebug(ExternalSystemRunnableState state,
+                                                    @NotNull ExecutionEnvironment env) throws ExecutionException {
+
+    RunContentDescriptor result;
+
+    try {
+      result = XDebuggerManager.getInstance(env.getProject()).startSession(env, new XDebugProcessStarter() {
+        @Override
+        @NotNull
+        public XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException {
+          XDebugProcess nonJvmDebugProcess = state.startDebugProcess(session, env);
+          return nonJvmDebugProcess != null ? nonJvmDebugProcess : jvmProcessToDebug(session, state, env);
+        }
+      }).getRunContentDescriptor();
+    }
+    catch (ExecutionException e) {
+      if (!e.getMessage().equals(ATTACH_VM_FAILED)) {
+        throw e;
+      }
+      result = null;
+    }
+
+    return result;
+  }
 }
index 585edac809933d502fa13b793b447b6427b26e1c..373f6481810594e2c7e5e56fd0bab7e669d30003 100644 (file)
@@ -10,7 +10,6 @@ import com.intellij.debugger.engine.managerThread.DebuggerCommand;
 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
 import com.intellij.execution.ExecutionException;
 import com.intellij.execution.ProgramRunnerUtil;
-import com.intellij.execution.RunManager;
 import com.intellij.execution.RunnerAndConfigurationSettings;
 import com.intellij.execution.executors.DefaultDebugExecutor;
 import com.intellij.execution.impl.ConsoleViewImpl;
@@ -18,8 +17,6 @@ import com.intellij.execution.impl.EditorHyperlinkSupport;
 import com.intellij.execution.process.ProcessAdapter;
 import com.intellij.execution.process.ProcessEvent;
 import com.intellij.execution.process.ProcessHandler;
-import com.intellij.execution.remote.RemoteConfiguration;
-import com.intellij.execution.remote.RemoteConfigurationType;
 import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
 import com.intellij.execution.runners.ProgramRunner;
@@ -32,6 +29,8 @@ import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.markup.HighlighterLayer;
 import com.intellij.openapi.editor.markup.RangeHighlighter;
 import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension;
+import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.io.StreamUtil;
@@ -64,9 +63,9 @@ class ForkedDebuggerThread extends Thread {
   private final ExecutionConsole myMainExecutionConsole;
 
   ForkedDebuggerThread(@NotNull ProcessHandler mainProcessHandler,
-                              @NotNull RunContentDescriptor mainRunContentDescriptor,
-                              @NotNull ServerSocket socket,
-                              @NotNull Project project) {
+                       @NotNull RunContentDescriptor mainRunContentDescriptor,
+                       @NotNull ServerSocket socket,
+                       @NotNull Project project) {
     super("external task forked debugger runner");
     setDaemon(true);
     mySocket = socket;
@@ -122,6 +121,16 @@ class ForkedDebuggerThread extends Thread {
     // the stream can not be closed in the current thread
     //noinspection IOResourceOpenedButNotSafelyClosed
     DataInputStream stream = new DataInputStream(accept.getInputStream());
+
+    String debuggerId = stream.readUTF();
+    String processName = stream.readUTF();
+    String processParameters = stream.readUTF();
+
+    if (processParameters.startsWith(ForkedDebuggerHelper.FINISH_PARAMS)) {
+      removeTerminatedForks(processName, processParameters, accept, stream);
+      return;
+    }
+
     myMainProcessHandler.addProcessListener(new ProcessAdapter() {
       @Override
       public void processTerminated(@NotNull ProcessEvent event) {
@@ -129,76 +138,78 @@ class ForkedDebuggerThread extends Thread {
         StreamUtil.closeStream(accept);
       }
     });
-    int signal = stream.readInt();
-    String processName = stream.readUTF();
-    if (signal > 0) {
-      String debugPort = String.valueOf(signal);
-      attachVM(myProject, processName, debugPort, new ProgramRunner.Callback() {
-        @Override
-        public void processStarted(RunContentDescriptor descriptor) {
-          // select tab for the forked process only when it has been suspended
-          descriptor.setSelectContentWhenAdded(false);
-
-          // restore selection of the 'main' tab to avoid flickering of the reused content tab when no suspend events occur
-          ProcessHandler forkedProcessHandler = descriptor.getProcessHandler();
-          if (forkedProcessHandler != null) {
-            myMainProcessHandler.addProcessListener(new ProcessAdapter() {
-              @Override
-              public void processWillTerminate(@NotNull ProcessEvent event, boolean willBeDestroyed) {
-                myMainProcessHandler.removeProcessListener(this);
-                terminateForkedProcess(forkedProcessHandler);
-              }
-            });
 
-            forkedProcessHandler.addProcessListener(new MyForkedProcessListener(descriptor, processName));
-            try {
-              accept.getOutputStream().write(0);
-              stream.close();
-            }
-            catch (IOException e) {
-              ExternalSystemTaskDebugRunner.LOG.debug(e);
-            }
+    for (DebuggerBackendExtension extension : DebuggerBackendExtension.EP_NAME.getExtensionList()) {
+      if (extension.id().equals(debuggerId)) {
+        RunnerAndConfigurationSettings settings = extension.debugConfigurationSettings(myProject, processName, processParameters);
+        runDebugConfiguration(settings, new ProgramRunner.Callback() {
+          @Override
+          public void processStarted(RunContentDescriptor descriptor) {
+            handleStartedDebugConfiguration(descriptor, processName, accept, stream);
           }
-        }
-      });
+        });
+        break;
+      }
     }
-    else if (signal == 0) {
-      // remove content for terminated forked processes
-      ApplicationManager.getApplication().invokeLater(() -> {
-        final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
-        ContentManager contentManager = toolWindowManager.getToolWindow(ToolWindowId.DEBUG).getContentManager();
-        Content content = contentManager.findContent(processName);
-        if (content != null) {
-          RunContentDescriptor descriptor = content.getUserData(RunContentDescriptor.DESCRIPTOR_KEY);
-          if (descriptor != null) {
-            ProcessHandler handler = descriptor.getProcessHandler();
-            if (handler != null) {
-              handler.destroyProcess();
-            }
-          }
-        }
-        try {
-          accept.getOutputStream().write(0);
-          stream.close();
-        }
-        catch (IOException e) {
-          ExternalSystemTaskDebugRunner.LOG.debug(e);
+  }
+
+  private void unblockRemote(Socket socket, DataInputStream inputStream) {
+    try {
+      socket.getOutputStream().write(0);
+      inputStream.close();
+    }
+    catch (IOException e) {
+      ExternalSystemTaskDebugRunner.LOG.debug(e);
+    }
+  }
+
+  private void handleStartedDebugConfiguration(RunContentDescriptor descriptor,
+                                               String processName,
+                                               Socket socket,
+                                               DataInputStream inputStream) {
+    // select tab for the forked process only when it has been suspended
+    descriptor.setSelectContentWhenAdded(false);
+
+    // restore selection of the 'main' tab to avoid flickering of the reused content tab when no suspend events occur
+    ProcessHandler forkedProcessHandler = descriptor.getProcessHandler();
+    if (forkedProcessHandler != null) {
+      myMainProcessHandler.addProcessListener(new ProcessAdapter() {
+        @Override
+        public void processWillTerminate(@NotNull ProcessEvent event, boolean willBeDestroyed) {
+          myMainProcessHandler.removeProcessListener(this);
+          terminateForkedProcess(forkedProcessHandler);
         }
       });
+
+      forkedProcessHandler.addProcessListener(new MyForkedProcessListener(descriptor, processName));
+      unblockRemote(socket, inputStream);
     }
   }
 
-  private static void attachVM(@NotNull Project project, String runConfigName, @NotNull String debugPort, ProgramRunner.Callback callback) {
-    RunnerAndConfigurationSettings runSettings = RunManager.getInstance(project).createConfiguration(runConfigName, RemoteConfigurationType.class);
-    runSettings.setActivateToolWindowBeforeRun(false);
-
-    RemoteConfiguration configuration = (RemoteConfiguration)runSettings.getConfiguration();
-    configuration.HOST = "localhost";
-    configuration.PORT = debugPort;
-    configuration.USE_SOCKET_TRANSPORT = true;
-    configuration.SERVER_MODE = true;
+  private void removeTerminatedForks(@NotNull String processName,
+                                     @NotNull String processParams,
+                                     Socket socket,
+                                     DataInputStream inputStream) {
+    ApplicationManager.getApplication().invokeLater(() -> {
+      final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
+      ContentManager contentManager = toolWindowManager.getToolWindow(ToolWindowId.DEBUG).getContentManager();
+      Content content = contentManager.findContent(processName);
+      if (content != null) {
+        RunContentDescriptor descriptor = content.getUserData(RunContentDescriptor.DESCRIPTOR_KEY);
+        if (descriptor != null) {
+          ProcessHandler handler = descriptor.getProcessHandler();
+          if (handler != null) {
+            handler.destroyProcess();
+          }
+        }
+      }
+      unblockRemote(socket, inputStream);
+    });
+  }
 
+  private static void runDebugConfiguration(@NotNull RunnerAndConfigurationSettings runSettings, ProgramRunner.Callback callback) {
     try {
+      runSettings.setActivateToolWindowBeforeRun(false);
       ExecutionEnvironment environment = ExecutionEnvironmentBuilder.create(DefaultDebugExecutor.getDebugExecutorInstance(), runSettings)
         .contentToReuse(null)
         .dataContext(null)
@@ -339,7 +350,8 @@ class ForkedDebuggerThread extends Thread {
           debugProcess.stop(true);
         }
       });
-    } else {
+    }
+    else {
       processHandler.destroyProcess();
     }
   }
diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/debugger/DebuggerBackendExtension.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/debugger/DebuggerBackendExtension.java
new file mode 100644 (file)
index 0000000..e31bee8
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.openapi.externalSystem.debugger;
+
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Provides ability to preconfigure tasks run by external system and to attach them with debugger.
+ */
+public interface DebuggerBackendExtension {
+  ExtensionPointName<DebuggerBackendExtension> EP_NAME = ExtensionPointName.create("com.intellij.externalSystem.debuggerBackend");
+
+  String id();
+
+  default List<String> initializationCode(@NotNull String dispatchPort, @NotNull String parameters) {
+    return new ArrayList();
+  }
+
+  RunnerAndConfigurationSettings debugConfigurationSettings(@NotNull Project project,
+                                                            @NotNull String processName,
+                                                            @NotNull String processParameters);
+
+  default HashMap<String, String> splitParameters(@NotNull String processParameters) {
+    HashMap<String, String> result = new HashMap();
+
+    final String[] envVars = processParameters.split(ForkedDebuggerHelper.PARAMETERS_SEPARATOR);
+    for (String envVar : envVars) {
+      final int idx = envVar.indexOf('=');
+      if (idx > -1) {
+        result.put(envVar.substring(0, idx), idx < envVar.length() - 1 ? envVar.substring(idx + 1) : "");
+      }
+    }
+
+    return result;
+  }
+}
\ No newline at end of file
index 40b4bcf441890c01705be3bd7ea5cbcf7e2b3f38..1a5d2d747a008ee78c506266ffbd7c1cd2ac168d 100644 (file)
@@ -20,5 +20,6 @@
     <orderEntry type="library" name="kotlin-reflect" level="project" />
     <orderEntry type="module" module-name="intellij.platform.objectSerializer" />
     <orderEntry type="module" module-name="intellij.platform.objectSerializer.annotations" />
+    <orderEntry type="module" module-name="intellij.platform.debugger" />
   </component>
 </module>
\ No newline at end of file
index 4cf5a582ff79b031477d88192d32746faa8a222d..7ad6910e985a0da77180d4408345d888e9f91c00 100644 (file)
@@ -2,51 +2,29 @@
 package com.intellij.openapi.externalSystem.service.execution;
 
 import com.intellij.build.*;
-import com.intellij.build.events.BuildEvent;
-import com.intellij.build.events.FailureResult;
-import com.intellij.build.events.impl.FinishBuildEventImpl;
-import com.intellij.build.events.impl.StartBuildEventImpl;
-import com.intellij.build.events.impl.SuccessResultImpl;
 import com.intellij.diagnostic.logging.LogConfigurationPanel;
 import com.intellij.execution.*;
 import com.intellij.execution.configurations.*;
 import com.intellij.execution.console.DuplexConsoleView;
 import com.intellij.execution.impl.ConsoleViewImpl;
 import com.intellij.execution.impl.ExecutionManagerImpl;
-import com.intellij.execution.process.ProcessHandler;
-import com.intellij.execution.process.ProcessOutputTypes;
 import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.execution.runners.FakeRerunAction;
-import com.intellij.execution.runners.ProgramRunner;
 import com.intellij.execution.ui.ExecutionConsole;
 import com.intellij.execution.ui.RunContentDescriptor;
 import com.intellij.icons.AllIcons;
-import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.DefaultActionGroup;
 import com.intellij.openapi.actionSystem.Presentation;
 import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.FoldRegion;
 import com.intellij.openapi.editor.FoldingModel;
 import com.intellij.openapi.extensions.ExtensionPointName;
 import com.intellij.openapi.externalSystem.ExternalSystemManager;
-import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent;
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
-import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
-import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemBuildEvent;
-import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemTaskExecutionEvent;
-import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
-import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.options.SettingsEditor;
 import com.intellij.openapi.options.SettingsEditorGroup;
@@ -59,10 +37,7 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.wm.ToolWindowId;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.search.GlobalSearchScopes;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.net.NetUtils;
 import com.intellij.util.text.CharArrayUtil;
-import com.intellij.util.text.DateFormatUtil;
 import com.intellij.util.xmlb.Accessor;
 import com.intellij.util.xmlb.SerializationFilter;
 import com.intellij.util.xmlb.XmlSerializer;
@@ -72,31 +47,24 @@ import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.io.File;
-import java.io.IOException;
 import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.ServerSocket;
 import java.util.Collections;
 
-import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.DEBUG_FORK_SOCKET_PARAM;
-import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.DEBUG_SETUP_PREFIX;
-import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.convert;
-import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.getConsoleManagerFor;
 import static com.intellij.openapi.util.text.StringUtil.nullize;
 
 /**
  * @author Denis Zhdanov
  */
 public class ExternalSystemRunConfiguration extends LocatableConfigurationBase implements SearchScopeProvidingRunProfile {
-  private static final ExtensionPointName<ExternalSystemRunConfigurationExtension> EP_NAME
+  static final ExtensionPointName<ExternalSystemRunConfigurationExtension> EP_NAME
     = ExtensionPointName.create("com.intellij.externalSystem.runConfigurationExtension");
 
   public static final Key<InputStream> RUN_INPUT_KEY = Key.create("RUN_INPUT_KEY");
   public static final Key<Class<? extends BuildProgressListener>> PROGRESS_LISTENER_KEY = Key.create("PROGRESS_LISTENER_KEY");
 
-  private static final Logger LOG = Logger.getInstance(ExternalSystemRunConfiguration.class);
+  static final Logger LOG = Logger.getInstance(ExternalSystemRunConfiguration.class);
   private ExternalSystemTaskExecutionSettings mySettings = new ExternalSystemTaskExecutionSettings();
-  private static final boolean DISABLE_FORK_DEBUGGER = Boolean.getBoolean("external.system.disable.fork.debugger");
+  static final boolean DISABLE_FORK_DEBUGGER = Boolean.getBoolean("external.system.disable.fork.debugger");
 
   public ExternalSystemRunConfiguration(@NotNull ProjectSystemId externalSystemId,
                                         Project project,
@@ -178,7 +146,8 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
   public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) {
     // DebugExecutor ID  - com.intellij.execution.executors.DefaultDebugExecutor.EXECUTOR_ID
     String debugExecutorId = ToolWindowId.DEBUG;
-    MyRunnableState runnableState = new MyRunnableState(mySettings, getProject(), debugExecutorId.equals(executor.getId()), this, env);
+    ExternalSystemRunnableState
+      runnableState = new ExternalSystemRunnableState(mySettings, getProject(), debugExecutorId.equals(executor.getId()), this, env);
     copyUserDataTo(runnableState);
     return runnableState;
   }
@@ -203,264 +172,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
     return scope;
   }
 
-  public static class MyRunnableState extends UserDataHolderBase implements RunProfileState {
-
-    @NotNull private final ExternalSystemTaskExecutionSettings mySettings;
-    @NotNull private final Project myProject;
-    @NotNull private final ExternalSystemRunConfiguration myConfiguration;
-    @NotNull private final ExecutionEnvironment myEnv;
-    @Nullable private RunContentDescriptor myContentDescriptor;
-
-    private final int myDebugPort;
-    private ServerSocket myForkSocket = null;
-
-    public MyRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
-                           @NotNull Project project,
-                           boolean debug,
-                           @NotNull ExternalSystemRunConfiguration configuration,
-                           @NotNull ExecutionEnvironment env) {
-      mySettings = settings;
-      myProject = project;
-      myConfiguration = configuration;
-      myEnv = env;
-      int port;
-      if (debug) {
-        try {
-          port = NetUtils.findAvailableSocketPort();
-        }
-        catch (IOException e) {
-          LOG.warn("Unexpected I/O exception occurred on attempt to find a free port to use for external system task debugging", e);
-          port = 0;
-        }
-      }
-      else {
-        port = 0;
-      }
-      myDebugPort = port;
-    }
-
-    public int getDebugPort() {
-      return myDebugPort;
-    }
-
-    @Nullable
-    public ServerSocket getForkSocket() {
-      if (myForkSocket == null && !DISABLE_FORK_DEBUGGER) {
-        try {
-          myForkSocket = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"));
-        }
-        catch (IOException e) {
-          LOG.error(e);
-        }
-      }
-      return myForkSocket;
-    }
-
-    @Nullable
-    @Override
-    public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
-      if (myProject.isDisposed()) return null;
-
-      String jvmParametersSetup = getJvmParametersSetup();
-
-      ApplicationManager.getApplication().assertIsDispatchThread();
-      FileDocumentManager.getInstance().saveAllDocuments();
-
-      final ExternalSystemExecuteTaskTask task = new ExternalSystemExecuteTaskTask(myProject, mySettings, jvmParametersSetup);
-      copyUserDataTo(task);
-
-      final String executionName = StringUtil.isNotEmpty(mySettings.getExecutionName())
-                                   ? mySettings.getExecutionName()
-                                   : StringUtil.isNotEmpty(myConfiguration.getName())
-                                     ? myConfiguration.getName() : AbstractExternalSystemTaskConfigurationType.generateName(
-                                     myProject, mySettings.getExternalSystemId(), mySettings.getExternalProjectPath(),
-                                     mySettings.getTaskNames(), mySettings.getExecutionName(), ": ", "");
-
-      final ExternalSystemProcessHandler processHandler = new ExternalSystemProcessHandler(task, executionName);
-      final ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration, ExecutionConsole, ProcessHandler>
-        consoleManager = getConsoleManagerFor(task);
-
-      final ExecutionConsole consoleView =
-        consoleManager.attachExecutionConsole(myProject, task, myEnv, processHandler);
-      AnAction[] restartActions;
-      if (consoleView == null) {
-        restartActions = AnAction.EMPTY_ARRAY;
-        Disposer.register(myProject, processHandler);
-      }
-      else {
-        Disposer.register(myProject, consoleView);
-        Disposer.register(consoleView, processHandler);
-        restartActions = consoleManager.getRestartActions(consoleView);
-      }
-      Class<? extends BuildProgressListener> progressListenerClazz = task.getUserData(PROGRESS_LISTENER_KEY);
-      final BuildProgressListener progressListener =
-        progressListenerClazz != null ? ServiceManager.getService(myProject, progressListenerClazz)
-                                      : createBuildView(task.getId(), executionName, task.getExternalProjectPath(), consoleView);
-
-      EP_NAME.forEachExtensionSafe(extension -> extension.attachToProcess(myConfiguration, processHandler, myEnv.getRunnerSettings()));
-
-      ApplicationManager.getApplication().executeOnPooledThread(() -> {
-        final String startDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
-        final String greeting;
-        final String settingsDescription = StringUtil.isEmpty(mySettings.toString()) ? "" : String.format(" '%s'", mySettings.toString());
-        if (mySettings.getTaskNames().size() > 1) {
-          greeting = ExternalSystemBundle.message("run.text.starting.multiple.task", startDateTime, settingsDescription) + "\n";
-        }
-        else {
-          greeting = ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, settingsDescription) + "\n";
-        }
-        processHandler.notifyTextAvailable(greeting + "\n", ProcessOutputTypes.SYSTEM);
-        try (BuildEventDispatcher eventDispatcher = new ExternalSystemEventDispatcher(task.getId(), progressListener, false)) {
-          ExternalSystemTaskNotificationListenerAdapter taskListener = new ExternalSystemTaskNotificationListenerAdapter() {
-            @Override
-            public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) {
-              if (progressListener != null) {
-                long eventTime = System.currentTimeMillis();
-                AnAction rerunTaskAction = new MyTaskRerunAction(progressListener, myEnv, myContentDescriptor);
-                BuildViewSettingsProvider viewSettingsProvider =
-                  consoleView instanceof BuildViewSettingsProvider ?
-                  new BuildViewSettingsProviderAdapter((BuildViewSettingsProvider)consoleView) : null;
-                progressListener.onEvent(id,
-                  new StartBuildEventImpl(new DefaultBuildDescriptor(id, executionName, workingDir, eventTime), "running...")
-                    .withProcessHandler(processHandler, view -> foldGreetingOrFarewell(consoleView, greeting, true))
-                    .withContentDescriptorSupplier(() -> myContentDescriptor)
-                    .withRestartAction(rerunTaskAction)
-                    .withRestartActions(restartActions)
-                    .withExecutionEnvironment(myEnv)
-                    .withBuildViewSettingsProvider(viewSettingsProvider)
-                );
-              }
-            }
-
-            @Override
-            public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
-              if (consoleView != null) {
-                consoleManager.onOutput(consoleView, processHandler, text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
-              }
-              else {
-                processHandler.notifyTextAvailable(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
-              }
-              eventDispatcher.setStdOut(stdOut);
-              eventDispatcher.append(text);
-            }
-
-            @Override
-            public void onFailure(@NotNull ExternalSystemTaskId id, @NotNull Exception e) {
-              FailureResult failureResult =
-                ExternalSystemUtil.createFailureResult(executionName + " failed", e, id.getProjectSystemId(), myProject);
-              eventDispatcher.onEvent(id, new FinishBuildEventImpl(id, null, System.currentTimeMillis(), "failed", failureResult));
-              processHandler.notifyProcessTerminated(1);
-            }
-
-            @Override
-            public void onSuccess(@NotNull ExternalSystemTaskId id) {
-              eventDispatcher.onEvent(id, new FinishBuildEventImpl(
-                id, null, System.currentTimeMillis(), "successful", new SuccessResultImpl()));
-            }
-
-            @Override
-            public void onStatusChange(@NotNull ExternalSystemTaskNotificationEvent event) {
-              if (event instanceof ExternalSystemBuildEvent) {
-                eventDispatcher.onEvent(event.getId(), ((ExternalSystemBuildEvent)event).getBuildEvent());
-              }
-              else if (event instanceof ExternalSystemTaskExecutionEvent) {
-                BuildEvent buildEvent = convert(((ExternalSystemTaskExecutionEvent)event));
-                eventDispatcher.onEvent(event.getId(), buildEvent);
-              }
-            }
-
-            @Override
-            public void onEnd(@NotNull ExternalSystemTaskId id) {
-              final String endDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
-              final String farewell;
-              if (mySettings.getTaskNames().size() > 1) {
-                farewell = ExternalSystemBundle.message("run.text.ended.multiple.task", endDateTime, settingsDescription);
-              }
-              else {
-                farewell = ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, settingsDescription);
-              }
-              processHandler.notifyTextAvailable(farewell + "\n", ProcessOutputTypes.SYSTEM);
-              foldGreetingOrFarewell(consoleView, farewell, false);
-              processHandler.notifyProcessTerminated(0);
-              eventDispatcher.close();
-            }
-          };
-          task.execute(ArrayUtil.prepend(taskListener, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions()));
-        }
-      });
-      ExecutionConsole executionConsole = progressListener instanceof ExecutionConsole ? (ExecutionConsole)progressListener : consoleView;
-      DefaultActionGroup actionGroup = new DefaultActionGroup();
-      if (executionConsole instanceof BuildView) {
-        actionGroup.addAll(((BuildView)executionConsole).getSwitchActions());
-        actionGroup.add(BuildTreeFilters.createFilteringActionsGroup((BuildView)executionConsole));
-      }
-      DefaultExecutionResult executionResult = new DefaultExecutionResult(executionConsole, processHandler, actionGroup.getChildren(null));
-      executionResult.setRestartActions(restartActions);
-      return executionResult;
-    }
-
-    @Nullable
-    private String getJvmParametersSetup() throws ExecutionException {
-      final SimpleJavaParameters extensionsJP = new SimpleJavaParameters();
-      EP_NAME.forEachExtensionSafe(
-        extension -> extension.updateVMParameters(myConfiguration, extensionsJP, myEnv.getRunnerSettings(), myEnv.getExecutor()));
-      String jvmParametersSetup;
-      if (myDebugPort > 0) {
-        jvmParametersSetup = DEBUG_SETUP_PREFIX + myDebugPort;
-        if (getForkSocket() != null) {
-          jvmParametersSetup += (" " + DEBUG_FORK_SOCKET_PARAM + getForkSocket().getLocalPort());
-        }
-      }
-      else {
-        final ParametersList allVMParameters = new ParametersList();
-        final ParametersList data = myEnv.getUserData(ExternalSystemTaskExecutionSettings.JVM_AGENT_SETUP_KEY);
-        if (data != null) {
-          for (String parameter : data.getList()) {
-            if (parameter.startsWith("-agentlib:")) continue;
-            if (parameter.startsWith("-agentpath:")) continue;
-            if (parameter.startsWith("-javaagent:")) continue;
-            throw new ExecutionException(ExternalSystemBundle.message("run.invalid.jvm.agent.configuration", parameter));
-          }
-          allVMParameters.addAll(data.getParameters());
-        }
-        allVMParameters.addAll(extensionsJP.getVMParametersList().getParameters());
-        jvmParametersSetup = allVMParameters.getParametersString();
-      }
-      return nullize(jvmParametersSetup);
-    }
-
-    private BuildProgressListener createBuildView(ExternalSystemTaskId id,
-                                                  String executionName,
-                                                  String workingDir,
-                                                  ExecutionConsole executionConsole) {
-      BuildDescriptor buildDescriptor = new DefaultBuildDescriptor(id, executionName, workingDir, System.currentTimeMillis());
-      return new BuildView(myProject, executionConsole, buildDescriptor, "build.toolwindow.run.selection.state",
-                           new ViewManager() {
-                             @Override
-                             public boolean isConsoleEnabledByDefault() {
-                               return true;
-                             }
-
-                             @Override
-                             public boolean isBuildContentView() {
-                               return false;
-                             }
-                           });
-    }
-
-    public void setContentDescriptor(@Nullable RunContentDescriptor contentDescriptor) {
-      myContentDescriptor = contentDescriptor;
-      if (contentDescriptor != null) {
-        contentDescriptor.setExecutionId(myEnv.getExecutionId());
-        RunnerAndConfigurationSettings settings = myEnv.getRunnerAndConfigurationSettings();
-        if (settings != null) {
-          contentDescriptor.setActivateToolWindowWhenAdded(settings.isActivateToolWindowBeforeRun());
-        }
-      }
-    }
-  }
-
-  private static void foldGreetingOrFarewell(@Nullable ExecutionConsole consoleView, String text, boolean isGreeting) {
+  static void foldGreetingOrFarewell(@Nullable ExecutionConsole consoleView, String text, boolean isGreeting) {
     int limit = 100;
     if (text.length() < limit) {
       return;
@@ -505,7 +217,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
     }
   }
 
-  private static class MyTaskRerunAction extends FakeRerunAction {
+  static class MyTaskRerunAction extends FakeRerunAction {
     private final BuildProgressListener myProgressListener;
     private final RunContentDescriptor myContentDescriptor;
     private final ExecutionEnvironment myEnvironment;
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunnableState.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunnableState.java
new file mode 100644 (file)
index 0000000..1deca11
--- /dev/null
@@ -0,0 +1,324 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.openapi.externalSystem.service.execution;
+
+import com.intellij.build.*;
+import com.intellij.build.events.BuildEvent;
+import com.intellij.build.events.FailureResult;
+import com.intellij.build.events.impl.FinishBuildEventImpl;
+import com.intellij.build.events.impl.StartBuildEventImpl;
+import com.intellij.build.events.impl.SuccessResultImpl;
+import com.intellij.execution.*;
+import com.intellij.execution.configurations.ParametersList;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.configurations.SimpleJavaParameters;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.execution.ui.ExecutionConsole;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
+import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent;
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
+import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
+import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemBuildEvent;
+import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemTaskExecutionEvent;
+import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper;
+import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.net.NetUtils;
+import com.intellij.util.text.DateFormatUtil;
+import com.intellij.xdebugger.XDebugProcess;
+import com.intellij.xdebugger.XDebugSession;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+
+import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.DEBUG_FORK_SOCKET_PARAM;
+import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX;
+import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.convert;
+import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.getConsoleManagerFor;
+import static com.intellij.openapi.util.text.StringUtil.nullize;
+
+public class ExternalSystemRunnableState extends UserDataHolderBase implements RunProfileState {
+
+  @NotNull private final ExternalSystemTaskExecutionSettings mySettings;
+  @NotNull private final Project myProject;
+  @NotNull private final ExternalSystemRunConfiguration myConfiguration;
+  @NotNull private final ExecutionEnvironment myEnv;
+  @Nullable private RunContentDescriptor myContentDescriptor;
+
+  private final int myDebugPort;
+  private ServerSocket myForkSocket = null;
+
+  public ExternalSystemRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
+                                     @NotNull Project project,
+                                     boolean debug,
+                                     @NotNull ExternalSystemRunConfiguration configuration,
+                                     @NotNull ExecutionEnvironment env) {
+    mySettings = settings;
+    myProject = project;
+    myConfiguration = configuration;
+    myEnv = env;
+    int port;
+    if (debug) {
+      try {
+        port = NetUtils.findAvailableSocketPort();
+      }
+      catch (IOException e) {
+        ExternalSystemRunConfiguration.LOG
+          .warn("Unexpected I/O exception occurred on attempt to find a free port to use for external system task debugging", e);
+        port = 0;
+      }
+    }
+    else {
+      port = 0;
+    }
+    myDebugPort = port;
+  }
+
+  public int getDebugPort() {
+    return myDebugPort;
+  }
+
+  @Nullable
+  public ServerSocket getForkSocket() {
+    if (myForkSocket == null && !ExternalSystemRunConfiguration.DISABLE_FORK_DEBUGGER) {
+      try {
+        myForkSocket = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"));
+      }
+      catch (IOException e) {
+        ExternalSystemRunConfiguration.LOG.error(e);
+      }
+    }
+    return myForkSocket;
+  }
+
+  @Nullable
+  @Override
+  public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
+    if (myProject.isDisposed()) return null;
+
+    String jvmParametersSetup = getJvmParametersSetup();
+
+    ApplicationManager.getApplication().assertIsDispatchThread();
+    FileDocumentManager.getInstance().saveAllDocuments();
+
+    final ExternalSystemExecuteTaskTask task = new ExternalSystemExecuteTaskTask(myProject, mySettings, jvmParametersSetup);
+    copyUserDataTo(task);
+
+    final String executionName = StringUtil.isNotEmpty(mySettings.getExecutionName())
+                                 ? mySettings.getExecutionName()
+                                 : StringUtil.isNotEmpty(myConfiguration.getName())
+                                   ? myConfiguration.getName() : AbstractExternalSystemTaskConfigurationType.generateName(
+                                   myProject, mySettings.getExternalSystemId(), mySettings.getExternalProjectPath(),
+                                   mySettings.getTaskNames(), mySettings.getExecutionName(), ": ", "");
+
+    final ExternalSystemProcessHandler processHandler = new ExternalSystemProcessHandler(task, executionName);
+    final ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration, ExecutionConsole, ProcessHandler>
+      consoleManager = getConsoleManagerFor(task);
+
+    final ExecutionConsole consoleView =
+      consoleManager.attachExecutionConsole(myProject, task, myEnv, processHandler);
+    AnAction[] restartActions;
+    if (consoleView == null) {
+      restartActions = AnAction.EMPTY_ARRAY;
+      Disposer.register(myProject, processHandler);
+    }
+    else {
+      Disposer.register(myProject, consoleView);
+      Disposer.register(consoleView, processHandler);
+      restartActions = consoleManager.getRestartActions(consoleView);
+    }
+    Class<? extends BuildProgressListener> progressListenerClazz = task.getUserData(ExternalSystemRunConfiguration.PROGRESS_LISTENER_KEY);
+    final BuildProgressListener progressListener =
+      progressListenerClazz != null ? ServiceManager.getService(myProject, progressListenerClazz)
+                                    : createBuildView(task.getId(), executionName, task.getExternalProjectPath(), consoleView);
+
+    ExternalSystemRunConfiguration.EP_NAME
+      .forEachExtensionSafe(extension -> extension.attachToProcess(myConfiguration, processHandler, myEnv.getRunnerSettings()));
+
+    ApplicationManager.getApplication().executeOnPooledThread(() -> {
+      final String startDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
+      final String greeting;
+      final String settingsDescription = StringUtil.isEmpty(mySettings.toString()) ? "" : String.format(" '%s'", mySettings.toString());
+      if (mySettings.getTaskNames().size() > 1) {
+        greeting = ExternalSystemBundle.message("run.text.starting.multiple.task", startDateTime, settingsDescription) + "\n";
+      }
+      else {
+        greeting = ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, settingsDescription) + "\n";
+      }
+      processHandler.notifyTextAvailable(greeting + "\n", ProcessOutputTypes.SYSTEM);
+      try (BuildEventDispatcher eventDispatcher = new ExternalSystemEventDispatcher(task.getId(), progressListener, false)) {
+        ExternalSystemTaskNotificationListenerAdapter taskListener = new ExternalSystemTaskNotificationListenerAdapter() {
+          @Override
+          public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) {
+            if (progressListener != null) {
+              long eventTime = System.currentTimeMillis();
+              AnAction rerunTaskAction = new ExternalSystemRunConfiguration.MyTaskRerunAction(progressListener, myEnv, myContentDescriptor);
+              BuildViewSettingsProvider viewSettingsProvider =
+                consoleView instanceof BuildViewSettingsProvider ?
+                new BuildViewSettingsProviderAdapter((BuildViewSettingsProvider)consoleView) : null;
+              progressListener.onEvent(id,
+                                       new StartBuildEventImpl(new DefaultBuildDescriptor(id, executionName, workingDir, eventTime),
+                                                               "running...")
+                                         .withProcessHandler(processHandler, view -> ExternalSystemRunConfiguration
+                                           .foldGreetingOrFarewell(consoleView, greeting, true))
+                                         .withContentDescriptorSupplier(() -> myContentDescriptor)
+                                         .withRestartAction(rerunTaskAction)
+                                         .withRestartActions(restartActions)
+                                         .withExecutionEnvironment(myEnv)
+                                         .withBuildViewSettingsProvider(viewSettingsProvider)
+              );
+            }
+          }
+
+          @Override
+          public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
+            if (consoleView != null) {
+              consoleManager.onOutput(consoleView, processHandler, text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
+            }
+            else {
+              processHandler.notifyTextAvailable(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
+            }
+            eventDispatcher.setStdOut(stdOut);
+            eventDispatcher.append(text);
+          }
+
+          @Override
+          public void onFailure(@NotNull ExternalSystemTaskId id, @NotNull Exception e) {
+            FailureResult failureResult =
+              ExternalSystemUtil.createFailureResult(executionName + " failed", e, id.getProjectSystemId(), myProject);
+            eventDispatcher.onEvent(id, new FinishBuildEventImpl(id, null, System.currentTimeMillis(), "failed", failureResult));
+            processHandler.notifyProcessTerminated(1);
+          }
+
+          @Override
+          public void onSuccess(@NotNull ExternalSystemTaskId id) {
+            eventDispatcher.onEvent(id, new FinishBuildEventImpl(
+              id, null, System.currentTimeMillis(), "successful", new SuccessResultImpl()));
+          }
+
+          @Override
+          public void onStatusChange(@NotNull ExternalSystemTaskNotificationEvent event) {
+            if (event instanceof ExternalSystemBuildEvent) {
+              eventDispatcher.onEvent(event.getId(), ((ExternalSystemBuildEvent)event).getBuildEvent());
+            }
+            else if (event instanceof ExternalSystemTaskExecutionEvent) {
+              BuildEvent buildEvent = convert(((ExternalSystemTaskExecutionEvent)event));
+              eventDispatcher.onEvent(event.getId(), buildEvent);
+            }
+          }
+
+          @Override
+          public void onEnd(@NotNull ExternalSystemTaskId id) {
+            final String endDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
+            final String farewell;
+            if (mySettings.getTaskNames().size() > 1) {
+              farewell = ExternalSystemBundle.message("run.text.ended.multiple.task", endDateTime, settingsDescription);
+            }
+            else {
+              farewell = ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, settingsDescription);
+            }
+            processHandler.notifyTextAvailable(farewell + "\n", ProcessOutputTypes.SYSTEM);
+            ExternalSystemRunConfiguration.foldGreetingOrFarewell(consoleView, farewell, false);
+            processHandler.notifyProcessTerminated(0);
+            eventDispatcher.close();
+          }
+        };
+        task.execute(ArrayUtil.prepend(taskListener, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions()));
+      }
+    });
+    ExecutionConsole executionConsole = progressListener instanceof ExecutionConsole ? (ExecutionConsole)progressListener : consoleView;
+    DefaultActionGroup actionGroup = new DefaultActionGroup();
+    if (executionConsole instanceof BuildView) {
+      actionGroup.addAll(((BuildView)executionConsole).getSwitchActions());
+      actionGroup.add(BuildTreeFilters.createFilteringActionsGroup((BuildView)executionConsole));
+    }
+    DefaultExecutionResult executionResult = new DefaultExecutionResult(executionConsole, processHandler, actionGroup.getChildren(null));
+    executionResult.setRestartActions(restartActions);
+    return executionResult;
+  }
+
+  @Nullable
+  private String getJvmParametersSetup() throws ExecutionException {
+    final SimpleJavaParameters extensionsJP = new SimpleJavaParameters();
+    ExternalSystemRunConfiguration.EP_NAME.forEachExtensionSafe(
+      extension -> extension.updateVMParameters(myConfiguration, extensionsJP, myEnv.getRunnerSettings(), myEnv.getExecutor()));
+    String jvmParametersSetup;
+    if (myDebugPort > 0) {
+      jvmParametersSetup = ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX + myDebugPort;
+      if (getForkSocket() != null) {
+        jvmParametersSetup += (" " + DEBUG_FORK_SOCKET_PARAM + getForkSocket().getLocalPort());
+      }
+    }
+    else {
+      final ParametersList allVMParameters = new ParametersList();
+      final ParametersList data = myEnv.getUserData(ExternalSystemTaskExecutionSettings.JVM_AGENT_SETUP_KEY);
+      if (data != null) {
+        for (String parameter : data.getList()) {
+          if (parameter.startsWith("-agentlib:")) continue;
+          if (parameter.startsWith("-agentpath:")) continue;
+          if (parameter.startsWith("-javaagent:")) continue;
+          throw new ExecutionException(ExternalSystemBundle.message("run.invalid.jvm.agent.configuration", parameter));
+        }
+        allVMParameters.addAll(data.getParameters());
+      }
+      allVMParameters.addAll(extensionsJP.getVMParametersList().getParameters());
+      jvmParametersSetup = allVMParameters.getParametersString();
+    }
+    return nullize(jvmParametersSetup);
+  }
+
+  private BuildProgressListener createBuildView(ExternalSystemTaskId id,
+                                                String executionName,
+                                                String workingDir,
+                                                ExecutionConsole executionConsole) {
+    BuildDescriptor buildDescriptor = new DefaultBuildDescriptor(id, executionName, workingDir, System.currentTimeMillis());
+    return new BuildView(myProject, executionConsole, buildDescriptor, "build.toolwindow.run.selection.state",
+                         new ViewManager() {
+                           @Override
+                           public boolean isConsoleEnabledByDefault() {
+                             return true;
+                           }
+
+                           @Override
+                           public boolean isBuildContentView() {
+                             return false;
+                           }
+                         });
+  }
+
+  public void setContentDescriptor(@Nullable RunContentDescriptor contentDescriptor) {
+    myContentDescriptor = contentDescriptor;
+    if (contentDescriptor != null) {
+      contentDescriptor.setExecutionId(myEnv.getExecutionId());
+      RunnerAndConfigurationSettings settings = myEnv.getRunnerAndConfigurationSettings();
+      if (settings != null) {
+        contentDescriptor.setActivateToolWindowWhenAdded(settings.isActivateToolWindowBeforeRun());
+      }
+    }
+  }
+
+  @Nullable
+  public XDebugProcess startDebugProcess(@NotNull XDebugSession session,
+                                         @NotNull ExecutionEnvironment env) {
+    return null;
+  }
+}
index 576b13c901c9fa42d876d6707eadbae4b9a93423..e3620c395106ca62a4f121b1fd707c4f1b342996 100644 (file)
@@ -43,7 +43,7 @@ class ExternalSystemTaskRunner : GenericProgramRunner<RunnerSettings>() {
 
   @Throws(ExecutionException::class)
   override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {
-    if (state !is ExternalSystemRunConfiguration.MyRunnableState && state !is HistoryTestRunnableState) {
+    if (state !is ExternalSystemRunnableState && state !is HistoryTestRunnableState) {
       return null
     }
 
@@ -54,7 +54,7 @@ class ExternalSystemTaskRunner : GenericProgramRunner<RunnerSettings>() {
       return runContentDescriptor
     }
 
-    (state as ExternalSystemRunConfiguration.MyRunnableState).setContentDescriptor(runContentDescriptor)
+    (state as ExternalSystemRunnableState).setContentDescriptor(runContentDescriptor)
     if (executionResult.executionConsole is BuildView) {
       return runContentDescriptor
     }
index c9bfac4826538c902dfeaf7e5cbfeb45dba892d2..3c88742e982a896d85ef348f29f8e1beb02d3a74 100644 (file)
@@ -25,13 +25,13 @@ public class ForkedDebuggerConfiguration {
 
   @Nullable
   public static ForkedDebuggerConfiguration parse(@Nullable String jvmAgentSetup) {
-    if (jvmAgentSetup != null && jvmAgentSetup.startsWith(ForkedDebuggerHelper.DEBUG_SETUP_PREFIX)) {
+    if (jvmAgentSetup != null && jvmAgentSetup.startsWith(ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX)) {
       int forkSocketIndex = jvmAgentSetup.indexOf(ForkedDebuggerHelper.DEBUG_FORK_SOCKET_PARAM);
       if (forkSocketIndex > 0) {
         try {
           int forkSocketPort =
             Integer.parseInt(jvmAgentSetup.substring(forkSocketIndex + ForkedDebuggerHelper.DEBUG_FORK_SOCKET_PARAM.length()));
-          int debugPort = Integer.parseInt(jvmAgentSetup.substring(ForkedDebuggerHelper.DEBUG_SETUP_PREFIX.length(), forkSocketIndex - 1));
+          int debugPort = Integer.parseInt(jvmAgentSetup.substring(ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX.length(), forkSocketIndex - 1));
           return new ForkedDebuggerConfiguration(forkSocketPort, debugPort);
         }
         catch (NumberFormatException ignore) {
@@ -42,6 +42,6 @@ public class ForkedDebuggerConfiguration {
   }
 
   public String getJvmAgentSetup(boolean isJdk9orLater) {
-    return ForkedDebuggerHelper.DEBUG_SETUP_PREFIX + (isJdk9orLater ? "127.0.0.1:" : "") + myDebugPort;
+    return ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX + (isJdk9orLater ? "127.0.0.1:" : "") + myDebugPort;
   }
 }
index db47d2c9c9256881b958409415633c996109b315..5c098ceca4f901eaf303ab28ce62c6d6c77b1450 100644 (file)
@@ -11,51 +11,57 @@ import java.net.Socket;
  */
 public class ForkedDebuggerHelper {
 
-  public static final String DEBUG_SETUP_PREFIX = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=";
+  public static final String JVM_DEBUG_SETUP_PREFIX = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=";
   public static final String DEBUG_FORK_SOCKET_PARAM = "-forkSocket";
 
-  public static String setupDebugger(String processName, int debugPort) {
-    String setup = "";
+  public static final String DEBUG_SERVER_PORT_KEY = "DEBUG_SERVER_PORT";
+  public static final String PARAMETERS_SEPARATOR = ";";
+
+  public static final String FINISH_PARAMS = "FINISH_PARAMS";
+
+  // returns port at which debugger is supposed to communicate with debuggee process
+  public static int setupDebugger(String debuggerId, String processName, String processParameters, int dispatchPort) {
+    int port = 0;
     try {
-      if (debugPort > -1) {
-        int debugAddress = findAvailableSocketPort();
-        if (debugAddress > -1) {
-          setup = DEBUG_SETUP_PREFIX + debugAddress;
-          send(debugAddress, processName, debugPort);
-        }
-      }
+      port = findAvailableSocketPort();
+      processParameters = (processParameters == null || processParameters.isEmpty()) ? "" : processParameters + PARAMETERS_SEPARATOR;
+      processParameters = processParameters + DEBUG_SERVER_PORT_KEY + "=" + port;
+      send(debuggerId, processName, processParameters, dispatchPort);
     }
-    catch (Exception e) {
+    catch (IOException e) {
       //noinspection CallToPrintStackTrace
       e.printStackTrace();
     }
-    return setup;
+    return port;
   }
 
-  public static void processFinished(String processName, int debugPort) {
-    send(0, processName, debugPort);
+  public static void signalizeFinish(String debuggerId, String processName, int dispatchPort) {
+    try {
+      send(debuggerId, processName, FINISH_PARAMS, dispatchPort);
+    }
+    catch (IOException e) {
+      //noinspection CallToPrintStackTrace
+      e.printStackTrace();
+    }
   }
 
-  private static void send(int signal, String processName, int debugPort) {
+  private static void send(String debuggerId, String processName, String processParameters, int dispatchPort) throws IOException {
+    Socket socket = new Socket("127.0.0.1", dispatchPort);
     try {
-      Socket socket = new Socket("127.0.0.1", debugPort);
+      DataOutputStream stream = new DataOutputStream(socket.getOutputStream());
       try {
-        DataOutputStream stream = new DataOutputStream(socket.getOutputStream());
-        try {
-          stream.writeInt(signal);
-          stream.writeUTF(processName);
-          // wait for the signal handling
-          int read = socket.getInputStream().read();
-        }
-        finally {
-          stream.close();
-        }
+        stream.writeUTF(debuggerId);
+        stream.writeUTF(processName);
+        stream.writeUTF(processParameters);
+        // wait for the signal handling
+        int read = socket.getInputStream().read();
       }
       finally {
-        socket.close();
+        stream.close();
       }
     }
-    catch (Exception ignore) {
+    finally {
+      socket.close();
     }
   }
 
@@ -76,6 +82,11 @@ public class ForkedDebuggerHelper {
           e.printStackTrace();
         }
       }
+
+      if (port <= -1) {
+        throw new IOException("Failed to find available port");
+      }
+
       return port;
     }
     finally {
index 587ae5c8132239989d79e3bfd7eb06b1ad374d10..a732cd6b11ed926a3f35356bc57ca3d926948d7a 100644 (file)
@@ -2,6 +2,8 @@
   <extensionPoints>
     <extensionPoint qualifiedName="com.intellij.externalSystemManager"
                     interface="com.intellij.openapi.externalSystem.ExternalSystemManager"/>
+    <extensionPoint qualifiedName="com.intellij.externalSystem.debuggerBackend"
+                    interface="com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension"/>
     <extensionPoint qualifiedName="com.intellij.externalSystemWorkspaceContributor"
                     interface="com.intellij.openapi.externalSystem.service.project.ExternalProjectsWorkspaceImpl$Contributor"/>
     <extensionPoint qualifiedName="com.intellij.externalProjectDataService"
index c9e46300b1a372d5cdd38f038bc42af0e0fbe6a9..a0c28693bb713a57564dd284135745d353ae3ade 100644 (file)
@@ -48,6 +48,7 @@
     <projectTaskRunner implementation="org.jetbrains.plugins.gradle.execution.build.GradleProjectTaskRunner" id="gradle" order="first"/>
     <buildProcess.parametersProvider implementation="org.jetbrains.plugins.gradle.compiler.GradleBuildProcessParametersProvider"/>
     <externalSystemNotificationExtension implementation="org.jetbrains.plugins.gradle.service.notification.GradleNotificationJavaExtension"/>
+    <externalSystem.debuggerBackend implementation="org.jetbrains.plugins.gradle.service.debugger.GradleJvmDebuggerBackend" />
     <externalProjectDataService implementation="org.jetbrains.plugins.gradle.service.project.data.BuildClasspathModuleGradleDataService"/>
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager"/>
 
diff --git a/plugins/gradle/java/src/service/debugger/GradleJvmDebuggerBackend.kt b/plugins/gradle/java/src/service/debugger/GradleJvmDebuggerBackend.kt
new file mode 100644 (file)
index 0000000..3203f54
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.plugins.gradle.service.debugger
+
+import com.intellij.execution.RunManager.Companion.getInstance
+import com.intellij.execution.RunnerAndConfigurationSettings
+import com.intellij.execution.remote.RemoteConfiguration
+import com.intellij.execution.remote.RemoteConfigurationType
+import com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension
+import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper
+import com.intellij.openapi.project.Project
+
+class GradleJvmDebuggerBackend : DebuggerBackendExtension {
+  override fun id() = "Gradle JVM"
+
+  override fun debugConfigurationSettings(project: Project,
+                                          processName: String,
+                                          processParameters: String): RunnerAndConfigurationSettings {
+    val runSettings = getInstance(project).createConfiguration(processName, RemoteConfigurationType::class.java)
+    val description = splitParameters(processParameters)
+
+    val configuration = runSettings.configuration as RemoteConfiguration
+    configuration.HOST = "localhost"
+    configuration.PORT = description[ForkedDebuggerHelper.DEBUG_SERVER_PORT_KEY]
+    configuration.USE_SOCKET_TRANSPORT = true
+    configuration.SERVER_MODE = true
+
+    return runSettings
+  }
+
+  override fun initializationCode(dispatchPort: String, parameters: String) = listOf(
+    "import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper",
+    "gradle.taskGraph.beforeTask { Task task ->",
+    "  if (task instanceof org.gradle.api.tasks.testing.Test) {",
+    "    task.maxParallelForks = 1",
+    "    task.forkEvery = 0",
+    "  }",
+    "  if (task instanceof JavaForkOptions) {",
+    "    def debugPort = ForkedDebuggerHelper.setupDebugger('${id()}', task.path, '$parameters', $dispatchPort)",
+    "    def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
+    "    jvmArgs << ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX + debugPort",
+    "    task.jvmArgs = jvmArgs",
+    "  }",
+    "}",
+    "gradle.taskGraph.afterTask { Task task ->",
+    "  if (task instanceof JavaForkOptions) {",
+    "    ForkedDebuggerHelper.signalizeFinish('${id()}', task.path, $dispatchPort)",
+    "  }",
+    "}")
+}
\ No newline at end of file
index 56d44a4f98c3381dde1e0b5e48f54b45927abba5..37d20e7b11ac300c1152a3d279161aac626794a9 100644 (file)
@@ -6,14 +6,18 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerConfiguration;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.util.io.StreamUtil;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.pom.java.LanguageLevel;
 import com.intellij.util.Consumer;
 import com.intellij.util.Function;
+import com.intellij.util.SystemProperties;
 import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.execution.ParametersListUtil;
 import org.gradle.tooling.model.idea.IdeaModule;
 import org.gradle.tooling.model.idea.IdeaProject;
 import org.jetbrains.annotations.NotNull;
@@ -31,6 +35,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @author Vladislav.Soroka
@@ -144,11 +149,11 @@ public class JavaGradleProjectResolver extends AbstractProjectResolverExtension
 
   @Override
   public void enhanceTaskProcessing(@NotNull List<String> taskNames,
-                                    @Nullable String jvmParametersSetup,
                                     @NotNull Consumer<String> initScriptConsumer,
-                                    boolean testExecutionExpected) {
+                                    @NotNull Map<String, String> parameters) {
+    String testExecutionExpected = parameters.get(GradleProjectResolverExtension.TEST_EXECUTION_EXPECTED_KEY);
 
-    if (testExecutionExpected) {
+    if (Boolean.valueOf(testExecutionExpected)) {
       try (InputStream stream = getClass().getResourceAsStream("/org/jetbrains/plugins/gradle/java/addTestListener.groovy")) {
         String addTestListenerScript = StreamUtil.readText(stream, StandardCharsets.UTF_8);
         initScriptConsumer.consume(addTestListenerScript);
@@ -157,6 +162,46 @@ public class JavaGradleProjectResolver extends AbstractProjectResolverExtension
         LOG.info(e);
       }
     }
+
+    String jvmParametersSetup = parameters.get(GradleProjectResolverExtension.JVM_PARAMETERS_SETUP_KEY);
+    enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer);
+  }
+
+  private String loadTestEventListenerDefinition() {
+    try(InputStream stream = getClass().getResourceAsStream("/org/jetbrains/plugins/gradle/IJTestLogger.groovy")) {
+      return StreamUtil.readText(stream, StandardCharsets.UTF_8);
+    } catch (IOException e) {
+      LOG.info(e);
+    }
+    return "";
+  }
+
+  @Override
+  public void enhanceTaskProcessing(@NotNull List<String> taskNames,
+                                    @Nullable String jvmParametersSetup,
+                                    @NotNull Consumer<String> initScriptConsumer) {
+    if (!StringUtil.isEmpty(jvmParametersSetup)) {
+      ForkedDebuggerConfiguration forkedDebuggerSetup = ForkedDebuggerConfiguration.parse(jvmParametersSetup);
+      if (forkedDebuggerSetup == null) {
+        final String names = "[\"" + StringUtil.join(taskNames, "\", \"") + "\"]";
+        final String jvmArgs = Arrays.stream(ParametersListUtil.parseToArray(jvmParametersSetup))
+          .map(s -> '\'' + s.trim().replace("\\", "\\\\") + '\'').collect(Collectors.joining(" << "));
+        final String[] lines = {
+          "gradle.taskGraph.beforeTask { Task task ->",
+          "    if (task instanceof JavaForkOptions && (" + names + ".contains(task.name) || " + names + ".contains(task.path))) {",
+          "        def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
+          "        jvmArgs << " + jvmArgs,
+          "        task.jvmArgs = jvmArgs",
+          "    }" +
+          "}",
+        };
+        final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
+        initScriptConsumer.consume(script);
+      }
+    }
+
+    final String testEventListenerDefinition = loadTestEventListenerDefinition();
+    initScriptConsumer.consume(testEventListenerDefinition);
   }
 
   @NotNull
index ac28745506232d9233986a42d07be3911be612ce..2b25c6fd31295ba2d236db511d9d99ecf93f4f7c 100644 (file)
@@ -15,6 +15,7 @@ import com.intellij.openapi.util.InvalidDataException;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.WriteExternalException;
 import org.jdom.Element;
+import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.plugins.gradle.GradleIdeManager;
@@ -25,6 +26,12 @@ public class GradleRunConfiguration extends ExternalSystemRunConfiguration imple
 
   public static final String DEBUG_FLAG_NAME = "GradleScriptDebugEnabled";
   public static final Key<Boolean> DEBUG_FLAG_KEY = Key.create("DEBUG_GRADLE_SCRIPT");
+
+  @ApiStatus.Internal
+  public static final Key<Integer> DEBUGGER_DISPATCH_PORT_KEY = Key.create("DEBUGGER_DISPATCH_PORT");
+  @ApiStatus.Internal
+  public static final Key<String> DEBUGGER_PARAMETERS_KEY = Key.create("DEBUGGER_PARAMETERS");
+
   private boolean isScriptDebugEnabled = true;
 
   public GradleRunConfiguration(Project project, ConfigurationFactory factory, String name) {
index 46adc3f6891aa4d83ee736ac907b29f622b22e69..6b475c7ee8cc4e30b272652cd649996bc596760a 100644 (file)
@@ -6,6 +6,7 @@ import com.google.gson.GsonBuilder;
 import com.intellij.execution.configurations.SimpleJavaParameters;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension;
 import com.intellij.openapi.externalSystem.model.ConfigurationDataImpl;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalSystemException;
@@ -837,69 +838,30 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
   public void enhanceTaskProcessing(@NotNull List<String> taskNames,
                                     @Nullable String jvmParametersSetup,
                                     @NotNull Consumer<String> initScriptConsumer) {
-    if (!StringUtil.isEmpty(jvmParametersSetup)) {
-      ForkedDebuggerConfiguration forkedDebuggerSetup = ForkedDebuggerConfiguration.parse(jvmParametersSetup);
-      if (forkedDebuggerSetup != null) {
-        setupDebugForAllJvmForkedTasks(initScriptConsumer, forkedDebuggerSetup.getForkSocketPort());
-      }
-      else {
-        final String names = "[\"" + StringUtil.join(taskNames, "\", \"") + "\"]";
-        final String jvmArgs = Arrays.stream(ParametersListUtil.parseToArray(jvmParametersSetup))
-          .map(s -> '\'' + s.trim().replace("\\", "\\\\") + '\'').collect(Collectors.joining(" << "));
-        final String[] lines = {
-          "gradle.taskGraph.beforeTask { Task task ->",
-          "    if (task instanceof JavaForkOptions && (" + names + ".contains(task.name) || " + names + ".contains(task.path))) {",
-          "        def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
-          "        jvmArgs << " + jvmArgs,
-          "        task.jvmArgs = jvmArgs",
-          "    }" +
-          "}",
-        };
-        final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
-        initScriptConsumer.consume(script);
-      }
-    }
-
-    final String testEventListenerDefinition = loadTestEventListenerDefinition();
-    initScriptConsumer.consume(testEventListenerDefinition);
   }
 
-  private String loadTestEventListenerDefinition() {
-    try (InputStream stream = getClass().getResourceAsStream("/org/jetbrains/plugins/gradle/IJTestLogger.groovy")) {
-      return StreamUtil.readText(stream, StandardCharsets.UTF_8);
+  @Override
+  public void enhanceTaskProcessing(@NotNull List<String> taskNames,
+                                    @NotNull Consumer<String> initScriptConsumer,
+                                    @NotNull Map<String, String> parameters) {
+    String dispatchPort = parameters.get(GradleProjectResolverExtension.DEBUG_DISPATCH_PORT_KEY);
+    if (dispatchPort == null) {
+      return;
     }
-    catch (IOException e) {
-      LOG.info(e);
+
+    String debugOptions = parameters.get(GradleProjectResolverExtension.DEBUG_OPTIONS_KEY);
+    if (debugOptions == null) {
+      debugOptions = "";
     }
-    return "";
-  }
+    List<String> lines = new ArrayList<>();
 
-  public void setupDebugForAllJvmForkedTasks(@NotNull Consumer<? super String> initScriptConsumer, int debugPort) {
-    // external-system-rt.jar
     String esRtJarPath = PathUtil.getCanonicalPath(PathManager.getJarPathForClass(ExternalSystemSourceType.class));
-    final String[] lines = {
-      "initscript {",
-      "  dependencies {",
-      "    classpath files(\"" + esRtJarPath + "\")",
-      "  }",
-      "}",
-      "gradle.taskGraph.beforeTask { Task task ->",
-      " if (task instanceof org.gradle.api.tasks.testing.Test) {",
-      "  task.maxParallelForks = 1",
-      "  task.forkEvery = 0",
-      " }",
-      " if (task instanceof JavaForkOptions) {",
-      "  def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
-      "  jvmArgs << com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.setupDebugger(task.path, " + debugPort + ")",
-      "  task.jvmArgs = jvmArgs",
-      " }",
-      "}",
-      "gradle.taskGraph.afterTask { Task task ->",
-      "    if (task instanceof JavaForkOptions) {",
-      "        com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.processFinished(task.path, " + debugPort + ")",
-      "    }",
-      "}",
-    };
+    lines.add("initscript { dependencies { classpath files(\""+ esRtJarPath + "\") } }"); // bring external-system-rt.jar
+
+    for (DebuggerBackendExtension extension: DebuggerBackendExtension.EP_NAME.getExtensionList()) {
+      lines.addAll(extension.initializationCode(dispatchPort, debugOptions));
+    }
+
     final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
     initScriptConsumer.consume(script);
   }
index 7bfa34de62defad9116f7c87ae6f0060e89ac596..1e5f53254ecbed8ea3c5cb9d4f62175463dc4778 100644 (file)
@@ -40,10 +40,7 @@ import org.jetbrains.plugins.gradle.GradleManager;
 import org.jetbrains.plugins.gradle.model.ModelsHolder;
 import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 /**
  * Allows to enhance {@link GradleProjectResolver} processing.
@@ -205,18 +202,30 @@ public interface GradleProjectResolverExtension extends ParametersEnhancer {
    */
   void enhanceTaskProcessing(@NotNull List<String> taskNames, @Nullable String jvmParametersSetup, @NotNull Consumer<String> initScriptConsumer);
 
+  // jvm configuration that will be applied to Gradle jvm
+  String JVM_PARAMETERS_SETUP_KEY = "JVM_PARAMETERS_SETUP";
+
+  // flag that shows if tasks will be treated as tests invocation by the IDE (e.g., test events are expected)
+  String TEST_EXECUTION_EXPECTED_KEY = "TEST_EXECUTION_EXPECTED";
+
+  // port for callbacks which Gradle tasks communicate to IDE
+  String DEBUG_DISPATCH_PORT_KEY = "DEBUG_DISPATCH_PORT";
+
+  // options passed from project to Gradle
+  String DEBUG_OPTIONS_KEY = "DEBUG_OPTIONS";
+
   /**
    * Allows extension to contribute to init script
    * @param taskNames gradle task names to be executed
    * @param jvmParametersSetup jvm configuration that will be applied to Gradle jvm
    * @param initScriptConsumer consumer of init script text. Must be called to add script txt
-   * @param testExecutionExpected flag that shows if tasks will be treated as tests invocation by the IDE (e.g., test events are expected)
+   * @param parameters storage for passing optional named parameters
    */
   @ApiStatus.Experimental
   default void enhanceTaskProcessing(@NotNull List<String> taskNames,
-                             @Nullable String jvmParametersSetup,
-                             @NotNull Consumer<String> initScriptConsumer,
-                             boolean testExecutionExpected) {
+                                     @NotNull Consumer<String> initScriptConsumer,
+                                     @NotNull Map<String, String> parameters) {
+    String jvmParametersSetup = parameters.get(JVM_PARAMETERS_SETUP_KEY);
     enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer);
   }
 }
index aa34391449c61bbd349dfdf163522ce25612f3bb..817234b00d2aa3feed2c591d5b6eb02a930d6305 100644 (file)
@@ -84,8 +84,11 @@ public class GradleTaskManager extends BaseExternalSystemTaskManager<GradleExecu
       settings == null ? new GradleExecutionSettings(null, null, DistributionType.BUNDLED, false) : settings;
 
     ForkedDebuggerConfiguration forkedDebuggerSetup = ForkedDebuggerConfiguration.parse(jvmParametersSetup);
-    if (forkedDebuggerSetup != null && isGradleScriptDebug(settings)) {
-      effectiveSettings.withVmOption(forkedDebuggerSetup.getJvmAgentSetup(isJdk9orLater(effectiveSettings.getJavaHome())));
+    if (forkedDebuggerSetup != null) {
+      if (isGradleScriptDebug(settings)) {
+        effectiveSettings.withVmOption(forkedDebuggerSetup.getJvmAgentSetup(isJdk9orLater(effectiveSettings.getJavaHome())));
+      }
+      effectiveSettings.putUserData(GradleRunConfiguration.DEBUGGER_DISPATCH_PORT_KEY, forkedDebuggerSetup.getForkSocketPort());
     }
 
     CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource();
@@ -180,8 +183,23 @@ public class GradleTaskManager extends BaseExternalSystemTaskManager<GradleExecu
             "//");
         }
       };
-      boolean isTestExecution = Boolean.TRUE == effectiveSettings.getUserData(GradleConstants.RUN_TASK_AS_TEST);
-      resolverExtension.enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer, isTestExecution);
+
+      Map<String, String> enhancementParameters = new HashMap<>();
+      enhancementParameters.put(GradleProjectResolverExtension.JVM_PARAMETERS_SETUP_KEY, jvmParametersSetup);
+
+      String isTestExecution = String.valueOf(Boolean.TRUE == effectiveSettings.getUserData(GradleConstants.RUN_TASK_AS_TEST));
+      enhancementParameters.put(GradleProjectResolverExtension.TEST_EXECUTION_EXPECTED_KEY, isTestExecution);
+
+      Integer debugDispatchPort = effectiveSettings.getUserData(GradleRunConfiguration.DEBUGGER_DISPATCH_PORT_KEY);
+
+      if (debugDispatchPort != null) {
+        enhancementParameters.put(GradleProjectResolverExtension.DEBUG_DISPATCH_PORT_KEY, String.valueOf(debugDispatchPort));
+        String debugOptions = effectiveSettings.getUserData(GradleRunConfiguration.DEBUGGER_PARAMETERS_KEY);
+        enhancementParameters.put(GradleProjectResolverExtension.DEBUG_OPTIONS_KEY, debugOptions);
+      }
+
+
+      resolverExtension.enhanceTaskProcessing(taskNames, initScriptConsumer, enhancementParameters);
     }
 
     if (!initScripts.isEmpty()) {