mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Support debug of non-jvm Gradle tasks in External System
GitOrigin-RevId: 2d9b816110abf38cbc9159558e696e729e0f4025
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b8ec126655
commit
cea74ff27f
@@ -29,6 +29,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.vcs.impl" />
|
||||
<orderEntry type="library" name="NanoXML" level="project" />
|
||||
<orderEntry type="library" name="jackson-databind" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger.impl" />
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
+74
-9
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+83
-71
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.debugger;
|
||||
|
||||
import com.intellij.execution.RunnerAndConfigurationSettings;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides ability to preconfigure tasks run by external system and to attach them with debugger.
|
||||
*/
|
||||
public interface DebuggerBackendExtension {
|
||||
ExtensionPointName<DebuggerBackendExtension> EP_NAME = ExtensionPointName.create("com.intellij.externalSystem.debuggerBackend");
|
||||
|
||||
String id();
|
||||
|
||||
default List<String> initializationCode(@NotNull String dispatchPort, @NotNull String parameters) {
|
||||
return new ArrayList();
|
||||
}
|
||||
|
||||
RunnerAndConfigurationSettings debugConfigurationSettings(@NotNull Project project,
|
||||
@NotNull String processName,
|
||||
@NotNull String processParameters);
|
||||
|
||||
default HashMap<String, String> splitParameters(@NotNull String processParameters) {
|
||||
HashMap<String, String> result = new HashMap();
|
||||
|
||||
final String[] envVars = processParameters.split(ForkedDebuggerHelper.PARAMETERS_SEPARATOR);
|
||||
for (String envVar : envVars) {
|
||||
final int idx = envVar.indexOf('=');
|
||||
if (idx > -1) {
|
||||
result.put(envVar.substring(0, idx), idx < envVar.length() - 1 ? envVar.substring(idx + 1) : "");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -20,5 +20,6 @@
|
||||
<orderEntry type="library" name="kotlin-reflect" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.objectSerializer" />
|
||||
<orderEntry type="module" module-name="intellij.platform.objectSerializer.annotations" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger" />
|
||||
</component>
|
||||
</module>
|
||||
+7
-295
@@ -2,51 +2,29 @@
|
||||
package com.intellij.openapi.externalSystem.service.execution;
|
||||
|
||||
import com.intellij.build.*;
|
||||
import com.intellij.build.events.BuildEvent;
|
||||
import com.intellij.build.events.FailureResult;
|
||||
import com.intellij.build.events.impl.FinishBuildEventImpl;
|
||||
import com.intellij.build.events.impl.StartBuildEventImpl;
|
||||
import com.intellij.build.events.impl.SuccessResultImpl;
|
||||
import com.intellij.diagnostic.logging.LogConfigurationPanel;
|
||||
import com.intellij.execution.*;
|
||||
import com.intellij.execution.configurations.*;
|
||||
import com.intellij.execution.console.DuplexConsoleView;
|
||||
import com.intellij.execution.impl.ConsoleViewImpl;
|
||||
import com.intellij.execution.impl.ExecutionManagerImpl;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.runners.FakeRerunAction;
|
||||
import com.intellij.execution.runners.ProgramRunner;
|
||||
import com.intellij.execution.ui.ExecutionConsole;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import com.intellij.openapi.editor.FoldingModel;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.externalSystem.ExternalSystemManager;
|
||||
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
|
||||
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
|
||||
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
|
||||
import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemBuildEvent;
|
||||
import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemTaskExecutionEvent;
|
||||
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.options.SettingsEditor;
|
||||
import com.intellij.openapi.options.SettingsEditorGroup;
|
||||
@@ -59,10 +37,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.ToolWindowId;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.GlobalSearchScopes;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.net.NetUtils;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.intellij.util.text.DateFormatUtil;
|
||||
import com.intellij.util.xmlb.Accessor;
|
||||
import com.intellij.util.xmlb.SerializationFilter;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
@@ -72,31 +47,24 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.DEBUG_FORK_SOCKET_PARAM;
|
||||
import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.DEBUG_SETUP_PREFIX;
|
||||
import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.convert;
|
||||
import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.getConsoleManagerFor;
|
||||
import static com.intellij.openapi.util.text.StringUtil.nullize;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
*/
|
||||
public class ExternalSystemRunConfiguration extends LocatableConfigurationBase implements SearchScopeProvidingRunProfile {
|
||||
private static final ExtensionPointName<ExternalSystemRunConfigurationExtension> EP_NAME
|
||||
static final ExtensionPointName<ExternalSystemRunConfigurationExtension> EP_NAME
|
||||
= ExtensionPointName.create("com.intellij.externalSystem.runConfigurationExtension");
|
||||
|
||||
public static final Key<InputStream> RUN_INPUT_KEY = Key.create("RUN_INPUT_KEY");
|
||||
public static final Key<Class<? extends BuildProgressListener>> PROGRESS_LISTENER_KEY = Key.create("PROGRESS_LISTENER_KEY");
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(ExternalSystemRunConfiguration.class);
|
||||
static final Logger LOG = Logger.getInstance(ExternalSystemRunConfiguration.class);
|
||||
private ExternalSystemTaskExecutionSettings mySettings = new ExternalSystemTaskExecutionSettings();
|
||||
private static final boolean DISABLE_FORK_DEBUGGER = Boolean.getBoolean("external.system.disable.fork.debugger");
|
||||
static final boolean DISABLE_FORK_DEBUGGER = Boolean.getBoolean("external.system.disable.fork.debugger");
|
||||
|
||||
public ExternalSystemRunConfiguration(@NotNull ProjectSystemId externalSystemId,
|
||||
Project project,
|
||||
@@ -178,7 +146,8 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
|
||||
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) {
|
||||
// DebugExecutor ID - com.intellij.execution.executors.DefaultDebugExecutor.EXECUTOR_ID
|
||||
String debugExecutorId = ToolWindowId.DEBUG;
|
||||
MyRunnableState runnableState = new MyRunnableState(mySettings, getProject(), debugExecutorId.equals(executor.getId()), this, env);
|
||||
ExternalSystemRunnableState
|
||||
runnableState = new ExternalSystemRunnableState(mySettings, getProject(), debugExecutorId.equals(executor.getId()), this, env);
|
||||
copyUserDataTo(runnableState);
|
||||
return runnableState;
|
||||
}
|
||||
@@ -203,264 +172,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
|
||||
return scope;
|
||||
}
|
||||
|
||||
public static class MyRunnableState extends UserDataHolderBase implements RunProfileState {
|
||||
|
||||
@NotNull private final ExternalSystemTaskExecutionSettings mySettings;
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final ExternalSystemRunConfiguration myConfiguration;
|
||||
@NotNull private final ExecutionEnvironment myEnv;
|
||||
@Nullable private RunContentDescriptor myContentDescriptor;
|
||||
|
||||
private final int myDebugPort;
|
||||
private ServerSocket myForkSocket = null;
|
||||
|
||||
public MyRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
|
||||
@NotNull Project project,
|
||||
boolean debug,
|
||||
@NotNull ExternalSystemRunConfiguration configuration,
|
||||
@NotNull ExecutionEnvironment env) {
|
||||
mySettings = settings;
|
||||
myProject = project;
|
||||
myConfiguration = configuration;
|
||||
myEnv = env;
|
||||
int port;
|
||||
if (debug) {
|
||||
try {
|
||||
port = NetUtils.findAvailableSocketPort();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn("Unexpected I/O exception occurred on attempt to find a free port to use for external system task debugging", e);
|
||||
port = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
port = 0;
|
||||
}
|
||||
myDebugPort = port;
|
||||
}
|
||||
|
||||
public int getDebugPort() {
|
||||
return myDebugPort;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerSocket getForkSocket() {
|
||||
if (myForkSocket == null && !DISABLE_FORK_DEBUGGER) {
|
||||
try {
|
||||
myForkSocket = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"));
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
return myForkSocket;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
|
||||
if (myProject.isDisposed()) return null;
|
||||
|
||||
String jvmParametersSetup = getJvmParametersSetup();
|
||||
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
FileDocumentManager.getInstance().saveAllDocuments();
|
||||
|
||||
final ExternalSystemExecuteTaskTask task = new ExternalSystemExecuteTaskTask(myProject, mySettings, jvmParametersSetup);
|
||||
copyUserDataTo(task);
|
||||
|
||||
final String executionName = StringUtil.isNotEmpty(mySettings.getExecutionName())
|
||||
? mySettings.getExecutionName()
|
||||
: StringUtil.isNotEmpty(myConfiguration.getName())
|
||||
? myConfiguration.getName() : AbstractExternalSystemTaskConfigurationType.generateName(
|
||||
myProject, mySettings.getExternalSystemId(), mySettings.getExternalProjectPath(),
|
||||
mySettings.getTaskNames(), mySettings.getExecutionName(), ": ", "");
|
||||
|
||||
final ExternalSystemProcessHandler processHandler = new ExternalSystemProcessHandler(task, executionName);
|
||||
final ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration, ExecutionConsole, ProcessHandler>
|
||||
consoleManager = getConsoleManagerFor(task);
|
||||
|
||||
final ExecutionConsole consoleView =
|
||||
consoleManager.attachExecutionConsole(myProject, task, myEnv, processHandler);
|
||||
AnAction[] restartActions;
|
||||
if (consoleView == null) {
|
||||
restartActions = AnAction.EMPTY_ARRAY;
|
||||
Disposer.register(myProject, processHandler);
|
||||
}
|
||||
else {
|
||||
Disposer.register(myProject, consoleView);
|
||||
Disposer.register(consoleView, processHandler);
|
||||
restartActions = consoleManager.getRestartActions(consoleView);
|
||||
}
|
||||
Class<? extends BuildProgressListener> progressListenerClazz = task.getUserData(PROGRESS_LISTENER_KEY);
|
||||
final BuildProgressListener progressListener =
|
||||
progressListenerClazz != null ? ServiceManager.getService(myProject, progressListenerClazz)
|
||||
: createBuildView(task.getId(), executionName, task.getExternalProjectPath(), consoleView);
|
||||
|
||||
EP_NAME.forEachExtensionSafe(extension -> extension.attachToProcess(myConfiguration, processHandler, myEnv.getRunnerSettings()));
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
final String startDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
|
||||
final String greeting;
|
||||
final String settingsDescription = StringUtil.isEmpty(mySettings.toString()) ? "" : String.format(" '%s'", mySettings.toString());
|
||||
if (mySettings.getTaskNames().size() > 1) {
|
||||
greeting = ExternalSystemBundle.message("run.text.starting.multiple.task", startDateTime, settingsDescription) + "\n";
|
||||
}
|
||||
else {
|
||||
greeting = ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, settingsDescription) + "\n";
|
||||
}
|
||||
processHandler.notifyTextAvailable(greeting + "\n", ProcessOutputTypes.SYSTEM);
|
||||
try (BuildEventDispatcher eventDispatcher = new ExternalSystemEventDispatcher(task.getId(), progressListener, false)) {
|
||||
ExternalSystemTaskNotificationListenerAdapter taskListener = new ExternalSystemTaskNotificationListenerAdapter() {
|
||||
@Override
|
||||
public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) {
|
||||
if (progressListener != null) {
|
||||
long eventTime = System.currentTimeMillis();
|
||||
AnAction rerunTaskAction = new MyTaskRerunAction(progressListener, myEnv, myContentDescriptor);
|
||||
BuildViewSettingsProvider viewSettingsProvider =
|
||||
consoleView instanceof BuildViewSettingsProvider ?
|
||||
new BuildViewSettingsProviderAdapter((BuildViewSettingsProvider)consoleView) : null;
|
||||
progressListener.onEvent(id,
|
||||
new StartBuildEventImpl(new DefaultBuildDescriptor(id, executionName, workingDir, eventTime), "running...")
|
||||
.withProcessHandler(processHandler, view -> foldGreetingOrFarewell(consoleView, greeting, true))
|
||||
.withContentDescriptorSupplier(() -> myContentDescriptor)
|
||||
.withRestartAction(rerunTaskAction)
|
||||
.withRestartActions(restartActions)
|
||||
.withExecutionEnvironment(myEnv)
|
||||
.withBuildViewSettingsProvider(viewSettingsProvider)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
|
||||
if (consoleView != null) {
|
||||
consoleManager.onOutput(consoleView, processHandler, text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
|
||||
}
|
||||
else {
|
||||
processHandler.notifyTextAvailable(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
|
||||
}
|
||||
eventDispatcher.setStdOut(stdOut);
|
||||
eventDispatcher.append(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull ExternalSystemTaskId id, @NotNull Exception e) {
|
||||
FailureResult failureResult =
|
||||
ExternalSystemUtil.createFailureResult(executionName + " failed", e, id.getProjectSystemId(), myProject);
|
||||
eventDispatcher.onEvent(id, new FinishBuildEventImpl(id, null, System.currentTimeMillis(), "failed", failureResult));
|
||||
processHandler.notifyProcessTerminated(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NotNull ExternalSystemTaskId id) {
|
||||
eventDispatcher.onEvent(id, new FinishBuildEventImpl(
|
||||
id, null, System.currentTimeMillis(), "successful", new SuccessResultImpl()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(@NotNull ExternalSystemTaskNotificationEvent event) {
|
||||
if (event instanceof ExternalSystemBuildEvent) {
|
||||
eventDispatcher.onEvent(event.getId(), ((ExternalSystemBuildEvent)event).getBuildEvent());
|
||||
}
|
||||
else if (event instanceof ExternalSystemTaskExecutionEvent) {
|
||||
BuildEvent buildEvent = convert(((ExternalSystemTaskExecutionEvent)event));
|
||||
eventDispatcher.onEvent(event.getId(), buildEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnd(@NotNull ExternalSystemTaskId id) {
|
||||
final String endDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
|
||||
final String farewell;
|
||||
if (mySettings.getTaskNames().size() > 1) {
|
||||
farewell = ExternalSystemBundle.message("run.text.ended.multiple.task", endDateTime, settingsDescription);
|
||||
}
|
||||
else {
|
||||
farewell = ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, settingsDescription);
|
||||
}
|
||||
processHandler.notifyTextAvailable(farewell + "\n", ProcessOutputTypes.SYSTEM);
|
||||
foldGreetingOrFarewell(consoleView, farewell, false);
|
||||
processHandler.notifyProcessTerminated(0);
|
||||
eventDispatcher.close();
|
||||
}
|
||||
};
|
||||
task.execute(ArrayUtil.prepend(taskListener, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions()));
|
||||
}
|
||||
});
|
||||
ExecutionConsole executionConsole = progressListener instanceof ExecutionConsole ? (ExecutionConsole)progressListener : consoleView;
|
||||
DefaultActionGroup actionGroup = new DefaultActionGroup();
|
||||
if (executionConsole instanceof BuildView) {
|
||||
actionGroup.addAll(((BuildView)executionConsole).getSwitchActions());
|
||||
actionGroup.add(BuildTreeFilters.createFilteringActionsGroup((BuildView)executionConsole));
|
||||
}
|
||||
DefaultExecutionResult executionResult = new DefaultExecutionResult(executionConsole, processHandler, actionGroup.getChildren(null));
|
||||
executionResult.setRestartActions(restartActions);
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getJvmParametersSetup() throws ExecutionException {
|
||||
final SimpleJavaParameters extensionsJP = new SimpleJavaParameters();
|
||||
EP_NAME.forEachExtensionSafe(
|
||||
extension -> extension.updateVMParameters(myConfiguration, extensionsJP, myEnv.getRunnerSettings(), myEnv.getExecutor()));
|
||||
String jvmParametersSetup;
|
||||
if (myDebugPort > 0) {
|
||||
jvmParametersSetup = DEBUG_SETUP_PREFIX + myDebugPort;
|
||||
if (getForkSocket() != null) {
|
||||
jvmParametersSetup += (" " + DEBUG_FORK_SOCKET_PARAM + getForkSocket().getLocalPort());
|
||||
}
|
||||
}
|
||||
else {
|
||||
final ParametersList allVMParameters = new ParametersList();
|
||||
final ParametersList data = myEnv.getUserData(ExternalSystemTaskExecutionSettings.JVM_AGENT_SETUP_KEY);
|
||||
if (data != null) {
|
||||
for (String parameter : data.getList()) {
|
||||
if (parameter.startsWith("-agentlib:")) continue;
|
||||
if (parameter.startsWith("-agentpath:")) continue;
|
||||
if (parameter.startsWith("-javaagent:")) continue;
|
||||
throw new ExecutionException(ExternalSystemBundle.message("run.invalid.jvm.agent.configuration", parameter));
|
||||
}
|
||||
allVMParameters.addAll(data.getParameters());
|
||||
}
|
||||
allVMParameters.addAll(extensionsJP.getVMParametersList().getParameters());
|
||||
jvmParametersSetup = allVMParameters.getParametersString();
|
||||
}
|
||||
return nullize(jvmParametersSetup);
|
||||
}
|
||||
|
||||
private BuildProgressListener createBuildView(ExternalSystemTaskId id,
|
||||
String executionName,
|
||||
String workingDir,
|
||||
ExecutionConsole executionConsole) {
|
||||
BuildDescriptor buildDescriptor = new DefaultBuildDescriptor(id, executionName, workingDir, System.currentTimeMillis());
|
||||
return new BuildView(myProject, executionConsole, buildDescriptor, "build.toolwindow.run.selection.state",
|
||||
new ViewManager() {
|
||||
@Override
|
||||
public boolean isConsoleEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuildContentView() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setContentDescriptor(@Nullable RunContentDescriptor contentDescriptor) {
|
||||
myContentDescriptor = contentDescriptor;
|
||||
if (contentDescriptor != null) {
|
||||
contentDescriptor.setExecutionId(myEnv.getExecutionId());
|
||||
RunnerAndConfigurationSettings settings = myEnv.getRunnerAndConfigurationSettings();
|
||||
if (settings != null) {
|
||||
contentDescriptor.setActivateToolWindowWhenAdded(settings.isActivateToolWindowBeforeRun());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void foldGreetingOrFarewell(@Nullable ExecutionConsole consoleView, String text, boolean isGreeting) {
|
||||
static void foldGreetingOrFarewell(@Nullable ExecutionConsole consoleView, String text, boolean isGreeting) {
|
||||
int limit = 100;
|
||||
if (text.length() < limit) {
|
||||
return;
|
||||
@@ -505,7 +217,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyTaskRerunAction extends FakeRerunAction {
|
||||
static class MyTaskRerunAction extends FakeRerunAction {
|
||||
private final BuildProgressListener myProgressListener;
|
||||
private final RunContentDescriptor myContentDescriptor;
|
||||
private final ExecutionEnvironment myEnvironment;
|
||||
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.externalSystem.service.execution;
|
||||
|
||||
import com.intellij.build.*;
|
||||
import com.intellij.build.events.BuildEvent;
|
||||
import com.intellij.build.events.FailureResult;
|
||||
import com.intellij.build.events.impl.FinishBuildEventImpl;
|
||||
import com.intellij.build.events.impl.StartBuildEventImpl;
|
||||
import com.intellij.build.events.impl.SuccessResultImpl;
|
||||
import com.intellij.execution.*;
|
||||
import com.intellij.execution.configurations.ParametersList;
|
||||
import com.intellij.execution.configurations.RunProfileState;
|
||||
import com.intellij.execution.configurations.SimpleJavaParameters;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.runners.ProgramRunner;
|
||||
import com.intellij.execution.ui.ExecutionConsole;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
|
||||
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
|
||||
import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemBuildEvent;
|
||||
import com.intellij.openapi.externalSystem.model.task.event.ExternalSystemTaskExecutionEvent;
|
||||
import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper;
|
||||
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.net.NetUtils;
|
||||
import com.intellij.util.text.DateFormatUtil;
|
||||
import com.intellij.xdebugger.XDebugProcess;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.DEBUG_FORK_SOCKET_PARAM;
|
||||
import static com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX;
|
||||
import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.convert;
|
||||
import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.getConsoleManagerFor;
|
||||
import static com.intellij.openapi.util.text.StringUtil.nullize;
|
||||
|
||||
public class ExternalSystemRunnableState extends UserDataHolderBase implements RunProfileState {
|
||||
|
||||
@NotNull private final ExternalSystemTaskExecutionSettings mySettings;
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final ExternalSystemRunConfiguration myConfiguration;
|
||||
@NotNull private final ExecutionEnvironment myEnv;
|
||||
@Nullable private RunContentDescriptor myContentDescriptor;
|
||||
|
||||
private final int myDebugPort;
|
||||
private ServerSocket myForkSocket = null;
|
||||
|
||||
public ExternalSystemRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
|
||||
@NotNull Project project,
|
||||
boolean debug,
|
||||
@NotNull ExternalSystemRunConfiguration configuration,
|
||||
@NotNull ExecutionEnvironment env) {
|
||||
mySettings = settings;
|
||||
myProject = project;
|
||||
myConfiguration = configuration;
|
||||
myEnv = env;
|
||||
int port;
|
||||
if (debug) {
|
||||
try {
|
||||
port = NetUtils.findAvailableSocketPort();
|
||||
}
|
||||
catch (IOException e) {
|
||||
ExternalSystemRunConfiguration.LOG
|
||||
.warn("Unexpected I/O exception occurred on attempt to find a free port to use for external system task debugging", e);
|
||||
port = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
port = 0;
|
||||
}
|
||||
myDebugPort = port;
|
||||
}
|
||||
|
||||
public int getDebugPort() {
|
||||
return myDebugPort;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerSocket getForkSocket() {
|
||||
if (myForkSocket == null && !ExternalSystemRunConfiguration.DISABLE_FORK_DEBUGGER) {
|
||||
try {
|
||||
myForkSocket = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"));
|
||||
}
|
||||
catch (IOException e) {
|
||||
ExternalSystemRunConfiguration.LOG.error(e);
|
||||
}
|
||||
}
|
||||
return myForkSocket;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
|
||||
if (myProject.isDisposed()) return null;
|
||||
|
||||
String jvmParametersSetup = getJvmParametersSetup();
|
||||
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
FileDocumentManager.getInstance().saveAllDocuments();
|
||||
|
||||
final ExternalSystemExecuteTaskTask task = new ExternalSystemExecuteTaskTask(myProject, mySettings, jvmParametersSetup);
|
||||
copyUserDataTo(task);
|
||||
|
||||
final String executionName = StringUtil.isNotEmpty(mySettings.getExecutionName())
|
||||
? mySettings.getExecutionName()
|
||||
: StringUtil.isNotEmpty(myConfiguration.getName())
|
||||
? myConfiguration.getName() : AbstractExternalSystemTaskConfigurationType.generateName(
|
||||
myProject, mySettings.getExternalSystemId(), mySettings.getExternalProjectPath(),
|
||||
mySettings.getTaskNames(), mySettings.getExecutionName(), ": ", "");
|
||||
|
||||
final ExternalSystemProcessHandler processHandler = new ExternalSystemProcessHandler(task, executionName);
|
||||
final ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration, ExecutionConsole, ProcessHandler>
|
||||
consoleManager = getConsoleManagerFor(task);
|
||||
|
||||
final ExecutionConsole consoleView =
|
||||
consoleManager.attachExecutionConsole(myProject, task, myEnv, processHandler);
|
||||
AnAction[] restartActions;
|
||||
if (consoleView == null) {
|
||||
restartActions = AnAction.EMPTY_ARRAY;
|
||||
Disposer.register(myProject, processHandler);
|
||||
}
|
||||
else {
|
||||
Disposer.register(myProject, consoleView);
|
||||
Disposer.register(consoleView, processHandler);
|
||||
restartActions = consoleManager.getRestartActions(consoleView);
|
||||
}
|
||||
Class<? extends BuildProgressListener> progressListenerClazz = task.getUserData(ExternalSystemRunConfiguration.PROGRESS_LISTENER_KEY);
|
||||
final BuildProgressListener progressListener =
|
||||
progressListenerClazz != null ? ServiceManager.getService(myProject, progressListenerClazz)
|
||||
: createBuildView(task.getId(), executionName, task.getExternalProjectPath(), consoleView);
|
||||
|
||||
ExternalSystemRunConfiguration.EP_NAME
|
||||
.forEachExtensionSafe(extension -> extension.attachToProcess(myConfiguration, processHandler, myEnv.getRunnerSettings()));
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
final String startDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
|
||||
final String greeting;
|
||||
final String settingsDescription = StringUtil.isEmpty(mySettings.toString()) ? "" : String.format(" '%s'", mySettings.toString());
|
||||
if (mySettings.getTaskNames().size() > 1) {
|
||||
greeting = ExternalSystemBundle.message("run.text.starting.multiple.task", startDateTime, settingsDescription) + "\n";
|
||||
}
|
||||
else {
|
||||
greeting = ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, settingsDescription) + "\n";
|
||||
}
|
||||
processHandler.notifyTextAvailable(greeting + "\n", ProcessOutputTypes.SYSTEM);
|
||||
try (BuildEventDispatcher eventDispatcher = new ExternalSystemEventDispatcher(task.getId(), progressListener, false)) {
|
||||
ExternalSystemTaskNotificationListenerAdapter taskListener = new ExternalSystemTaskNotificationListenerAdapter() {
|
||||
@Override
|
||||
public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) {
|
||||
if (progressListener != null) {
|
||||
long eventTime = System.currentTimeMillis();
|
||||
AnAction rerunTaskAction = new ExternalSystemRunConfiguration.MyTaskRerunAction(progressListener, myEnv, myContentDescriptor);
|
||||
BuildViewSettingsProvider viewSettingsProvider =
|
||||
consoleView instanceof BuildViewSettingsProvider ?
|
||||
new BuildViewSettingsProviderAdapter((BuildViewSettingsProvider)consoleView) : null;
|
||||
progressListener.onEvent(id,
|
||||
new StartBuildEventImpl(new DefaultBuildDescriptor(id, executionName, workingDir, eventTime),
|
||||
"running...")
|
||||
.withProcessHandler(processHandler, view -> ExternalSystemRunConfiguration
|
||||
.foldGreetingOrFarewell(consoleView, greeting, true))
|
||||
.withContentDescriptorSupplier(() -> myContentDescriptor)
|
||||
.withRestartAction(rerunTaskAction)
|
||||
.withRestartActions(restartActions)
|
||||
.withExecutionEnvironment(myEnv)
|
||||
.withBuildViewSettingsProvider(viewSettingsProvider)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
|
||||
if (consoleView != null) {
|
||||
consoleManager.onOutput(consoleView, processHandler, text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
|
||||
}
|
||||
else {
|
||||
processHandler.notifyTextAvailable(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
|
||||
}
|
||||
eventDispatcher.setStdOut(stdOut);
|
||||
eventDispatcher.append(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull ExternalSystemTaskId id, @NotNull Exception e) {
|
||||
FailureResult failureResult =
|
||||
ExternalSystemUtil.createFailureResult(executionName + " failed", e, id.getProjectSystemId(), myProject);
|
||||
eventDispatcher.onEvent(id, new FinishBuildEventImpl(id, null, System.currentTimeMillis(), "failed", failureResult));
|
||||
processHandler.notifyProcessTerminated(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NotNull ExternalSystemTaskId id) {
|
||||
eventDispatcher.onEvent(id, new FinishBuildEventImpl(
|
||||
id, null, System.currentTimeMillis(), "successful", new SuccessResultImpl()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(@NotNull ExternalSystemTaskNotificationEvent event) {
|
||||
if (event instanceof ExternalSystemBuildEvent) {
|
||||
eventDispatcher.onEvent(event.getId(), ((ExternalSystemBuildEvent)event).getBuildEvent());
|
||||
}
|
||||
else if (event instanceof ExternalSystemTaskExecutionEvent) {
|
||||
BuildEvent buildEvent = convert(((ExternalSystemTaskExecutionEvent)event));
|
||||
eventDispatcher.onEvent(event.getId(), buildEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnd(@NotNull ExternalSystemTaskId id) {
|
||||
final String endDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
|
||||
final String farewell;
|
||||
if (mySettings.getTaskNames().size() > 1) {
|
||||
farewell = ExternalSystemBundle.message("run.text.ended.multiple.task", endDateTime, settingsDescription);
|
||||
}
|
||||
else {
|
||||
farewell = ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, settingsDescription);
|
||||
}
|
||||
processHandler.notifyTextAvailable(farewell + "\n", ProcessOutputTypes.SYSTEM);
|
||||
ExternalSystemRunConfiguration.foldGreetingOrFarewell(consoleView, farewell, false);
|
||||
processHandler.notifyProcessTerminated(0);
|
||||
eventDispatcher.close();
|
||||
}
|
||||
};
|
||||
task.execute(ArrayUtil.prepend(taskListener, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions()));
|
||||
}
|
||||
});
|
||||
ExecutionConsole executionConsole = progressListener instanceof ExecutionConsole ? (ExecutionConsole)progressListener : consoleView;
|
||||
DefaultActionGroup actionGroup = new DefaultActionGroup();
|
||||
if (executionConsole instanceof BuildView) {
|
||||
actionGroup.addAll(((BuildView)executionConsole).getSwitchActions());
|
||||
actionGroup.add(BuildTreeFilters.createFilteringActionsGroup((BuildView)executionConsole));
|
||||
}
|
||||
DefaultExecutionResult executionResult = new DefaultExecutionResult(executionConsole, processHandler, actionGroup.getChildren(null));
|
||||
executionResult.setRestartActions(restartActions);
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getJvmParametersSetup() throws ExecutionException {
|
||||
final SimpleJavaParameters extensionsJP = new SimpleJavaParameters();
|
||||
ExternalSystemRunConfiguration.EP_NAME.forEachExtensionSafe(
|
||||
extension -> extension.updateVMParameters(myConfiguration, extensionsJP, myEnv.getRunnerSettings(), myEnv.getExecutor()));
|
||||
String jvmParametersSetup;
|
||||
if (myDebugPort > 0) {
|
||||
jvmParametersSetup = ForkedDebuggerHelper.JVM_DEBUG_SETUP_PREFIX + myDebugPort;
|
||||
if (getForkSocket() != null) {
|
||||
jvmParametersSetup += (" " + DEBUG_FORK_SOCKET_PARAM + getForkSocket().getLocalPort());
|
||||
}
|
||||
}
|
||||
else {
|
||||
final ParametersList allVMParameters = new ParametersList();
|
||||
final ParametersList data = myEnv.getUserData(ExternalSystemTaskExecutionSettings.JVM_AGENT_SETUP_KEY);
|
||||
if (data != null) {
|
||||
for (String parameter : data.getList()) {
|
||||
if (parameter.startsWith("-agentlib:")) continue;
|
||||
if (parameter.startsWith("-agentpath:")) continue;
|
||||
if (parameter.startsWith("-javaagent:")) continue;
|
||||
throw new ExecutionException(ExternalSystemBundle.message("run.invalid.jvm.agent.configuration", parameter));
|
||||
}
|
||||
allVMParameters.addAll(data.getParameters());
|
||||
}
|
||||
allVMParameters.addAll(extensionsJP.getVMParametersList().getParameters());
|
||||
jvmParametersSetup = allVMParameters.getParametersString();
|
||||
}
|
||||
return nullize(jvmParametersSetup);
|
||||
}
|
||||
|
||||
private BuildProgressListener createBuildView(ExternalSystemTaskId id,
|
||||
String executionName,
|
||||
String workingDir,
|
||||
ExecutionConsole executionConsole) {
|
||||
BuildDescriptor buildDescriptor = new DefaultBuildDescriptor(id, executionName, workingDir, System.currentTimeMillis());
|
||||
return new BuildView(myProject, executionConsole, buildDescriptor, "build.toolwindow.run.selection.state",
|
||||
new ViewManager() {
|
||||
@Override
|
||||
public boolean isConsoleEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuildContentView() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setContentDescriptor(@Nullable RunContentDescriptor contentDescriptor) {
|
||||
myContentDescriptor = contentDescriptor;
|
||||
if (contentDescriptor != null) {
|
||||
contentDescriptor.setExecutionId(myEnv.getExecutionId());
|
||||
RunnerAndConfigurationSettings settings = myEnv.getRunnerAndConfigurationSettings();
|
||||
if (settings != null) {
|
||||
contentDescriptor.setActivateToolWindowWhenAdded(settings.isActivateToolWindowBeforeRun());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public XDebugProcess startDebugProcess(@NotNull XDebugSession session,
|
||||
@NotNull ExecutionEnvironment env) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -43,7 +43,7 @@ class ExternalSystemTaskRunner : GenericProgramRunner<RunnerSettings>() {
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {
|
||||
if (state !is ExternalSystemRunConfiguration.MyRunnableState && state !is HistoryTestRunnableState) {
|
||||
if (state !is ExternalSystemRunnableState && state !is HistoryTestRunnableState) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ExternalSystemTaskRunner : GenericProgramRunner<RunnerSettings>() {
|
||||
return runContentDescriptor
|
||||
}
|
||||
|
||||
(state as ExternalSystemRunConfiguration.MyRunnableState).setContentDescriptor(runContentDescriptor)
|
||||
(state as ExternalSystemRunnableState).setContentDescriptor(runContentDescriptor)
|
||||
if (executionResult.executionConsole is BuildView) {
|
||||
return runContentDescriptor
|
||||
}
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+41
-30
@@ -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 {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.externalSystemManager"
|
||||
interface="com.intellij.openapi.externalSystem.ExternalSystemManager"/>
|
||||
<extensionPoint qualifiedName="com.intellij.externalSystem.debuggerBackend"
|
||||
interface="com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension"/>
|
||||
<extensionPoint qualifiedName="com.intellij.externalSystemWorkspaceContributor"
|
||||
interface="com.intellij.openapi.externalSystem.service.project.ExternalProjectsWorkspaceImpl$Contributor"/>
|
||||
<extensionPoint qualifiedName="com.intellij.externalProjectDataService"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<projectTaskRunner implementation="org.jetbrains.plugins.gradle.execution.build.GradleProjectTaskRunner" id="gradle" order="first"/>
|
||||
<buildProcess.parametersProvider implementation="org.jetbrains.plugins.gradle.compiler.GradleBuildProcessParametersProvider"/>
|
||||
<externalSystemNotificationExtension implementation="org.jetbrains.plugins.gradle.service.notification.GradleNotificationJavaExtension"/>
|
||||
<externalSystem.debuggerBackend implementation="org.jetbrains.plugins.gradle.service.debugger.GradleJvmDebuggerBackend" />
|
||||
<externalProjectDataService implementation="org.jetbrains.plugins.gradle.service.project.data.BuildClasspathModuleGradleDataService"/>
|
||||
<projectService serviceImplementation="org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager"/>
|
||||
|
||||
|
||||
@@ -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)",
|
||||
" }",
|
||||
"}")
|
||||
}
|
||||
@@ -6,14 +6,18 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.externalSystem.model.DataNode;
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData;
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData;
|
||||
import com.intellij.openapi.externalSystem.rt.execution.ForkedDebuggerConfiguration;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
|
||||
import com.intellij.openapi.externalSystem.util.Order;
|
||||
import com.intellij.openapi.util.io.StreamUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.execution.ParametersListUtil;
|
||||
import org.gradle.tooling.model.idea.IdeaModule;
|
||||
import org.gradle.tooling.model.idea.IdeaProject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -31,6 +35,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
@@ -144,11 +149,11 @@ public class JavaGradleProjectResolver extends AbstractProjectResolverExtension
|
||||
|
||||
@Override
|
||||
public void enhanceTaskProcessing(@NotNull List<String> taskNames,
|
||||
@Nullable String jvmParametersSetup,
|
||||
@NotNull Consumer<String> initScriptConsumer,
|
||||
boolean testExecutionExpected) {
|
||||
@NotNull Map<String, String> parameters) {
|
||||
String testExecutionExpected = parameters.get(GradleProjectResolverExtension.TEST_EXECUTION_EXPECTED_KEY);
|
||||
|
||||
if (testExecutionExpected) {
|
||||
if (Boolean.valueOf(testExecutionExpected)) {
|
||||
try (InputStream stream = getClass().getResourceAsStream("/org/jetbrains/plugins/gradle/java/addTestListener.groovy")) {
|
||||
String addTestListenerScript = StreamUtil.readText(stream, StandardCharsets.UTF_8);
|
||||
initScriptConsumer.consume(addTestListenerScript);
|
||||
@@ -157,6 +162,46 @@ public class JavaGradleProjectResolver extends AbstractProjectResolverExtension
|
||||
LOG.info(e);
|
||||
}
|
||||
}
|
||||
|
||||
String jvmParametersSetup = parameters.get(GradleProjectResolverExtension.JVM_PARAMETERS_SETUP_KEY);
|
||||
enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer);
|
||||
}
|
||||
|
||||
private String loadTestEventListenerDefinition() {
|
||||
try(InputStream stream = getClass().getResourceAsStream("/org/jetbrains/plugins/gradle/IJTestLogger.groovy")) {
|
||||
return StreamUtil.readText(stream, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
LOG.info(e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enhanceTaskProcessing(@NotNull List<String> taskNames,
|
||||
@Nullable String jvmParametersSetup,
|
||||
@NotNull Consumer<String> initScriptConsumer) {
|
||||
if (!StringUtil.isEmpty(jvmParametersSetup)) {
|
||||
ForkedDebuggerConfiguration forkedDebuggerSetup = ForkedDebuggerConfiguration.parse(jvmParametersSetup);
|
||||
if (forkedDebuggerSetup == null) {
|
||||
final String names = "[\"" + StringUtil.join(taskNames, "\", \"") + "\"]";
|
||||
final String jvmArgs = Arrays.stream(ParametersListUtil.parseToArray(jvmParametersSetup))
|
||||
.map(s -> '\'' + s.trim().replace("\\", "\\\\") + '\'').collect(Collectors.joining(" << "));
|
||||
final String[] lines = {
|
||||
"gradle.taskGraph.beforeTask { Task task ->",
|
||||
" if (task instanceof JavaForkOptions && (" + names + ".contains(task.name) || " + names + ".contains(task.path))) {",
|
||||
" def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
|
||||
" jvmArgs << " + jvmArgs,
|
||||
" task.jvmArgs = jvmArgs",
|
||||
" }" +
|
||||
"}",
|
||||
};
|
||||
final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
|
||||
initScriptConsumer.consume(script);
|
||||
}
|
||||
}
|
||||
|
||||
final String testEventListenerDefinition = loadTestEventListenerDefinition();
|
||||
initScriptConsumer.consume(testEventListenerDefinition);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+7
@@ -15,6 +15,7 @@ import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.gradle.GradleIdeManager;
|
||||
@@ -25,6 +26,12 @@ public class GradleRunConfiguration extends ExternalSystemRunConfiguration imple
|
||||
|
||||
public static final String DEBUG_FLAG_NAME = "GradleScriptDebugEnabled";
|
||||
public static final Key<Boolean> DEBUG_FLAG_KEY = Key.create("DEBUG_GRADLE_SCRIPT");
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static final Key<Integer> DEBUGGER_DISPATCH_PORT_KEY = Key.create("DEBUGGER_DISPATCH_PORT");
|
||||
@ApiStatus.Internal
|
||||
public static final Key<String> DEBUGGER_PARAMETERS_KEY = Key.create("DEBUGGER_PARAMETERS");
|
||||
|
||||
private boolean isScriptDebugEnabled = true;
|
||||
|
||||
public GradleRunConfiguration(Project project, ConfigurationFactory factory, String name) {
|
||||
|
||||
+20
-58
@@ -6,6 +6,7 @@ import com.google.gson.GsonBuilder;
|
||||
import com.intellij.execution.configurations.SimpleJavaParameters;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension;
|
||||
import com.intellij.openapi.externalSystem.model.ConfigurationDataImpl;
|
||||
import com.intellij.openapi.externalSystem.model.DataNode;
|
||||
import com.intellij.openapi.externalSystem.model.ExternalSystemException;
|
||||
@@ -837,69 +838,30 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
|
||||
public void enhanceTaskProcessing(@NotNull List<String> taskNames,
|
||||
@Nullable String jvmParametersSetup,
|
||||
@NotNull Consumer<String> initScriptConsumer) {
|
||||
if (!StringUtil.isEmpty(jvmParametersSetup)) {
|
||||
ForkedDebuggerConfiguration forkedDebuggerSetup = ForkedDebuggerConfiguration.parse(jvmParametersSetup);
|
||||
if (forkedDebuggerSetup != null) {
|
||||
setupDebugForAllJvmForkedTasks(initScriptConsumer, forkedDebuggerSetup.getForkSocketPort());
|
||||
}
|
||||
else {
|
||||
final String names = "[\"" + StringUtil.join(taskNames, "\", \"") + "\"]";
|
||||
final String jvmArgs = Arrays.stream(ParametersListUtil.parseToArray(jvmParametersSetup))
|
||||
.map(s -> '\'' + s.trim().replace("\\", "\\\\") + '\'').collect(Collectors.joining(" << "));
|
||||
final String[] lines = {
|
||||
"gradle.taskGraph.beforeTask { Task task ->",
|
||||
" if (task instanceof JavaForkOptions && (" + names + ".contains(task.name) || " + names + ".contains(task.path))) {",
|
||||
" def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib:jdwp') && !it?.startsWith('-Xrunjdwp')}",
|
||||
" jvmArgs << " + jvmArgs,
|
||||
" task.jvmArgs = jvmArgs",
|
||||
" }" +
|
||||
"}",
|
||||
};
|
||||
final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
|
||||
initScriptConsumer.consume(script);
|
||||
}
|
||||
}
|
||||
|
||||
final String testEventListenerDefinition = loadTestEventListenerDefinition();
|
||||
initScriptConsumer.consume(testEventListenerDefinition);
|
||||
}
|
||||
|
||||
private String loadTestEventListenerDefinition() {
|
||||
try (InputStream stream = getClass().getResourceAsStream("/org/jetbrains/plugins/gradle/IJTestLogger.groovy")) {
|
||||
return StreamUtil.readText(stream, StandardCharsets.UTF_8);
|
||||
@Override
|
||||
public void enhanceTaskProcessing(@NotNull List<String> taskNames,
|
||||
@NotNull Consumer<String> initScriptConsumer,
|
||||
@NotNull Map<String, String> parameters) {
|
||||
String dispatchPort = parameters.get(GradleProjectResolverExtension.DEBUG_DISPATCH_PORT_KEY);
|
||||
if (dispatchPort == null) {
|
||||
return;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setupDebugForAllJvmForkedTasks(@NotNull Consumer<? super String> initScriptConsumer, int debugPort) {
|
||||
// external-system-rt.jar
|
||||
String debugOptions = parameters.get(GradleProjectResolverExtension.DEBUG_OPTIONS_KEY);
|
||||
if (debugOptions == null) {
|
||||
debugOptions = "";
|
||||
}
|
||||
List<String> 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);
|
||||
}
|
||||
|
||||
+17
-8
@@ -40,10 +40,7 @@ import org.jetbrains.plugins.gradle.GradleManager;
|
||||
import org.jetbrains.plugins.gradle.model.ModelsHolder;
|
||||
import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Allows to enhance {@link GradleProjectResolver} processing.
|
||||
@@ -205,18 +202,30 @@ public interface GradleProjectResolverExtension extends ParametersEnhancer {
|
||||
*/
|
||||
void enhanceTaskProcessing(@NotNull List<String> taskNames, @Nullable String jvmParametersSetup, @NotNull Consumer<String> initScriptConsumer);
|
||||
|
||||
// jvm configuration that will be applied to Gradle jvm
|
||||
String JVM_PARAMETERS_SETUP_KEY = "JVM_PARAMETERS_SETUP";
|
||||
|
||||
// flag that shows if tasks will be treated as tests invocation by the IDE (e.g., test events are expected)
|
||||
String TEST_EXECUTION_EXPECTED_KEY = "TEST_EXECUTION_EXPECTED";
|
||||
|
||||
// port for callbacks which Gradle tasks communicate to IDE
|
||||
String DEBUG_DISPATCH_PORT_KEY = "DEBUG_DISPATCH_PORT";
|
||||
|
||||
// options passed from project to Gradle
|
||||
String DEBUG_OPTIONS_KEY = "DEBUG_OPTIONS";
|
||||
|
||||
/**
|
||||
* Allows extension to contribute to init script
|
||||
* @param taskNames gradle task names to be executed
|
||||
* @param jvmParametersSetup jvm configuration that will be applied to Gradle jvm
|
||||
* @param initScriptConsumer consumer of init script text. Must be called to add script txt
|
||||
* @param testExecutionExpected flag that shows if tasks will be treated as tests invocation by the IDE (e.g., test events are expected)
|
||||
* @param parameters storage for passing optional named parameters
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
default void enhanceTaskProcessing(@NotNull List<String> taskNames,
|
||||
@Nullable String jvmParametersSetup,
|
||||
@NotNull Consumer<String> initScriptConsumer,
|
||||
boolean testExecutionExpected) {
|
||||
@NotNull Consumer<String> initScriptConsumer,
|
||||
@NotNull Map<String, String> parameters) {
|
||||
String jvmParametersSetup = parameters.get(JVM_PARAMETERS_SETUP_KEY);
|
||||
enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-4
@@ -84,8 +84,11 @@ public class GradleTaskManager extends BaseExternalSystemTaskManager<GradleExecu
|
||||
settings == null ? new GradleExecutionSettings(null, null, DistributionType.BUNDLED, false) : settings;
|
||||
|
||||
ForkedDebuggerConfiguration forkedDebuggerSetup = ForkedDebuggerConfiguration.parse(jvmParametersSetup);
|
||||
if (forkedDebuggerSetup != null && isGradleScriptDebug(settings)) {
|
||||
effectiveSettings.withVmOption(forkedDebuggerSetup.getJvmAgentSetup(isJdk9orLater(effectiveSettings.getJavaHome())));
|
||||
if (forkedDebuggerSetup != null) {
|
||||
if (isGradleScriptDebug(settings)) {
|
||||
effectiveSettings.withVmOption(forkedDebuggerSetup.getJvmAgentSetup(isJdk9orLater(effectiveSettings.getJavaHome())));
|
||||
}
|
||||
effectiveSettings.putUserData(GradleRunConfiguration.DEBUGGER_DISPATCH_PORT_KEY, forkedDebuggerSetup.getForkSocketPort());
|
||||
}
|
||||
|
||||
CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource();
|
||||
@@ -180,8 +183,23 @@ public class GradleTaskManager extends BaseExternalSystemTaskManager<GradleExecu
|
||||
"//");
|
||||
}
|
||||
};
|
||||
boolean isTestExecution = Boolean.TRUE == effectiveSettings.getUserData(GradleConstants.RUN_TASK_AS_TEST);
|
||||
resolverExtension.enhanceTaskProcessing(taskNames, jvmParametersSetup, initScriptConsumer, isTestExecution);
|
||||
|
||||
Map<String, String> enhancementParameters = new HashMap<>();
|
||||
enhancementParameters.put(GradleProjectResolverExtension.JVM_PARAMETERS_SETUP_KEY, jvmParametersSetup);
|
||||
|
||||
String isTestExecution = String.valueOf(Boolean.TRUE == effectiveSettings.getUserData(GradleConstants.RUN_TASK_AS_TEST));
|
||||
enhancementParameters.put(GradleProjectResolverExtension.TEST_EXECUTION_EXPECTED_KEY, isTestExecution);
|
||||
|
||||
Integer debugDispatchPort = effectiveSettings.getUserData(GradleRunConfiguration.DEBUGGER_DISPATCH_PORT_KEY);
|
||||
|
||||
if (debugDispatchPort != null) {
|
||||
enhancementParameters.put(GradleProjectResolverExtension.DEBUG_DISPATCH_PORT_KEY, String.valueOf(debugDispatchPort));
|
||||
String debugOptions = effectiveSettings.getUserData(GradleRunConfiguration.DEBUGGER_PARAMETERS_KEY);
|
||||
enhancementParameters.put(GradleProjectResolverExtension.DEBUG_OPTIONS_KEY, debugOptions);
|
||||
}
|
||||
|
||||
|
||||
resolverExtension.enhanceTaskProcessing(taskNames, initScriptConsumer, enhancementParameters);
|
||||
}
|
||||
|
||||
if (!initScripts.isEmpty()) {
|
||||
|
||||
Reference in New Issue
Block a user