| /******************************************************************************* |
| * Copyright (c) 2009, 2010 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ptp.rdt.sync.core.make; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.ICommandLauncher; |
| import org.eclipse.cdt.managedbuilder.core.IBuilder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| 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.Status; |
| import org.eclipse.ptp.internal.rdt.sync.core.make.RemoteProcessClosure; |
| import org.eclipse.ptp.rdt.sync.core.serviceproviders.IRemoteExecutionServiceProvider; |
| import org.eclipse.ptp.rdt.sync.core.services.IRemoteSyncServiceConstants; |
| import org.eclipse.ptp.remote.core.IRemoteConnection; |
| import org.eclipse.ptp.remote.core.IRemoteFileManager; |
| import org.eclipse.ptp.remote.core.IRemoteProcess; |
| import org.eclipse.ptp.remote.core.IRemoteProcessBuilder; |
| import org.eclipse.ptp.remote.core.IRemoteServices; |
| import org.eclipse.ptp.remote.core.RemoteProcessAdapter; |
| import org.eclipse.ptp.remote.core.exception.RemoteConnectionException; |
| import org.eclipse.ptp.services.core.IService; |
| import org.eclipse.ptp.services.core.IServiceConfiguration; |
| import org.eclipse.ptp.services.core.IServiceProvider; |
| import org.eclipse.ptp.services.core.ProjectNotConfiguredException; |
| import org.eclipse.ptp.services.core.ServiceModelManager; |
| |
| /** |
| * <strong>EXPERIMENTAL</strong>. This class or interface has been added as |
| * part of a work in progress. There is no guarantee that this API will work or |
| * that it will remain the same. Please do not use this API without consulting |
| * with the RDT team. |
| * |
| * @author crecoskie |
| * |
| */ |
| public class RemoteCommandLauncher implements ICommandLauncher { |
| protected IProject fProject; |
| |
| protected Process fProcess; |
| protected IRemoteProcess fRemoteProcess; |
| protected boolean fShowCommand; |
| protected String[] fCommandArgs; |
| protected String lineSeparator = "\r\n"; //$NON-NLS-1$ |
| protected String fErrorMessage; |
| |
| protected Map<String, String> remoteEnvMap; |
| |
| private boolean isCleanBuild; |
| |
| /** |
| * The number of milliseconds to pause between polling. |
| */ |
| protected static final long DELAY = 50L; |
| |
| /** |
| * |
| */ |
| public RemoteCommandLauncher() { |
| } |
| |
| private boolean isCleanBuild(String[] args){ |
| for(int i=0; i< args.length; i++){ |
| if(IBuilder.DEFAULT_TARGET_CLEAN.equals(args[i])){ |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#execute(org.eclipse.core.runtime.IPath, java.lang.String[], java.lang.String[], org.eclipse.core.runtime.IPath) |
| */ |
| public Process execute(IPath commandPath, String[] args, String[] env, |
| IPath changeToDirectory, final IProgressMonitor monitor) throws CoreException { |
| isCleanBuild= isCleanBuild(args); |
| // IndexBuildSequenceController projectStatus = IndexBuildSequenceController.getIndexBuildSequenceController(getProject()); |
| // |
| // if(projectStatus!=null){ |
| // projectStatus.setRuntimeBuildStatus(null); |
| // |
| // } |
| |
| fCommandArgs = constructCommandArray(commandPath.toPortableString(), args); |
| |
| // Determine the service model for this configuration, and use the provider of the build |
| // service to execute the build command. |
| |
| // if there is no project associated to us then we cannot function... throw an exception |
| if(getProject() == null) { |
| throw new CoreException(new Status(IStatus.ERROR, "org.eclipse.ptp.rdt.sync.core", "RemoteCommandLauncher has not been associated with a project.")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| ServiceModelManager smm = ServiceModelManager.getInstance(); |
| IServiceConfiguration serviceConfig; |
| |
| try { |
| serviceConfig = smm.getActiveConfiguration(getProject()); |
| } catch (ProjectNotConfiguredException e) { |
| return null; |
| } |
| |
| IService buildService = smm.getService(IRemoteSyncServiceConstants.SERVICE_BUILD); |
| IServiceProvider provider = serviceConfig.getServiceProvider(buildService); |
| IRemoteExecutionServiceProvider executionProvider = null; |
| if(provider instanceof IRemoteExecutionServiceProvider) { |
| executionProvider = (IRemoteExecutionServiceProvider) provider; |
| } |
| |
| if (executionProvider != null) { |
| |
| IRemoteServices remoteServices = executionProvider.getRemoteServices(); |
| if(!remoteServices.isInitialized()) { |
| remoteServices.initialize(); |
| } |
| |
| if (remoteServices == null) |
| return null; |
| |
| IRemoteConnection connection = executionProvider.getConnection(); |
| |
| if(!connection.isOpen()) { |
| try { |
| connection.open(monitor); |
| } catch (RemoteConnectionException e1) { |
| // rethrow as CoreException |
| throw new CoreException(new Status(IStatus.ERROR, "org.eclipse.ptp.rdt.sync.core", "Error opening connection.", e1)); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| List<String> command = new LinkedList<String>(); |
| |
| command.add(commandPath.toString()); |
| |
| for(int k = 0; k < args.length; k++) |
| command.add(args[k]); |
| |
| IRemoteProcessBuilder processBuilder = remoteServices.getProcessBuilder(connection, command); |
| |
| remoteEnvMap = processBuilder.environment(); |
| |
| for(String envVar : env) { |
| String[] splitStr = envVar.split("="); //$NON-NLS-1$ |
| remoteEnvMap.put(splitStr[0], splitStr[1]); |
| } |
| |
| // set the directory in which to run the command |
| IRemoteFileManager fileManager = remoteServices.getFileManager(connection); |
| if(changeToDirectory != null && fileManager != null) { |
| processBuilder.directory(fileManager.getResource(changeToDirectory.toString())); |
| } |
| |
| // combine stdout and stderr |
| processBuilder.redirectErrorStream(true); |
| |
| |
| |
| IRemoteProcess p = null; |
| try { |
| p = processBuilder.start(); |
| } catch (IOException e) { |
| // if(projectStatus!=null){ |
| // projectStatus.setRuntimeBuildStatus(IndexBuildSequenceController.STATUS_INCOMPLETE); |
| // } |
| // rethrow as CoreException |
| throw new CoreException(new Status(IStatus.ERROR, "org.eclipse.ptp.rdt.sync.core", "Error launching remote process.", e)); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| // if(projectStatus!=null){ |
| // if(!isCleanBuild){ |
| // projectStatus.setBuildRunning(); |
| // } |
| // } |
| |
| fRemoteProcess = p; |
| fProcess = new RemoteProcessAdapter(p); |
| // wait for the process to finish |
| while (!p.isCompleted()) { |
| try { |
| p.waitFor(); |
| } catch (InterruptedException e) { |
| // just keep waiting until the process is done |
| } |
| } |
| |
| return fProcess; |
| } |
| |
| return null; |
| } |
| |
| private String getCommandLine(String[] commandArgs) { |
| |
| if(fProject == null) |
| return null; |
| |
| StringBuffer buf = new StringBuffer(); |
| if (fCommandArgs != null) { |
| for (String commandArg : commandArgs) { |
| buf.append(commandArg); |
| buf.append(' '); |
| } |
| buf.append(lineSeparator); |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#getCommandLine() |
| */ |
| public String getCommandLine() { |
| return getCommandLine(getCommandArgs()); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#getCommandArgs() |
| */ |
| public String[] getCommandArgs() { |
| return fCommandArgs; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#getEnvironment() |
| */ |
| public Properties getEnvironment() { |
| return convertEnvMapToProperties(); |
| } |
| |
| private Properties convertEnvMapToProperties() { |
| Properties properties = new Properties(); |
| |
| for(String key : remoteEnvMap.keySet()) { |
| properties.put(key, remoteEnvMap.get(key)); |
| } |
| |
| return properties; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#getErrorMessage() |
| */ |
| public String getErrorMessage() { |
| return fErrorMessage; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#setErrorMessage(java.lang.String) |
| */ |
| public void setErrorMessage(String error) { |
| fErrorMessage = error; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#showCommand(boolean) |
| */ |
| public void showCommand(boolean show) { |
| fShowCommand = show; |
| |
| } |
| |
| protected void printCommandLine(OutputStream os) { |
| if (os != null) { |
| String cmd = getCommandLine(getCommandArgs()); |
| try { |
| os.write(cmd.getBytes()); |
| os.flush(); |
| } catch (IOException e) { |
| // ignore; |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#waitAndRead(java.io.OutputStream, java.io.OutputStream) |
| */ |
| public int waitAndRead(OutputStream out, OutputStream err) { |
| if (fShowCommand) { |
| printCommandLine(out); |
| } |
| |
| if (fProcess == null) { |
| return ILLEGAL_COMMAND; |
| } |
| |
| RemoteProcessClosure closure = new RemoteProcessClosure(fRemoteProcess, out, err); |
| closure.runBlocking(); // a blocking call |
| return OK; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.ICommandLauncher#waitAndRead(java.io.OutputStream, java.io.OutputStream, org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public int waitAndRead(OutputStream output, OutputStream err, |
| IProgressMonitor monitor) { |
| if (fShowCommand) { |
| printCommandLine(output); |
| } |
| |
| if (fProcess == null) { |
| return ILLEGAL_COMMAND; |
| } |
| |
| RemoteProcessClosure closure = new RemoteProcessClosure(fRemoteProcess, output, err); |
| closure.runNonBlocking(); |
| while (!monitor.isCanceled() && closure.isAlive()) { |
| try { |
| Thread.sleep(DELAY); |
| } catch (InterruptedException ie) { |
| // ignore |
| } |
| } |
| |
| int state = OK; |
| // final IndexBuildSequenceController projectStatus = IndexBuildSequenceController.getIndexBuildSequenceController(getProject()); |
| // Operation canceled by the user, terminate abnormally. |
| if (monitor.isCanceled()) { |
| closure.terminate(); |
| state = COMMAND_CANCELED; |
| setErrorMessage(CCorePlugin.getResourceString("CommandLauncher.error.commandCanceled")); //$NON-NLS-1$ |
| // if(projectStatus!=null){ |
| // projectStatus.setRuntimeBuildStatus(IndexBuildSequenceController.STATUS_INCOMPLETE); |
| // } |
| } |
| |
| try { |
| fProcess.waitFor(); |
| } catch (InterruptedException e) { |
| // ignore |
| } |
| |
| |
| try { |
| // Do not allow the cancel of the refresh, since the |
| // builder is external |
| // to Eclipse, files may have been created/modified |
| // and we will be out-of-sync. |
| // The caveat is that for huge projects, it may take a while |
| getProject().refreshLocal(IResource.DEPTH_INFINITE, null); |
| } catch (CoreException e) { |
| // this should never happen because we should never be building from a |
| // state where ressource changes are disallowed |
| } |
| |
| |
| // if(projectStatus!=null){ |
| // if(isCleanBuild){ |
| // |
| // projectStatus.setBuildInCompletedForCleanBuild(); |
| // |
| // |
| // }else{ |
| // |
| // if(projectStatus.isIndexAfterBuildSet()){ |
| // |
| // projectStatus.invokeIndex(); |
| // }else{ |
| // projectStatus.setFinalBuildStatus(); |
| // } |
| // |
| // } |
| // } |
| |
| return state; |
| } |
| |
| public IProject getProject() { |
| return fProject; |
| } |
| |
| public void setProject(IProject project) { |
| fProject = project; |
| } |
| |
| } |