blob: 0d5f4c3133e900c5b36ac4efd4694eab243de0a1 [file] [log] [blame]
/*
* Copyright 2000-2012 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.lang.ant.config.execution;
import com.intellij.concurrency.JobScheduler;
import com.intellij.execution.CantRunException;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.CommandLineBuilder;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.junit.JUnitProcessHandler;
import com.intellij.execution.junit2.segments.OutputPacketProcessor;
import com.intellij.execution.process.*;
import com.intellij.execution.testframework.Printable;
import com.intellij.execution.testframework.Printer;
import com.intellij.execution.util.ExecutionErrorDialog;
import com.intellij.history.LocalHistory;
import com.intellij.ide.macro.Macro;
import com.intellij.lang.ant.AntBundle;
import com.intellij.lang.ant.config.AntBuildFileBase;
import com.intellij.lang.ant.config.AntBuildListener;
import com.intellij.lang.ant.config.AntBuildTarget;
import com.intellij.lang.ant.config.impl.BuildFileProperty;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.util.concurrency.FutureResult;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.concurrent.TimeUnit;
public final class ExecutionHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.ant.execution.ExecutionHandler");
@NonNls public static final String PARSER_JAR = "xerces1.jar";
private ExecutionHandler() {
}
@Nullable
public static ProcessHandler executeRunConfiguration(AntRunConfiguration antRunConfiguration,
final DataContext dataContext,
List<BuildFileProperty> additionalProperties,
@NotNull final AntBuildListener antBuildListener) {
AntBuildTarget target = antRunConfiguration.getTarget();
if (target == null) return null;
FutureResult<ProcessHandler> result = runBuildImpl((AntBuildFileBase)target.getModel().getBuildFile(),
new String[]{target.getName()},
null,
dataContext,
additionalProperties, antBuildListener, false);
if (result != null) {
try {
return result.get();
}
catch (InterruptedException e) {
LOG.warn(e);
}
catch (java.util.concurrent.ExecutionException e) {
LOG.warn(e);
}
}
return null;
}
/**
* @param antBuildListener should not be null. Use {@link com.intellij.lang.ant.config.AntBuildListener#NULL}
*/
public static void runBuild(final AntBuildFileBase buildFile,
String[] targets,
@Nullable final AntBuildMessageView buildMessageViewToReuse,
final DataContext dataContext,
List<BuildFileProperty> additionalProperties, @NotNull final AntBuildListener antBuildListener) {
runBuildImpl(buildFile, targets, buildMessageViewToReuse, dataContext, additionalProperties, antBuildListener, true);
}
/**
* @param antBuildListener should not be null. Use {@link com.intellij.lang.ant.config.AntBuildListener#NULL}
*/
@Nullable
private static FutureResult<ProcessHandler> runBuildImpl(final AntBuildFileBase buildFile,
String[] targets,
@Nullable final AntBuildMessageView buildMessageViewToReuse,
final DataContext dataContext,
List<BuildFileProperty> additionalProperties,
@NotNull final AntBuildListener antBuildListener, final boolean waitFor) {
final AntBuildMessageView messageView;
final GeneralCommandLine commandLine;
final Project project = buildFile.getProject();
try {
FileDocumentManager.getInstance().saveAllDocuments();
final AntCommandLineBuilder builder = new AntCommandLineBuilder();
builder.setBuildFile(buildFile.getAllOptions(), VfsUtilCore.virtualToIoFile(buildFile.getVirtualFile()));
builder.calculateProperties(dataContext, additionalProperties);
builder.addTargets(targets);
builder.getCommandLine().setCharset(EncodingProjectManager.getInstance(buildFile.getProject()).getDefaultCharset());
messageView = prepareMessageView(buildMessageViewToReuse, buildFile, targets);
commandLine = CommandLineBuilder.createFromJavaParameters(builder.getCommandLine());
messageView.setBuildCommandLine(commandLine.getCommandLineString());
}
catch (RunCanceledException e) {
e.showMessage(project, AntBundle.message("run.ant.error.dialog.title"));
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
return null;
}
catch (CantRunException e) {
ExecutionErrorDialog.show(e, AntBundle.message("cant.run.ant.error.dialog.title"), project);
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
return null;
}
catch (Macro.ExecutionCancelledException e) {
antBuildListener.buildFinished(AntBuildListener.ABORTED, 0);
return null;
}
catch (Throwable e) {
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
LOG.error(e);
return null;
}
final FutureResult<ProcessHandler> future = new FutureResult<ProcessHandler>();
new Task.Backgroundable(buildFile.getProject(), AntBundle.message("ant.build.progress.dialog.title"), true) {
public boolean shouldStartInBackground() {
return true;
}
public void onCancel() {
antBuildListener.buildFinished(AntBuildListener.ABORTED, 0);
future.set(null);
}
public void run(@NotNull final ProgressIndicator indicator) {
try {
ProcessHandler handler = runBuild(indicator, messageView, buildFile, antBuildListener, commandLine);
future.set(handler);
if (waitFor && handler != null) {
handler.waitFor();
}
}
catch (Throwable e) {
LOG.error(e);
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
}
}
}.queue();
return future;
}
@Nullable
private static ProcessHandler runBuild(final ProgressIndicator progress,
@NotNull final AntBuildMessageView errorView,
@NotNull final AntBuildFileBase buildFile,
@NotNull final AntBuildListener antBuildListener,
@NotNull GeneralCommandLine commandLine) {
final Project project = buildFile.getProject();
final long startTime = System.currentTimeMillis();
LocalHistory.getInstance().putSystemLabel(project, AntBundle.message("ant.build.local.history.label", buildFile.getName()));
final JUnitProcessHandler handler;
try {
handler = JUnitProcessHandler.runCommandLine(commandLine);
}
catch (final ExecutionException e) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ExecutionErrorDialog.show(e, AntBundle.message("could.not.start.process.error.dialog.title"), project);
}
});
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
return null;
}
processRunningAnt(progress, handler, errorView, buildFile, startTime, antBuildListener);
return handler;
}
private static void processRunningAnt(final ProgressIndicator progress,
final JUnitProcessHandler handler,
final AntBuildMessageView errorView,
final AntBuildFileBase buildFile,
final long startTime,
final AntBuildListener antBuildListener) {
final Project project = buildFile.getProject();
final StatusBar statusbar = WindowManager.getInstance().getStatusBar(project);
if (statusbar != null) {
statusbar.setInfo(AntBundle.message("ant.build.started.status.message"));
}
final CheckCancelTask checkCancelTask = new CheckCancelTask(progress, handler);
checkCancelTask.start(0);
final OutputParser parser = OutputParser2.attachParser(project, handler, errorView, progress, buildFile);
handler.addProcessListener(new ProcessAdapter() {
private final StringBuilder myUnprocessedStdErr = new StringBuilder();
public void onTextAvailable(ProcessEvent event, Key outputType) {
if (outputType == ProcessOutputTypes.STDERR) {
final String text = event.getText();
synchronized (myUnprocessedStdErr) {
myUnprocessedStdErr.append(text);
}
}
}
public void processTerminated(ProcessEvent event) {
final long buildTime = System.currentTimeMillis() - startTime;
checkCancelTask.cancel();
parser.setStopped(true);
final OutputPacketProcessor dispatcher = handler.getErr().getEventsDispatcher();
try {
if (event.getExitCode() != 0) {
// in case process exits abnormally, provide all unprocessed stderr content
final String unprocessed;
synchronized (myUnprocessedStdErr) {
unprocessed = myUnprocessedStdErr.toString();
myUnprocessedStdErr.setLength(0);
}
if (!unprocessed.isEmpty()) {
dispatcher.processOutput(new Printable() {
public void printOn(Printer printer) {
errorView.outputError(unprocessed, AntBuildMessageView.PRIORITY_ERR);
}
});
}
}
else {
synchronized (myUnprocessedStdErr) {
myUnprocessedStdErr.setLength(0);
}
}
}
finally {
errorView.buildFinished(progress != null && progress.isCanceled(), buildTime, antBuildListener, dispatcher);
}
}
});
handler.startNotify();
}
static final class CheckCancelTask implements Runnable {
private final ProgressIndicator myProgressIndicator;
private final OSProcessHandler myProcessHandler;
private volatile boolean myCanceled;
public CheckCancelTask(ProgressIndicator progressIndicator, OSProcessHandler process) {
myProgressIndicator = progressIndicator;
myProcessHandler = process;
}
public void cancel() {
myCanceled = true;
}
public void run() {
if (!myCanceled) {
try {
myProgressIndicator.checkCanceled();
start(50);
}
catch (ProcessCanceledException e) {
myProcessHandler.destroyProcess();
}
}
}
public void start(final long delay) {
JobScheduler.getScheduler().schedule(this, delay, TimeUnit.MILLISECONDS);
}
}
private static AntBuildMessageView prepareMessageView(@Nullable AntBuildMessageView buildMessageViewToReuse,
AntBuildFileBase buildFile,
String[] targets) throws RunCanceledException {
AntBuildMessageView messageView;
if (buildMessageViewToReuse != null) {
messageView = buildMessageViewToReuse;
messageView.emptyAll();
}
else {
messageView = AntBuildMessageView.openBuildMessageView(buildFile.getProject(), buildFile, targets);
if (messageView == null) {
throw new RunCanceledException(AntBundle.message("canceled.by.user.error.message"));
}
}
return messageView;
}
}