| /******************************************************************************* |
| * Copyright (c) 2005, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.cdt.core; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.Properties; |
| |
| import org.eclipse.cdt.internal.core.Cygwin; |
| import org.eclipse.cdt.internal.core.Messages; |
| import org.eclipse.cdt.internal.core.ProcessClosure; |
| import org.eclipse.cdt.utils.PathUtil; |
| import org.eclipse.cdt.utils.spawner.EnvironmentReader; |
| import org.eclipse.cdt.utils.spawner.ProcessFactory; |
| import org.eclipse.core.resources.IProject; |
| 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.Platform; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class CommandLauncher implements ICommandLauncher { |
| public final static int COMMAND_CANCELED = ICommandLauncher.COMMAND_CANCELED; |
| public final static int ILLEGAL_COMMAND = ICommandLauncher.ILLEGAL_COMMAND; |
| public final static int OK = ICommandLauncher.OK; |
| |
| private static final String PATH_ENV = "PATH"; //$NON-NLS-1$ |
| private static final String NEWLINE = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| protected Process fProcess; |
| protected boolean fShowCommand; |
| protected String[] fCommandArgs; |
| private Properties fEnvironment = null; |
| |
| protected String fErrorMessage = ""; //$NON-NLS-1$ |
| private IProject fProject; |
| |
| /** |
| * The number of milliseconds to pause between polling. |
| */ |
| protected static final long DELAY = 50L; |
| |
| /** |
| * Creates a new launcher Fills in stderr and stdout output to the given |
| * streams. Streams can be set to <code>null</code>, if output not |
| * required |
| */ |
| public CommandLauncher() { |
| fProcess = null; |
| fShowCommand = false; |
| } |
| |
| @Override |
| public void showCommand(boolean show) { |
| fShowCommand = show; |
| } |
| |
| @Override |
| public String getErrorMessage() { |
| return fErrorMessage; |
| } |
| |
| @Override |
| public void setErrorMessage(String error) { |
| fErrorMessage = error; |
| } |
| |
| @Override |
| public String[] getCommandArgs() { |
| return fCommandArgs; |
| } |
| |
| @Override |
| public Properties getEnvironment() { |
| if (fEnvironment == null) { |
| // for backward compatibility, note that this return may be not accurate |
| return EnvironmentReader.getEnvVars(); |
| } |
| return fEnvironment; |
| } |
| |
| /** |
| * Returns a property from the given environment. |
| * Asks the Environment reader directly for its key instead of retrieving this entire property map |
| */ |
| private String getEnvironmentProperty(String key) { |
| if (fEnvironment == null) { |
| return EnvironmentReader.getEnvVar(key); |
| } |
| return fEnvironment.getProperty(key); |
| } |
| |
| @Override |
| public String getCommandLine() { |
| return getCommandLine(getCommandArgs()); |
| } |
| |
| /** |
| * Constructs a command array that will be passed to the process |
| */ |
| protected String[] constructCommandArray(String command, String[] commandArgs) { |
| String[] args = new String[1 + commandArgs.length]; |
| args[0] = command; |
| System.arraycopy(commandArgs, 0, args, 1, commandArgs.length); |
| return args; |
| } |
| |
| /** |
| * Parse array of "ENV=value" pairs to Properties. |
| */ |
| private void parseEnvironment(String[] env) { |
| fEnvironment = null; |
| if (env != null) { |
| fEnvironment = new Properties(); |
| for (String envStr : env) { |
| // Split "ENV=value" and put in Properties |
| int pos = envStr.indexOf('='); |
| if (pos < 0) |
| pos = envStr.length(); |
| String key = envStr.substring(0, pos); |
| String value = envStr.substring(pos + 1); |
| fEnvironment.put(key, value); |
| } |
| } |
| } |
| |
| /** |
| * @deprecated |
| * @since 5.1 |
| */ |
| @Deprecated |
| public Process execute(IPath commandPath, String[] args, String[] env, IPath changeToDirectory) { |
| try { |
| return execute(commandPath, args, env, changeToDirectory, null); |
| } catch (CoreException e) { |
| CCorePlugin.log(e); |
| } |
| return null; |
| } |
| |
| /** |
| * @since 5.1 |
| * @see org.eclipse.cdt.core.ICommandLauncher#execute(IPath, String[], String[], IPath, IProgressMonitor) |
| */ |
| @Override |
| public Process execute(IPath commandPath, String[] args, String[] env, IPath workingDirectory, |
| IProgressMonitor monitor) throws CoreException { |
| parseEnvironment(env); |
| String envPathValue = getEnvironmentProperty(PATH_ENV); |
| |
| Boolean isFound = null; |
| String command = commandPath.toOSString(); |
| fCommandArgs = constructCommandArray(command, args); |
| if (Platform.getOS().equals(Platform.OS_WIN32)) { |
| // Handle cygwin link |
| IPath location = PathUtil.findProgramLocation(command, envPathValue); |
| isFound = location != null; |
| if (location != null) { |
| try { |
| fCommandArgs[0] = Cygwin.cygwinToWindowsPath(location.toString(), envPathValue); |
| } catch (Exception e) { |
| // if no cygwin nothing to worry about |
| } |
| } |
| } |
| |
| File dir = workingDirectory != null ? workingDirectory.toFile() : null; |
| if (dir != null && !dir.isDirectory()) { |
| CCorePlugin.logStackTrace(IStatus.ERROR, NLS.bind(Messages.CommandLauncher_InvalidWorkingDirectory, dir)); |
| dir = null; |
| } |
| |
| try { |
| fProcess = ProcessFactory.getFactory().exec(fCommandArgs, env, dir); |
| fCommandArgs[0] = command; // to print original command on the console |
| fErrorMessage = ""; //$NON-NLS-1$ |
| } catch (IOException e) { |
| CCorePlugin.log(e); |
| |
| if (isFound == null) { |
| IPath location = PathUtil.findProgramLocation(command, envPathValue); |
| isFound = location != null; |
| } |
| |
| String errorMessage = getCommandLineQuoted(fCommandArgs, true); |
| String exMsg = e.getMessage(); |
| if (exMsg != null && !exMsg.isEmpty()) { |
| errorMessage = errorMessage + exMsg + NEWLINE; |
| } |
| |
| if (!isFound) { |
| if (envPathValue == null) { |
| envPathValue = System.getenv(PATH_ENV); |
| } |
| errorMessage = errorMessage + NEWLINE |
| + NLS.bind(Messages.CommandLauncher_ProgramNotFoundInPath, command) + NEWLINE + "PATH=[" //$NON-NLS-1$ |
| + envPathValue + "]" + NEWLINE; //$NON-NLS-1$ |
| } |
| setErrorMessage(errorMessage); |
| fProcess = null; |
| } |
| return fProcess; |
| } |
| |
| @Override |
| @Deprecated |
| public int waitAndRead(OutputStream out, OutputStream err) { |
| if (fShowCommand) { |
| printCommandLine(out); |
| } |
| |
| if (fProcess == null) { |
| return ILLEGAL_COMMAND; |
| } |
| |
| ProcessClosure closure = new ProcessClosure(fProcess, out, err); |
| closure.runBlocking(); // a blocking call |
| return OK; |
| } |
| |
| @Override |
| public int waitAndRead(OutputStream output, OutputStream err, IProgressMonitor monitor) { |
| if (fShowCommand) { |
| printCommandLine(output); |
| } |
| |
| if (fProcess == null) { |
| return ILLEGAL_COMMAND; |
| } |
| |
| ProcessClosure closure = new ProcessClosure(fProcess, output, err); |
| closure.runNonBlocking(); |
| while (!monitor.isCanceled() && closure.isAlive()) { |
| try { |
| Thread.sleep(DELAY); |
| } catch (InterruptedException ie) { |
| // ignore |
| } |
| } |
| |
| int state = OK; |
| |
| // Operation canceled by the user, terminate abnormally. |
| if (monitor.isCanceled()) { |
| closure.terminate(); |
| state = COMMAND_CANCELED; |
| setErrorMessage(Messages.CommandLauncher_CommandCancelled); |
| } |
| |
| try { |
| fProcess.waitFor(); |
| } catch (InterruptedException e) { |
| // ignore |
| } |
| return state; |
| } |
| |
| protected void printCommandLine(OutputStream os) { |
| if (os != null) { |
| try { |
| os.write(getCommandLineQuoted(getCommandArgs(), true).getBytes()); |
| os.flush(); |
| } catch (IOException e) { |
| // ignore; |
| } |
| } |
| } |
| |
| @SuppressWarnings("nls") |
| private String getCommandLineQuoted(String[] commandArgs, boolean quote) { |
| StringBuilder buf = new StringBuilder(); |
| if (commandArgs != null) { |
| for (String commandArg : commandArgs) { |
| if (quote && (commandArg.contains(" ") || commandArg.contains("\"") || commandArg.contains("\\"))) { |
| commandArg = '"' + commandArg.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\"") + '"'; |
| } |
| buf.append(commandArg); |
| buf.append(' '); |
| } |
| buf.append(NEWLINE); |
| } |
| return buf.toString(); |
| } |
| |
| protected String getCommandLine(String[] commandArgs) { |
| return getCommandLineQuoted(commandArgs, false); |
| } |
| |
| /** |
| * @since 5.1 |
| * @see org.eclipse.cdt.core.ICommandLauncher#getProject() |
| */ |
| @Override |
| public IProject getProject() { |
| return fProject; |
| } |
| |
| /** |
| * @since 5.1 |
| * @see org.eclipse.cdt.core.ICommandLauncher#setProject(org.eclipse.core.resources.IProject) |
| */ |
| @Override |
| public void setProject(IProject project) { |
| fProject = project; |
| } |
| |
| } |