blob: 9933dd59f46a273c6fddcb515eca60d681b5e90d [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.process;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.KillableProcess;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.PathEnvironmentVariableUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.charset.Charset;
/**
* @author Elena Shaverdova
* @author Nikolay Matveev
*/
public final class ScriptRunnerUtil {
private static final Logger LOG = Logger.getInstance("com.intellij.execution.process.ScriptRunnerUtil");
public static final Condition<Key> STDOUT_OUTPUT_KEY_FILTER = new Condition<Key>() {
@Override
public boolean value(Key key) {
return ProcessOutputTypes.STDOUT.equals(key);
}
};
public static final Condition<Key> STDERR_OUTPUT_KEY_FILTER = new Condition<Key>() {
@Override
public boolean value(Key key) {
return ProcessOutputTypes.STDERR.equals(key);
}
};
public static final Condition<Key> STDOUT_OR_STDERR_OUTPUT_KEY_FILTER = Conditions.or(STDOUT_OUTPUT_KEY_FILTER, STDERR_OUTPUT_KEY_FILTER);
private static final int DEFAULT_TIMEOUT = 30000;
private ScriptRunnerUtil() {
}
public static String getProcessOutput(@NotNull GeneralCommandLine commandLine)
throws ExecutionException {
return getProcessOutput(commandLine, STDOUT_OUTPUT_KEY_FILTER, DEFAULT_TIMEOUT);
}
public static String getProcessOutput(@NotNull GeneralCommandLine commandLine, @NotNull Condition<Key> outputTypeFilter, long timeout)
throws ExecutionException {
return getProcessOutput(new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString()), outputTypeFilter,
timeout);
}
public static String getProcessOutput(@NotNull final ProcessHandler processHandler,
@NotNull final Condition<Key> outputTypeFilter,
final long timeout)
throws ExecutionException {
LOG.assertTrue(!processHandler.isStartNotified());
final StringBuilder outputBuilder = new StringBuilder();
processHandler.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
if (outputTypeFilter.value(outputType)) {
final String text = event.getText();
outputBuilder.append(text);
LOG.debug(text);
}
}
});
processHandler.startNotify();
if (!processHandler.waitFor(timeout)) {
throw new ExecutionException(ExecutionBundle.message("script.execution.timeout", String.valueOf(timeout / 1000)));
}
return outputBuilder.toString();
}
@NotNull
public static OSProcessHandler execute(@NotNull String exePath,
@Nullable String workingDirectory,
@Nullable VirtualFile scriptFile,
String[] parameters) throws ExecutionException {
return execute(exePath, workingDirectory, scriptFile, parameters, null);
}
@NotNull
public static OSProcessHandler execute(@NotNull String exePath,
@Nullable String workingDirectory,
@Nullable VirtualFile scriptFile,
String[] parameters,
@Nullable Charset charset) throws ExecutionException {
exePath = PathEnvironmentVariableUtil.findAbsolutePathOnMac(exePath);
return doExecute(exePath, workingDirectory, scriptFile, parameters, charset);
}
@NotNull
private static OSProcessHandler doExecute(@NotNull String exePath,
@Nullable String workingDirectory,
@Nullable VirtualFile scriptFile,
String[] parameters,
@Nullable Charset charset) throws ExecutionException {
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(exePath);
commandLine.setPassParentEnvironment(true);
if (scriptFile != null) {
commandLine.addParameter(scriptFile.getPresentableUrl());
}
commandLine.addParameters(parameters);
if (workingDirectory != null) {
commandLine.setWorkDirectory(workingDirectory);
}
LOG.debug("Command line: ", commandLine.getCommandLineString());
LOG.debug("Command line env: ", commandLine.getEnvironment());
if (charset == null) {
charset = ObjectUtils.notNull(EncodingManager.getInstance().getDefaultCharset(), CharsetToolkit.UTF8_CHARSET);
}
final OSProcessHandler processHandler = new ColoredProcessHandler(commandLine.createProcess(),
commandLine.getCommandLineString(),
charset);
if (LOG.isDebugEnabled()) {
processHandler.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
LOG.debug(outputType + ": " + event.getText());
}
});
}
return processHandler;
}
public static ScriptOutput executeScriptInConsoleWithFullOutput(String exePathString,
@Nullable VirtualFile scriptFile,
@Nullable String workingDirectory,
long timeout,
Condition<Key> scriptOutputType,
@NonNls String... parameters) throws ExecutionException {
final OSProcessHandler processHandler = execute(exePathString, workingDirectory, scriptFile, parameters);
ScriptOutput output = new ScriptOutput(scriptOutputType);
processHandler.addProcessListener(output);
processHandler.startNotify();
if (!processHandler.waitFor(timeout)) {
LOG.warn("Process did not complete in " + timeout / 1000 + "s");
throw new ExecutionException(ExecutionBundle.message("script.execution.timeout", String.valueOf(timeout / 1000)));
}
LOG.debug("script output: ", output.myFilteredOutput);
return output;
}
public static class ScriptOutput extends ProcessAdapter {
private final Condition<Key> myScriptOutputType;
public final StringBuilder myFilteredOutput;
public final StringBuffer myMergedOutput;
private ScriptOutput(Condition<Key> scriptOutputType) {
myScriptOutputType = scriptOutputType;
myFilteredOutput = new StringBuilder();
myMergedOutput = new StringBuffer();
}
public String getFilteredOutput() {
return myFilteredOutput.toString();
}
public String getMergedOutput() {
return myMergedOutput.toString();
}
public String[] getOutputToParseArray() {
return getFilteredOutput().split("\n");
}
public String getDescriptiveOutput() {
String outputToParse = getFilteredOutput();
return StringUtil.isEmpty(outputToParse) ? getMergedOutput() : outputToParse;
}
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
final String text = event.getText();
if (myScriptOutputType.value(outputType)) {
myFilteredOutput.append(text);
}
myMergedOutput.append(text);
}
}
/**
* Gracefully terminates a process handler.
* Initially, 'soft kill' is performed (on UNIX it's equivalent to SIGINT signal sending).
* If the process isn't terminated within a given timeout, 'force quite' is performed (on UNIX it's equivalent to SIGKILL
* signal sending).
*
* @param processHandler {@link ProcessHandler} instance
* @param millisTimeout timeout in milliseconds between 'soft kill' and 'force quite'
* @param commandLine command line
*/
public static void terminateProcessHandler(@NotNull ProcessHandler processHandler,
long millisTimeout,
@Nullable String commandLine) {
if (processHandler.isProcessTerminated()) {
if (commandLine == null && processHandler instanceof BaseOSProcessHandler) {
commandLine = ((BaseOSProcessHandler) processHandler).getCommandLine();
}
LOG.warn("Process '" + commandLine + "' is already terminated!");
return;
}
processHandler.destroyProcess();
if (processHandler instanceof KillableProcess) {
KillableProcess killableProcess = (KillableProcess) processHandler;
if (killableProcess.canKillProcess()) {
if (!processHandler.waitFor(millisTimeout)) {
// doing 'force quite'
killableProcess.killProcess();
}
}
}
}
}