/******************************************************************************* * Copyright (c) 2009, 2016 Red Hat Inc. and others. * 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.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.List; 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.model.ICContainer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; 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.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; 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.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.handlers.HandlerUtil; public abstract class AbstractAutotoolsHandler extends AbstractHandler { private IContainer fContainer; protected abstract void run(Shell activeShell); protected Object execute1(ExecutionEvent event) { ISelection k = HandlerUtil.getCurrentSelection(event); if (!k.isEmpty() && k instanceof IStructuredSelection) { Object obj = ((IStructuredSelection)k).getFirstElement(); IContainer container = getContainer(obj); if (container != null) { setSelectedContainer(container); run(HandlerUtil.getActiveShell(event)); } } return null; } @SuppressWarnings("unchecked") protected IContainer getContainer(Object obj) { IContainer fContainer = null; if (obj instanceof Collection) { Collection<Object> c = (Collection<Object>)obj; Object[] objArray = c.toArray(); if (objArray.length > 0) obj = objArray[0]; } if (obj instanceof ICElement) { if ( obj instanceof ICContainer || obj instanceof ICProject) { fContainer = (IContainer) ((ICElement) obj).getUnderlyingResource(); } else { obj = ((ICElement)obj).getResource(); if ( obj != null) { fContainer = ((IResource)obj).getParent(); } } } else if (obj instanceof IResource) { if (obj instanceof IContainer) { fContainer = (IContainer) obj; } else { fContainer = ((IResource)obj).getParent(); } } else { fContainer = null; } return fContainer; } public final String SHELL_COMMAND = "sh"; //$NON-NLS-1$ protected void showError(String title, String content) { MessageDialog.openError(new Shell(), title, content); } /** * Separate targets to array from a string. * * @param rawArgList * @return targets in string[] array. if targets are not formatted properly, * returns null */ protected List<String> separateTargets(String rawArgList) { StringTokenizer st = new StringTokenizer(rawArgList, " "); //$NON-NLS-1$ List<String> targetList = new ArrayList<>(); while (st.hasMoreTokens()) { String currentWord = st.nextToken().trim(); if (currentWord.startsWith("'")) { //$NON-NLS-1$ StringBuilder tmpTarget = new StringBuilder(); while (!currentWord.endsWith("'")) { //$NON-NLS-1$ tmpTarget.append(currentWord).append(' '); 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$ StringBuilder tmpTarget = new StringBuilder(); while (!currentWord.endsWith("\"")) { //$NON-NLS-1$ tmpTarget.append(currentWord).append(' '); 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; } protected List<String> separateOptions(String rawArgList) { List<String> argList = new ArrayList<>(); // 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; } protected List<String> simpleParseOptions(String rawArgList) { List<String> argList = new ArrayList<>(); 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; } // 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; } protected IPath getExecDir(IContainer container) { int type = container.getType(); IPath execDir = null; if (type == IResource.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 == IResource.FILE) { cwd = container.getFullPath().removeLastSegments(1); } else { cwd = container.getFullPath(); } return cwd; } protected void executeConsoleCommand(final String actionName, final String command, final List<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 = Job.create(actionName, monitor -> { try { ResourcesPlugin.getWorkspace().run((IWorkspaceRunnable) monitor1 -> { 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(); StringBuilder buf = new StringBuilder(); 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 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<>(); 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. StringBuilder command1 = new StringBuilder(strippedCommand); for (String arg : argumentList) { command1.append(' ').append(arg); } newArgumentList = new String[] { "-c", command1.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 e1) { } if (launcher.waitAndRead(stdout, stderr, SubMonitor.convert(monitor1)) != 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)); monitor1.subTask(AutotoolsUIPlugin.getResourceString("MakeGenerator.refresh")); //$NON-NLS-1$ try { project.refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e2) { monitor1.subTask(AutotoolsUIPlugin.getResourceString("MakeGenerator.refresh.error")); //$NON-NLS-1$ } } else { errMsg = launcher.getErrorMessage(); } if (errMsg != null) AutotoolsUIPlugin.logErrorMessage(errMsg); } catch (IOException e3) { AutotoolsUIPlugin.log(e3); } }, rule, IWorkspace.AVOID_UPDATE, monitor); } catch (CoreException e) { return e.getStatus(); } return Status.OK_STATUS; }); backgroundJob.setRule(rule); backgroundJob.schedule(); } protected IContainer getSelectedContainer() { return fContainer; } public void setSelectedContainer(IContainer container) { fContainer = container; } }