diff --git a/java/execution/impl/intellij.java.execution.impl.iml b/java/execution/impl/intellij.java.execution.impl.iml index 0873bb1247e0..33b02cfea857 100644 --- a/java/execution/impl/intellij.java.execution.impl.iml +++ b/java/execution/impl/intellij.java.execution.impl.iml @@ -29,6 +29,7 @@ + diff --git a/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskDebugRunner.java b/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskDebugRunner.java index 193c79c00286..1b9f7bc59b41 100644 --- a/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskDebugRunner.java +++ b/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskDebugRunner.java @@ -16,8 +16,16 @@ 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; + } } diff --git a/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ForkedDebuggerThread.java b/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ForkedDebuggerThread.java index 585edac80993..373f64818105 100644 --- a/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ForkedDebuggerThread.java +++ b/java/execution/impl/src/com/intellij/openapi/externalSystem/service/execution/ForkedDebuggerThread.java @@ -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); } - } - }); - } - 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); - } - }); + }); + break; + } } } - 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 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 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 index 000000000000..e31bee86feb9 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/debugger/DebuggerBackendExtension.java @@ -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 EP_NAME = ExtensionPointName.create("com.intellij.externalSystem.debuggerBackend"); + + String id(); + + default List initializationCode(@NotNull String dispatchPort, @NotNull String parameters) { + return new ArrayList(); + } + + RunnerAndConfigurationSettings debugConfigurationSettings(@NotNull Project project, + @NotNull String processName, + @NotNull String processParameters); + + default HashMap splitParameters(@NotNull String processParameters) { + HashMap 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 diff --git a/platform/external-system-impl/intellij.platform.externalSystem.impl.iml b/platform/external-system-impl/intellij.platform.externalSystem.impl.iml index 40b4bcf44189..1a5d2d747a00 100644 --- a/platform/external-system-impl/intellij.platform.externalSystem.impl.iml +++ b/platform/external-system-impl/intellij.platform.externalSystem.impl.iml @@ -20,5 +20,6 @@ + \ No newline at end of file diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunConfiguration.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunConfiguration.java index 4cf5a582ff79..7ad6910e985a 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunConfiguration.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunConfiguration.java @@ -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 EP_NAME + static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.externalSystem.runConfigurationExtension"); public static final Key RUN_INPUT_KEY = Key.create("RUN_INPUT_KEY"); public static final Key> 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 - 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 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 index 000000000000..1deca11c70dd --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemRunnableState.java @@ -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 + 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 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; + } +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskRunner.kt b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskRunner.kt index 576b13c901c9..e3620c395106 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskRunner.kt +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskRunner.kt @@ -43,7 +43,7 @@ class ExternalSystemTaskRunner : GenericProgramRunner() { @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() { return runContentDescriptor } - (state as ExternalSystemRunConfiguration.MyRunnableState).setContentDescriptor(runContentDescriptor) + (state as ExternalSystemRunnableState).setContentDescriptor(runContentDescriptor) if (executionResult.executionConsole is BuildView) { return runContentDescriptor } diff --git a/platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerConfiguration.java b/platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerConfiguration.java index c9bfac482653..3c88742e982a 100644 --- a/platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerConfiguration.java +++ b/platform/external-system-rt/src/com/intellij/openapi/externalSystem/rt/execution/ForkedDebuggerConfiguration.java @@ -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; } } 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 index db47d2c9c925..5c098ceca4f9 100644 --- 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 @@ -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); - } - - private static void send(int signal, String processName, int debugPort) { + public static void signalizeFinish(String debuggerId, String processName, int dispatchPort) { try { - Socket socket = new Socket("127.0.0.1", debugPort); + send(debuggerId, processName, FINISH_PARAMS, dispatchPort); + } + catch (IOException e) { + //noinspection CallToPrintStackTrace + e.printStackTrace(); + } + } + + private static void send(String debuggerId, String processName, String processParameters, int dispatchPort) throws IOException { + Socket socket = new Socket("127.0.0.1", dispatchPort); + try { + 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 { diff --git a/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml b/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml index 587ae5c81322..a732cd6b11ed 100644 --- a/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml @@ -2,6 +2,8 @@ + + diff --git a/plugins/gradle/java/src/service/debugger/GradleJvmDebuggerBackend.kt b/plugins/gradle/java/src/service/debugger/GradleJvmDebuggerBackend.kt new file mode 100644 index 000000000000..3203f540f9f3 --- /dev/null +++ b/plugins/gradle/java/src/service/debugger/GradleJvmDebuggerBackend.kt @@ -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 diff --git a/plugins/gradle/java/src/service/project/JavaGradleProjectResolver.java b/plugins/gradle/java/src/service/project/JavaGradleProjectResolver.java index 56d44a4f98c3..37d20e7b11ac 100644 --- a/plugins/gradle/java/src/service/project/JavaGradleProjectResolver.java +++ b/plugins/gradle/java/src/service/project/JavaGradleProjectResolver.java @@ -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 taskNames, - @Nullable String jvmParametersSetup, @NotNull Consumer initScriptConsumer, - boolean testExecutionExpected) { + @NotNull Map 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 taskNames, + @Nullable String jvmParametersSetup, + @NotNull Consumer 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 diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleRunConfiguration.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleRunConfiguration.java index ac2874550623..2b25c6fd3129 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleRunConfiguration.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleRunConfiguration.java @@ -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 DEBUG_FLAG_KEY = Key.create("DEBUG_GRADLE_SCRIPT"); + + @ApiStatus.Internal + public static final Key DEBUGGER_DISPATCH_PORT_KEY = Key.create("DEBUGGER_DISPATCH_PORT"); + @ApiStatus.Internal + public static final Key DEBUGGER_PARAMETERS_KEY = Key.create("DEBUGGER_PARAMETERS"); + private boolean isScriptDebugEnabled = true; public GradleRunConfiguration(Project project, ConfigurationFactory factory, String name) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 46adc3f6891a..6b475c7ee8cc 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -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 taskNames, @Nullable String jvmParametersSetup, @NotNull Consumer 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 taskNames, + @NotNull Consumer initScriptConsumer, + @NotNull Map parameters) { + String dispatchPort = parameters.get(GradleProjectResolverExtension.DEBUG_DISPATCH_PORT_KEY); + if (dispatchPort == null) { + return; } - catch (IOException e) { - LOG.info(e); - } - return ""; - } - public void setupDebugForAllJvmForkedTasks(@NotNull Consumer initScriptConsumer, int debugPort) { - // external-system-rt.jar + String debugOptions = parameters.get(GradleProjectResolverExtension.DEBUG_OPTIONS_KEY); + if (debugOptions == null) { + debugOptions = ""; + } + List lines = new ArrayList<>(); + 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); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java index 7bfa34de62de..1e5f53254ecb 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java @@ -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 taskNames, @Nullable String jvmParametersSetup, @NotNull Consumer 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 taskNames, - @Nullable String jvmParametersSetup, - @NotNull Consumer initScriptConsumer, - boolean testExecutionExpected) { + @NotNull Consumer initScriptConsumer, + @NotNull Map parameters) { + String jvmParametersSetup = parameters.get(JVM_PARAMETERS_SETUP_KEY); enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java index aa34391449c6..817234b00d2a 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java @@ -84,8 +84,11 @@ public class GradleTaskManager extends BaseExternalSystemTaskManager 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()) {