// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md). // Licensed under the Apache License, Version 2.0 (see LICENSE). package com.twitter.intellij.pants.service.task; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.execution.process.UnixProcessManager; import com.intellij.openapi.externalSystem.model.ExternalSystemException; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener; import com.intellij.openapi.externalSystem.task.AbstractExternalSystemTaskManager; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.util.containers.ContainerUtil; import com.twitter.intellij.pants.PantsBundle; import com.twitter.intellij.pants.metrics.PantsExternalMetricsListener; import com.twitter.intellij.pants.metrics.PantsExternalMetricsListenerManager; import com.twitter.intellij.pants.model.PantsTargetAddress; import com.twitter.intellij.pants.settings.PantsExecutionSettings; import com.twitter.intellij.pants.util.PantsConstants; import com.twitter.intellij.pants.util.PantsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; public class PantsTaskManager extends AbstractExternalSystemTaskManager<PantsExecutionSettings> { public static final Map<String, String> goal2JvmOptionsFlag = ContainerUtil.newHashMap( Pair.create("test", "--jvm-test-junit-options"), Pair.create("run", "--jvm-run-jvm-options") ); private final Map<ExternalSystemTaskId, Process> myCancellationMap = ContainerUtil.newConcurrentMap(); @Override public void executeTasks( @NotNull final ExternalSystemTaskId id, @NotNull List<String> taskNames, @NotNull String projectPath, @Nullable PantsExecutionSettings settings, @NotNull List<String> vmOptions, @NotNull List<String> scriptParameters, @Nullable String debuggerSetup, @NotNull final ExternalSystemTaskNotificationListener listener ) throws ExternalSystemException { PantsExternalMetricsListenerManager.getInstance().logTestRunner(PantsExternalMetricsListener.TestRunnerType.PANTS_RUNNER); final GeneralCommandLine commandLine = constructCommandLine(taskNames, projectPath, settings, vmOptions, scriptParameters, debuggerSetup); if (commandLine == null) return; listener.onTaskOutput(id, commandLine.getCommandLineString(PantsConstants.PANTS), true); try { final Process process = commandLine.createProcess(); myCancellationMap.put(id, process); PantsUtil.getOutput( process, new ProcessAdapter() { @Override public void startNotified(ProcessEvent event) { super.startNotified(event); listener.onStart(id); } @Override public void onTextAvailable(ProcessEvent event, Key outputType) { super.onTextAvailable(event, outputType); listener.onTaskOutput(id, event.getText(), outputType == ProcessOutputTypes.STDOUT); } } ); } catch (ExecutionException e) { throw new ExternalSystemException(e); } finally { myCancellationMap.remove(id); // Sync files as generated sources may have changed after `pants test` called PantsUtil.synchronizeFiles(); } } @Nullable public GeneralCommandLine constructCommandLine( @NotNull List<String> taskNames, @NotNull String projectPath, @Nullable PantsExecutionSettings settings, @NotNull List<String> vmOptions, @NotNull List<String> scriptParameters, @Nullable String debuggerSetup ) { if (settings == null) { return null; } projectPath = PantsTargetAddress.extractPath(projectPath).get(); final GeneralCommandLine commandLine = PantsUtil.defaultCommandLine(projectPath); /** * Global options section. */ if (debuggerSetup != null) { if (taskNames.size() > 1) { throw new ExternalSystemException(PantsBundle.message("pants.error.multiple.tasks.for.debugging")); } commandLine.addParameter(PantsConstants.PANTS_CLI_OPTION_NO_TEST_JUNIT_TIMEOUTS); final String goal = taskNames.iterator().next(); final String jvmOptionsFlag = goal2JvmOptionsFlag.get(goal); if (jvmOptionsFlag == null) { throw new ExternalSystemException(PantsBundle.message("pants.error.cannot.debug.task", goal)); } commandLine.addParameter(jvmOptionsFlag + "=" + debuggerSetup); } if (settings.isUseIdeaProjectJdk()) { try{ commandLine.addParameter(PantsUtil.getJvmDistributionPathParameter(PantsUtil.getJdkPathFromIntelliJCore())); } catch(Exception e){ throw new ExternalSystemException(e); } } /** * Goals. */ commandLine.addParameters(taskNames); // Appending VM options. for (String goal : taskNames) { final String jvmOptionsFlag = goal2JvmOptionsFlag.get(goal); if (jvmOptionsFlag == null) { continue; } for (String vmOption : vmOptions) { commandLine.addParameter(jvmOptionsFlag + "=" + vmOption); } } /** * Script parameters section including targets and options. */ commandLine.addParameters(scriptParameters); return commandLine; } @Override public boolean cancelTask(@NotNull ExternalSystemTaskId id, @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException { final Process process = myCancellationMap.get(id); UnixProcessManager.sendSignalToProcessTree(process, UnixProcessManager.SIGTERM); return true; } }