/*******************************************************************************
* Copyright (c) 2006, 2007, 2009 Red Hat Inc..
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat Incorporated - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.autotools.ui.actions;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.eclipse.cdt.autotools.ui.AutotoolsUIPlugin;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ConsoleOutputStream;
import org.eclipse.cdt.core.ICommandLauncher;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.internal.autotools.core.AutotoolsNewMakeGenerator;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.remote.core.RemoteCommandLauncher;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Shell;
public abstract class InvokeAction extends AbstractTargetAction {
public final String SHELL_COMMAND = "sh"; //$NON-NLS-1$
protected void showInformation(String title, String content) {
MessageDialog.openInformation(new Shell(), title, content);
}
protected void showError(String title, String content) {
MessageDialog.openError(new Shell(), title, content);
}
protected void showSuccess(String title) {
MessageDialog.openInformation(new Shell(), title,
InvokeMessages.getString("InvokeAction.success")); //$NON-NLS-1$
}
protected String showInput(String title, String content, String defaultTxt) {
InputDialog getOptionDialog = new InputDialog(new Shell(), title,
content, defaultTxt, null);
getOptionDialog.open();
return getOptionDialog.getValue();
}
/**
* Separate targets to array from a string.
*
* @param rawArgList
* @return targets in string[] array. if targets are not formatted properly,
* returns null
*/
protected String[] separateTargets(String rawArgList) {
StringTokenizer st = new StringTokenizer(rawArgList, " "); //$NON-NLS-1$
ArrayList<String> targetList = new ArrayList<String>();
while (st.hasMoreTokens()) {
String currentWord = st.nextToken().trim();
if (currentWord.startsWith("'")) { //$NON-NLS-1$
StringBuffer tmpTarget = new StringBuffer();
while (!currentWord.endsWith("'")) { //$NON-NLS-1$
tmpTarget.append(currentWord + " "); //$NON-NLS-1$
if (!st.hasMoreTokens()) {
// quote not closed properly, so return null
return null;
}
currentWord = st.nextToken().trim();
}
tmpTarget.append(currentWord);
targetList.add(tmpTarget.toString());
continue;
}
if (currentWord.startsWith("\"")) { //$NON-NLS-1$
StringBuffer tmpTarget = new StringBuffer();
while (!currentWord.endsWith("\"")) { //$NON-NLS-1$
tmpTarget.append(currentWord + " "); //$NON-NLS-1$
if (!st.hasMoreTokens()) {
// double quote not closed properly, so return null
return null;
}
currentWord = st.nextToken().trim();
}
tmpTarget.append(currentWord);
targetList.add(tmpTarget.toString());
continue;
}
// for targets without quote/double quotes.
targetList.add(currentWord);
}
return targetList.toArray(new String[targetList.size()]);
}
protected String[] separateOptions(String rawArgList) {
ArrayList<String> argList = new ArrayList<String>();
// May be multiple user-specified options in which case we
// need to split them up into individual options
rawArgList = rawArgList.trim();
boolean finished = false;
int lastIndex = rawArgList.indexOf("--"); //$NON-NLS-1$
if (lastIndex != -1) {
while (!finished) {
int index = rawArgList.indexOf("--", lastIndex + 2); //$NON-NLS-1$
if (index != -1) {
String previous = rawArgList.substring(lastIndex, index)
.trim();
argList.add(previous);
rawArgList = rawArgList.substring(index);
} else {
argList.add(rawArgList);
finished = true;
}
}
}
return argList.toArray(new String[argList.size()]);
}
protected String[] simpleParseOptions(String rawArgList) {
ArrayList<String> argList = new ArrayList<String>();
int lastArgIndex = -1;
int i = 0;
while (i < rawArgList.length()) {
char ch = rawArgList.charAt(i);
// Skip white-space
while (Character.isWhitespace(ch)) {
++i;
if (i < rawArgList.length())
ch = rawArgList.charAt(i);
else // Otherwise we are done
return argList.toArray(new String[argList.size()]);
}
// Simplistic parser. We break up into strings delimited
// by blanks. If quotes are used, we ignore blanks within.
// If a backslash is used, we ignore the next character and
// pass it through.
lastArgIndex = i;
boolean inString = false;
while (i < rawArgList.length()) {
ch = rawArgList.charAt(i);
if (ch == '\\') // escape character
++i; // skip over the next character
else if (ch == '\"') { // double quotes
inString = !inString;
} else if (Character.isWhitespace(ch)) {
if (!inString) {
argList.add(rawArgList.substring(lastArgIndex, i));
break;
}
}
++i;
}
// Look for the case where we ran out of chars for the last
// token.
if (i >= rawArgList.length())
argList.add(rawArgList.substring(lastArgIndex));
++i;
}
return argList.toArray(new String[argList.size()]);
}
protected IPath getExecDir(IContainer container) {
int type = container.getType();
IPath execDir = null;
if (type == IContainer.FILE) {
execDir = container.getLocation().removeLastSegments(1);
} else {
execDir = container.getLocation();
}
return execDir;
}
protected IPath getCWD(IContainer container) {
int type = container.getType();
IPath cwd = null;
if (type == IContainer.FILE) {
cwd = container.getFullPath().removeLastSegments(1);
} else {
cwd = container.getFullPath();
}
return cwd;
}
private static class ExecuteProgressDialog implements IRunnableWithProgress {
private final IPath command;
private final String[] argumentList;
private final String[] envList;
private final IPath execDir;
private int status;
private HashMap<String, String> outputs = null;
public ExecuteProgressDialog(IPath command, String[] argumentList,
String[] envList, IPath execDir) {
this.command = command;
this.argumentList = argumentList;
this.envList = envList;
this.execDir = execDir;
}
@Override
public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
RemoteCommandLauncher cmdL = new RemoteCommandLauncher();
outputs = null;
// invoke command
try {
monitor.beginTask(
InvokeMessages.getFormattedString("InvokeAction.progress.message", // $NON-NLS-1$
new String[]{command.toOSString()}), IProgressMonitor.UNKNOWN);
monitor.worked(1);
Process process = cmdL.execute(command, argumentList, envList,
execDir, new NullProgressMonitor());
if (cmdL.waitAndRead(stdout, stderr, new NullProgressMonitor()) == ICommandLauncher.OK) {
try {
status = 0;
monitor.done();
process.getOutputStream().close();
} catch (IOException e) {
// ignore
}
} else {
// failed to execute command
status = -1;
monitor.done();
return;
}
} catch (CoreException e) {
monitor.done();
throw new InvocationTargetException(e);
}
outputs = new HashMap<String, String>();
outputs.put("stdout", stdout.toString()); //$NON-NLS-1$
outputs.put("stderr", stderr.toString()); //$NON-NLS-1$
try {
stdout.close();
stderr.close();
} catch (IOException e) {
// ignore
}
}
public HashMap<String, String> getOutputs() {
return outputs;
}
public int getStatus() {
return status;
}
}
protected HashMap<String, String> executeCommand(IPath command,
String[] argumentList, String[] envList, IPath execDir) {
try {
ExecuteProgressDialog d = new ExecuteProgressDialog(command,
argumentList, envList, execDir);
new ProgressMonitorDialog(new Shell()).run(false, false, d);
if (d.getStatus() == -1)
showError(InvokeMessages
.getString("InvokeAction.execute.windowTitle.error"), InvokeMessages //$NON-NLS-1$
.getString("InvokeAction.execute.message") //$NON-NLS-1$
+ command.toOSString()); //$NON-NLS-1$
return d.getOutputs();
} catch (InvocationTargetException e) {
showError(InvokeMessages
.getString("InvokeAction.execute.windowTitle.error"), InvokeMessages //$NON-NLS-1$
.getString("InvokeAction.execute.message") //$NON-NLS-1$
+ command.toOSString()); //$NON-NLS-1$
AutotoolsUIPlugin.logException(e);
return null;
} catch (InterruptedException e) {
return null;
}
}
protected void executeConsoleCommand(final String actionName, final String command,
final String[] argumentList, final IPath execDir) {
// We need to use a workspace root scheduling rule because adding MakeTargets
// may end up saving the project description which runs under a workspace root rule.
final ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRoot();
Job backgroundJob = new Job(actionName) {
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor monitor) throws CoreException {
try {
String errMsg = null;
IProject project = getSelectedContainer().getProject();
// Get a build console for the project
IConsole console = CCorePlugin.getDefault().getConsole("org.eclipse.cdt.autotools.ui.autotoolsConsole"); //$NON-NLS-1$
console.start(project);
CUIPlugin.getDefault().startGlobalConsole();
ConsoleOutputStream consoleOutStream = console.getOutputStream();
// FIXME: we want to remove need for ManagedBuilderManager, but how do we
// get environment variables.
IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(project);
IConfiguration cfg = info.getDefaultConfiguration();
StringBuffer buf = new StringBuffer();
String[] consoleHeader = new String[3];
consoleHeader[0] = actionName;
consoleHeader[1] = cfg.getName();
consoleHeader[2] = project.getName();
buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$
String invokeMsg = InvokeMessages.getFormattedString("InvokeAction.console.message", //$NON-NLS-1$
new String[]{actionName, execDir.toString()}); //$NON-NLS-1$
buf.append(invokeMsg);
buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$
buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$
consoleOutStream.write(buf.toString().getBytes());
consoleOutStream.flush();
ArrayList<String> additionalEnvs = new ArrayList<String>();
String strippedCommand = AutotoolsNewMakeGenerator.stripEnvVars(command, additionalEnvs);
// Get a launcher for the config command
RemoteCommandLauncher launcher = new RemoteCommandLauncher();
launcher.setProject(project);
// Set the environment
IEnvironmentVariable variables[] = ManagedBuildManager
.getEnvironmentVariableProvider().getVariables(cfg, true);
String[] env = null;
ArrayList<String> envList = new ArrayList<String>();
if (variables != null) {
for (int i = 0; i < variables.length; i++) {
envList.add(variables[i].getName()
+ "=" + variables[i].getValue()); //$NON-NLS-1$
}
if (additionalEnvs.size() > 0)
envList.addAll(additionalEnvs); // add any additional environment variables specified ahead of script
env = envList.toArray(new String[envList.size()]);
}
String[] newArgumentList;
// Fix for bug #343905 and bug #371277
// For Windows and Mac, we cannot run a script directly (in this case,
// autotools are scripts). We need to run "sh -c command args where command
// plus args is represented in a single string. The same applies for
// some Linux shells such as dash. Using sh -c will work on all Linux
// POSIX-compliant shells.
StringBuffer command = new StringBuffer(strippedCommand);
for (String arg : argumentList) {
command.append(" " + arg);
}
newArgumentList = new String[] { "-c", command.toString() };
OutputStream stdout = consoleOutStream;
OutputStream stderr = consoleOutStream;
launcher.showCommand(true);
// Run the shell script via shell command.
Process proc = launcher.execute(new Path(SHELL_COMMAND), newArgumentList, env,
execDir, new NullProgressMonitor());
if (proc != null) {
try {
// Close the input of the process since we will never write to
// it
proc.getOutputStream().close();
} catch (IOException e) {
}
if (launcher.waitAndRead(stdout, stderr, new SubProgressMonitor(
monitor, IProgressMonitor.UNKNOWN)) != ICommandLauncher.OK) {
errMsg = launcher.getErrorMessage();
}
// Force a resync of the projects without allowing the user to
// cancel.
// This is probably unkind, but short of this there is no way to
// ensure
// the UI is up-to-date with the build results
// monitor.subTask(ManagedMakeMessages
// .getResourceString(REFRESH));
monitor.subTask(AutotoolsUIPlugin.getResourceString("MakeGenerator.refresh")); //$NON-NLS-1$
try {
project.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
monitor.subTask(AutotoolsUIPlugin
.getResourceString("MakeGenerator.refresh.error")); //$NON-NLS-1$
}
} else {
errMsg = launcher.getErrorMessage();
}
if (errMsg != null)
AutotoolsUIPlugin.logErrorMessage(errMsg);
} catch (IOException e) {
AutotoolsUIPlugin.log(e);
}
}
}, rule, IWorkspace.AVOID_UPDATE, monitor);
} catch (CoreException e) {
return e.getStatus();
}
IStatus returnStatus = Status.OK_STATUS;
return returnStatus;
}
};
backgroundJob.setRule(rule);
backgroundJob.schedule();
}
}