Gradle: cross-cutting debug of the Gradle groovy DSL(daemon) and subsequent java...
authorVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Mon, 26 Feb 2018 14:49:53 +0000 (17:49 +0300)
committerVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Mon, 26 Feb 2018 14:50:48 +0000 (17:50 +0300)
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunConfiguration.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskDebugRunner.java
platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerHelper.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradlePositionManager.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java

index f8f40fe901022d690a3cc4200249c9c6b8fd55cb..b01e007dec619b75172762f0cb60e7b3385788a8 100644 (file)
@@ -79,6 +79,8 @@ 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.List;
 
 import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.convert;
@@ -92,9 +94,11 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
                                                                                           SMRunnerConsolePropertiesProvider {
   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");
+  public static final String DEBUG_SETUP_PREFIX = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=";
 
   private 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.");
 
   public ExternalSystemRunConfiguration(@NotNull ProjectSystemId externalSystemId,
                                         Project project,
@@ -219,6 +223,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
     @Nullable private RunContentDescriptor myContentDescriptor;
 
     private final int myDebugPort;
+    private ServerSocket myForkSocket = null;
 
     public MyRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
                            @NotNull Project project,
@@ -250,6 +255,19 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
     }
 
     @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;
@@ -266,7 +284,10 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
 
       String jvmAgentSetup;
       if (myDebugPort > 0) {
-        jvmAgentSetup = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + myDebugPort;
+        jvmAgentSetup = DEBUG_SETUP_PREFIX + myDebugPort;
+        if (getForkSocket() != null) {
+          jvmAgentSetup += " -forkSocket" + getForkSocket().getLocalPort();
+        }
       }
       else {
         ParametersList parametersList = extensionsJP.getVMParametersList();
index aca70d3d55c306e233be31333a4e117e1ea35c57..31fd344720b892ad855b2adc996805d2ce321c79 100644 (file)
@@ -18,18 +18,40 @@ package com.intellij.openapi.externalSystem.service.execution;
 import com.intellij.build.BuildView;
 import com.intellij.debugger.impl.GenericDebuggerRunner;
 import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ProgramRunnerUtil;
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.configurations.ConfigurationFactory;
 import com.intellij.execution.configurations.RemoteConnection;
 import com.intellij.execution.configurations.RunProfile;
 import com.intellij.execution.configurations.RunProfileState;
 import com.intellij.execution.executors.DefaultDebugExecutor;
+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.ui.ExecutionConsole;
 import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
 /**
  * @author Denis Zhdanov
  * @since 6/7/13 11:18 AM
@@ -52,30 +74,18 @@ public class ExternalSystemTaskDebugRunner extends GenericDebuggerRunner {
   @Override
   protected RunContentDescriptor createContentDescriptor(@NotNull RunProfileState state, @NotNull ExecutionEnvironment environment) throws ExecutionException {
     if (state instanceof ExternalSystemRunConfiguration.MyRunnableState) {
-      int port = ((ExternalSystemRunConfiguration.MyRunnableState)state).getDebugPort();
+      ExternalSystemRunConfiguration.MyRunnableState myRunnableState = (ExternalSystemRunConfiguration.MyRunnableState)state;
+      int port = myRunnableState.getDebugPort();
       if (port > 0) {
-        RemoteConnection connection = new RemoteConnection(true, "127.0.0.1", String.valueOf(port), true);
-        RunContentDescriptor runContentDescriptor = attachVirtualMachine(state, environment, connection, true);
+        RunContentDescriptor runContentDescriptor = doGetRunContentDescriptor(myRunnableState, environment, port);
         if (runContentDescriptor == null) return null;
 
-        ((ExternalSystemRunConfiguration.MyRunnableState)state).setContentDescriptor(runContentDescriptor);
-
-        ExecutionConsole executionConsole = runContentDescriptor.getExecutionConsole();
-        if(executionConsole instanceof BuildView) {
-          return runContentDescriptor;
+        ProcessHandler processHandler = runContentDescriptor.getProcessHandler();
+        final ServerSocket socket = myRunnableState.getForkSocket();
+        if (socket != null && processHandler != null) {
+          new ForkedDebuggerThread(processHandler, socket, environment.getProject()).start();
         }
-        RunContentDescriptor descriptor =
-          new RunContentDescriptor(runContentDescriptor.getExecutionConsole(), runContentDescriptor.getProcessHandler(),
-                                   runContentDescriptor.getComponent(), runContentDescriptor.getDisplayName(),
-                                   runContentDescriptor.getIcon(), null,
-                                   runContentDescriptor.getRestartActions()) {
-            @Override
-            public boolean isHiddenContent() {
-              return true;
-            }
-          };
-        descriptor.setRunnerLayoutUi(runContentDescriptor.getRunnerLayoutUi());
-        return descriptor;
+        return runContentDescriptor;
       }
       else {
         LOG.warn("Can't attach debugger to external system task execution. Reason: target debug port is unknown");
@@ -90,4 +100,182 @@ public class ExternalSystemTaskDebugRunner extends GenericDebuggerRunner {
     }
     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);
+    if (runContentDescriptor == null) return null;
+
+    state.setContentDescriptor(runContentDescriptor);
+
+    ExecutionConsole executionConsole = runContentDescriptor.getExecutionConsole();
+    if (executionConsole instanceof BuildView) {
+      return runContentDescriptor;
+    }
+    RunContentDescriptor descriptor =
+      new RunContentDescriptor(runContentDescriptor.getExecutionConsole(), runContentDescriptor.getProcessHandler(),
+                               runContentDescriptor.getComponent(), runContentDescriptor.getDisplayName(),
+                               runContentDescriptor.getIcon(), null,
+                               runContentDescriptor.getRestartActions()) {
+        @Override
+        public boolean isHiddenContent() {
+          return true;
+        }
+      };
+    descriptor.setRunnerLayoutUi(runContentDescriptor.getRunnerLayoutUi());
+    return descriptor;
+  }
+
+  private static class ForkedDebuggerThread extends Thread {
+    private final ProcessHandler myProcessHandler;
+    private final ServerSocket mySocket;
+    private final Project myProject;
+
+    public ForkedDebuggerThread(ProcessHandler processHandler, ServerSocket socket, Project project) {
+      super("external task forked debugger runner");
+      setDaemon(true);
+      myProcessHandler = processHandler;
+      mySocket = socket;
+      myProject = project;
+      myProcessHandler.addProcessListener(new ProcessAdapter() {
+        @Override
+        public void processTerminated(@NotNull ProcessEvent event) {
+          closeSocket();
+        }
+
+        @Override
+        public void processWillTerminate(@NotNull ProcessEvent event, boolean willBeDestroyed) {
+          closeSocket();
+        }
+
+        void closeSocket() {
+          try {
+            mySocket.close();
+          }
+          catch (IOException ignore) {
+          }
+        }
+      });
+    }
+
+    @Override
+    public void run() {
+      while (!myProcessHandler.isProcessTerminated() && !myProcessHandler.isProcessTerminating() && !mySocket.isClosed()) {
+        try {
+          Socket accept = mySocket.accept();
+          handleForkedProcessSignal(accept, myProject, myProcessHandler);
+        }
+        catch (EOFException ignored) {
+        }
+        catch (IOException e) {
+          LOG.warn(e);
+        }
+      }
+      try {
+        mySocket.close();
+      }
+      catch (IOException e) {
+        LOG.debug(e);
+      }
+    }
+
+    public static void handleForkedProcessSignal(Socket accept, Project project, ProcessHandler processHandler) throws IOException {
+      try {
+        DataInputStream stream = new DataInputStream(accept.getInputStream());
+        try {
+          int signal = stream.readInt();
+          String processName = stream.readUTF();
+          if (signal > 0) {
+            String debugPort = String.valueOf(signal);
+            attachVM(project, processName, debugPort, new 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 processHandler = descriptor.getProcessHandler();
+                if (processHandler != null) {
+                  processHandler.addProcessListener(new ProcessAdapter() {
+                    @Override
+                    public void processTerminated(@NotNull ProcessEvent event) {
+                      final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
+                      ContentManager contentManager = toolWindowManager.getToolWindow(ToolWindowId.DEBUG).getContentManager();
+                      Content content = descriptor.getAttachedContent();
+                      if (content != null) {
+                        ApplicationManager.getApplication().invokeLater(() -> contentManager.removeContent(content, true));
+                      }
+                    }
+                  });
+                  try {
+                    accept.getOutputStream().write(0);
+                  }
+                  catch (IOException e) {
+                    LOG.debug(e);
+                  }
+                }
+              }
+            });
+          }
+          else if (signal == 0) {
+            // remove content for terminated forked processes
+            ApplicationManager.getApplication().invokeLater(() -> {
+              final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
+              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);
+              }
+              catch (IOException e) {
+                LOG.debug(e);
+              }
+            });
+          }
+        }
+        finally {
+          stream.close();
+        }
+      }
+      finally {
+        accept.close();
+      }
+    }
+
+    private static void attachVM(@NotNull Project project, String runConfigName, @NotNull String debugPort, Callback callback) {
+      RemoteConfigurationType remoteConfigurationType = RemoteConfigurationType.getInstance();
+      ConfigurationFactory factory = remoteConfigurationType.getFactory();
+      RunnerAndConfigurationSettings runSettings = RunManager.getInstance(project).createRunConfiguration(runConfigName, factory);
+      runSettings.setActivateToolWindowBeforeRun(false);
+
+      RemoteConfiguration configuration = (RemoteConfiguration)runSettings.getConfiguration();
+      configuration.HOST = "localhost";
+      configuration.PORT = debugPort;
+      configuration.USE_SOCKET_TRANSPORT = true;
+      configuration.SERVER_MODE = true;
+
+      try {
+        ExecutionEnvironment environment = ExecutionEnvironmentBuilder.create(DefaultDebugExecutor.getDebugExecutorInstance(), runSettings)
+                                                                      .contentToReuse(null)
+                                                                      .dataContext(null)
+                                                                      .activeTarget()
+                                                                      .build();
+        ProgramRunnerUtil.executeConfigurationAsync(environment, true, true, callback);
+      }
+      catch (ExecutionException e) {
+        LOG.error(e);
+      }
+    }
+  }
 }
diff --git a/platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerHelper.java b/platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerHelper.java
new file mode 100644 (file)
index 0000000..58eac59
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright 2000-2018 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.rt.execution;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * @author Vladislav.Soroka
+ */
+public class ForkedDebuggerHelper {
+
+  public static String setupDebugger(String processName, int debugPort) {
+    String setup = "";
+    try {
+      if (debugPort > -1) {
+        int debugAddress = findAvailableSocketPort();
+        if (debugAddress > -1) {
+          setup = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + debugAddress;
+          send(debugAddress, processName, debugPort);
+        }
+      }
+    }
+    catch (Exception e) {
+      //noinspection CallToPrintStackTrace
+      e.printStackTrace();
+    }
+    return setup;
+  }
+
+  public static void processFinished(String processName, int debugPort) {
+    send(0, processName, debugPort);
+  }
+
+  private static void send(int signal, String processName, int debugPort) {
+    try {
+      Socket socket = new Socket("127.0.0.1", debugPort);
+      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();
+        }
+      }
+      finally {
+        socket.close();
+      }
+    }
+    catch (Exception ignore) {
+    }
+  }
+
+  // copied from NetUtils
+  protected static int findAvailableSocketPort() throws IOException {
+    final ServerSocket serverSocket = new ServerSocket(0);
+    try {
+      int port = serverSocket.getLocalPort();
+      // workaround for linux : calling close() immediately after opening socket
+      // may result that socket is not closed
+      //noinspection SynchronizationOnLocalVariableOrMethodParameter
+      synchronized (serverSocket) {
+        try {
+          //noinspection WaitNotInLoop
+          serverSocket.wait(1);
+        }
+        catch (InterruptedException e) {
+          System.err.println(e);
+        }
+      }
+      return port;
+    }
+    finally {
+      serverSocket.close();
+    }
+  }
+}
index b530727f0331756962595243bac37fb74a3ebfab..31d8d9633d2d44b0ba8611ff03e4e158a91c9af7 100644 (file)
  */
 package org.jetbrains.plugins.gradle.config;
 
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectRootManager;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiManager;
 import com.intellij.psi.search.GlobalSearchScope;
@@ -34,41 +32,27 @@ import com.intellij.psi.util.CachedValue;
 import com.intellij.psi.util.CachedValueProvider;
 import com.intellij.psi.util.CachedValuesManager;
 import com.intellij.util.containers.ConcurrentFactoryMap;
-import com.intellij.util.lang.UrlClassLoader;
 import com.sun.jdi.AbsentInformationException;
 import com.sun.jdi.ReferenceType;
+import org.gradle.groovy.scripts.TextResourceScriptSource;
+import org.gradle.internal.resource.BasicTextResourceLoader;
+import org.gradle.internal.resource.TextResource;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.jetbrains.plugins.gradle.service.GradleInstallationManager;
 import org.jetbrains.plugins.groovy.extensions.debugger.ScriptPositionManagerHelper;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
 import org.jetbrains.plugins.groovy.runner.GroovyScriptUtil;
 
 import java.io.File;
-import java.net.URL;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
-/**
- * @author John Murph
- */
 public class GradlePositionManager extends ScriptPositionManagerHelper {
-
-  private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.gradle.config.GradlePositionManager");
-
   private static final Pattern GRADLE_CLASS_PATTERN = Pattern.compile(".*_gradle_.*");
   private static final String SCRIPT_CLOSURE_PREFIX = "build_";
-  private static final Key<CachedValue<ClassLoader>> GRADLE_CLASS_LOADER = Key.create("GRADLE_CLASS_LOADER");
   private static final Key<CachedValue<Map<File, String>>> GRADLE_CLASS_NAME = Key.create("GRADLE_CLASS_NAME");
 
-  private final GradleInstallationManager myLibraryManager;
-
-  public GradlePositionManager(@NotNull GradleInstallationManager manager) {
-    myLibraryManager = manager;
-  }
-
   @Override
   public boolean isAppropriateRuntimeName(@NotNull final String runtimeName) {
     return runtimeName.startsWith(SCRIPT_CLOSURE_PREFIX) || GRADLE_CLASS_PATTERN.matcher(runtimeName).matches();
@@ -92,12 +76,28 @@ public class GradlePositionManager extends ScriptPositionManagerHelper {
 
     final File scriptFile = VfsUtilCore.virtualToIoFile(virtualFile);
     final String className = CachedValuesManager.getManager(module.getProject())
-      .getCachedValue(module, GRADLE_CLASS_NAME, new ScriptSourceMapCalculator(module), false).get(scriptFile);
+                                                .getCachedValue(module, GRADLE_CLASS_NAME, new ScriptSourceMapCalculator(module), false)
+                                                .get(scriptFile);
     return className == null ? "" : className;
   }
 
+  @Nullable
   @Override
-  public PsiFile getExtraScriptIfNotFound(@NotNull ReferenceType refType, @NotNull String runtimeName, @NotNull Project project, @NotNull GlobalSearchScope scope) {
+  public String customizeClassName(@NotNull PsiClass psiClass) {
+    PsiFile file = psiClass.getContainingFile();
+    if (file instanceof GroovyFile) {
+      return getRuntimeScriptName((GroovyFile)file);
+    }
+    else {
+      return null;
+    }
+  }
+
+  @Override
+  public PsiFile getExtraScriptIfNotFound(@NotNull ReferenceType refType,
+                                          @NotNull String runtimeName,
+                                          @NotNull Project project,
+                                          @NotNull GlobalSearchScope scope) {
     String sourceFilePath = getScriptForClassName(refType);
     if (sourceFilePath == null) return null;
 
@@ -120,37 +120,7 @@ public class GradlePositionManager extends ScriptPositionManagerHelper {
     return null;
   }
 
-  @Nullable
-  private ClassLoader getGradleClassLoader(@NotNull final Module module) {
-    final Project project = module.getProject();
-    return CachedValuesManager.getManager(project).getCachedValue(module, GRADLE_CLASS_LOADER,
-                                                                  () -> CachedValueProvider.Result.create(createGradleClassLoader(module), ProjectRootManager.getInstance(project)), false);
-  }
-
-  @Nullable
-  private ClassLoader createGradleClassLoader(@NotNull Module module) {
-    String rootProjectPath = ExternalSystemModulePropertyManager.getInstance(module).getRootProjectPath();
-    if (StringUtil.isEmpty(rootProjectPath)) {
-      return null;
-    }
-    final VirtualFile sdkHome = myLibraryManager.getGradleHome(module, module.getProject(), rootProjectPath);
-    if (sdkHome == null) {
-      return null;
-    }
-
-    List<URL> urls = new ArrayList<>();
-    final VirtualFile libDir = sdkHome.findChild("lib");
-    assert libDir != null;
-    for (final VirtualFile child : libDir.getChildren()) {
-      if ("jar".equals(child.getExtension())) {
-        urls.add(VfsUtilCore.convertToURL(child.getUrl()));
-      }
-    }
-
-    return UrlClassLoader.build().urls(urls).get();
-  }
-
-  private class ScriptSourceMapCalculator implements CachedValueProvider<Map<File, String>> {
+  private static class ScriptSourceMapCalculator implements CachedValueProvider<Map<File, String>> {
     private final Module myModule;
 
     public ScriptSourceMapCalculator(Module module) {
@@ -159,36 +129,15 @@ public class GradlePositionManager extends ScriptPositionManagerHelper {
 
     @Override
     public Result<Map<File, String>> compute() {
-      final Map<File, String> result = ConcurrentFactoryMap.createMap(scriptFile -> calcClassName(scriptFile));
+      final Map<File, String> result = ConcurrentFactoryMap.createMap(ScriptSourceMapCalculator::calcClassName);
       return Result.create(result, ProjectRootManager.getInstance(myModule.getProject()));
     }
 
     @Nullable
-    private String calcClassName(File scriptFile) {
-      final ClassLoader loader = getGradleClassLoader(myModule);
-      if (loader != null) {
-        Class<?> fileScriptSource;
-        try {
-          fileScriptSource = Class.forName("org.gradle.groovy.scripts.UriScriptSource", true, loader);
-        }
-        catch (ClassNotFoundException e) {
-          try {
-            fileScriptSource = Class.forName("org.gradle.groovy.scripts.FileScriptSource", true, loader); //before 0.9
-          }
-          catch (ClassNotFoundException e1) {
-            return null;
-          }
-        }
-
-        try {
-          final Object source = fileScriptSource.getConstructor(String.class, File.class).newInstance("script", scriptFile);
-          return (String)fileScriptSource.getMethod("getClassName").invoke(source);
-        }
-        catch (Exception e) {
-          LOG.error(e);
-        }
-      }
-      return null;
+    private static String calcClassName(File scriptFile) {
+      TextResource resource = new BasicTextResourceLoader().loadFile("script", scriptFile);
+      TextResourceScriptSource scriptSource = new TextResourceScriptSource(resource);
+      return scriptSource.getClassName();
     }
   }
 }
index a9031fa01e408deac8946c2d240a1c41151f2e7e..58e469f7a164c7e2ea3067de39c60313ea89145c 100644 (file)
@@ -92,6 +92,7 @@ import static com.intellij.openapi.util.Pair.pair;
 import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.CONFIGURATION_ARTIFACTS;
 import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.MODULES_OUTPUTS;
 import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.*;
+import static org.jetbrains.plugins.gradle.service.task.GradleTaskManager.getForkedDebuggerSetup;
 
 /**
  * {@link BaseGradleProjectResolverExtension} provides base implementation of Gradle project resolver.
@@ -727,21 +728,53 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
                                     @Nullable String jvmAgentSetup,
                                     @NotNull Consumer<String> initScriptConsumer) {
     if (!StringUtil.isEmpty(jvmAgentSetup)) {
-      final String names = "[\"" + StringUtil.join(taskNames, "\", \"") + "\"]";
-      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 << '" + jvmAgentSetup.trim().replace("\\", "\\\\") + '\'',
-        "        task.jvmArgs = jvmArgs",
-        "    }" +
-        "}",
-      };
-      final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
-      initScriptConsumer.consume(script);
+      int debugPort = getForkedDebuggerSetup(jvmAgentSetup);
+      if (debugPort != -1) {
+        setupDebugForAllJvmForkedTasks(initScriptConsumer, debugPort);
+      }
+      else {
+        final String names = "[\"" + StringUtil.join(taskNames, "\", \"") + "\"]";
+        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 << '" + jvmAgentSetup.trim().replace("\\", "\\\\") + '\'',
+          "        task.jvmArgs = jvmArgs",
+          "    }" +
+          "}",
+        };
+        final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
+        initScriptConsumer.consume(script);
+      }
     }
   }
 
+  public void setupDebugForAllJvmForkedTasks(@NotNull Consumer<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 JavaForkOptions) {",
+      "  def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
+      "  vmArgs << 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 + ")",
+      "    }",
+      "}",
+    };
+    final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
+    initScriptConsumer.consume(script);
+  }
+
   @Override
   public void enhanceRemoteProcessing(@NotNull SimpleJavaParameters parameters) throws ExecutionException {
     PathsList classPath = parameters.getClassPath();
index ab93deb7c4012e90e984141689e2b2f0c6ab7ea5..6754ad796158014ba885e240f6801b418bcde323 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
 import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
 import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemProgressEventUnsupportedImpl;
 import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemTaskExecutionEvent;
+import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
 import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.util.Key;
@@ -85,6 +86,10 @@ public class GradleTaskManager implements ExternalSystemTaskManager<GradleExecut
 
     GradleExecutionSettings effectiveSettings =
       settings == null ? new GradleExecutionSettings(null, null, DistributionType.BUNDLED, false) : settings;
+
+    if (getForkedDebuggerSetup(jvmAgentSetup) != -1) {
+      effectiveSettings.withVmOption(jvmAgentSetup);
+    }
     Function<ProjectConnection, Void> f = connection -> {
       try {
         appendInitScriptArgument(taskNames, jvmAgentSetup, effectiveSettings);
@@ -127,6 +132,20 @@ public class GradleTaskManager implements ExternalSystemTaskManager<GradleExecut
     myHelper.execute(projectPath, effectiveSettings, f);
   }
 
+  public static int getForkedDebuggerSetup(@Nullable String jvmAgentSetup) {
+    if (jvmAgentSetup != null && jvmAgentSetup.startsWith(ExternalSystemRunConfiguration.DEBUG_SETUP_PREFIX)) {
+      int forkSocketIndex = jvmAgentSetup.indexOf("-forkSocket");
+      if (forkSocketIndex > 0) {
+        try {
+          return Integer.parseInt(jvmAgentSetup.substring(forkSocketIndex + "-forkSocket".length()));
+        }
+        catch (NumberFormatException ignore) {
+        }
+      }
+    }
+    return -1;
+  }
+
   public static void appendInitScriptArgument(@NotNull List<String> taskNames,
                                               @Nullable String jvmAgentSetup,
                                               @NotNull GradleExecutionSettings effectiveSettings) {