| /*=============================================================================# |
| # Copyright (c) 2007, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.r.cmd.ui.launching; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.filesystem.URIUtil; |
| 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.core.runtime.SubMonitor; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.model.IProcess; |
| import org.eclipse.debug.core.model.LaunchConfigurationDelegate; |
| import org.eclipse.debug.ui.CommonTab; |
| import org.eclipse.debug.ui.DebugUITools; |
| import org.eclipse.ui.console.IConsole; |
| import org.eclipse.ui.console.TextConsole; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| import org.eclipse.statet.ecommons.debug.core.util.LaunchUtils; |
| import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigUtils; |
| import org.eclipse.statet.ecommons.debug.ui.util.UnterminatedLaunchAlerter; |
| import org.eclipse.statet.ecommons.io.FileUtil; |
| |
| import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin; |
| import org.eclipse.statet.r.cmd.ui.launching.RCmdLaunching; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration.Exec; |
| import org.eclipse.statet.r.launching.core.RLaunching; |
| import org.eclipse.statet.r.launching.ui.REnvTab; |
| import org.eclipse.statet.r.launching.ui.RErrorLineTracker; |
| |
| |
| @NonNullByDefault |
| public class RCmdLaunchDelegate extends LaunchConfigurationDelegate { |
| |
| |
| private static class RunData { |
| |
| private final IREnvConfiguration rEnvConfig; |
| |
| private final String cmd; |
| |
| private final List<String> command; |
| |
| private final IFileStore workingDirectory; |
| |
| private final @Nullable IPath resourcePathAbsolute; |
| |
| public RunData(final IREnvConfiguration rEnvConfig, final String cmd, final List<String> command, |
| final IFileStore workingDirectory, final @Nullable IPath resourcePathAbsolute) { |
| this.rEnvConfig= rEnvConfig; |
| this.cmd= cmd; |
| this.command= command; |
| this.workingDirectory= workingDirectory; |
| this.resourcePathAbsolute= resourcePathAbsolute; |
| } |
| |
| } |
| |
| |
| public RCmdLaunchDelegate() { |
| } |
| |
| |
| @Override |
| public String showCommandLine(final ILaunchConfiguration configuration, final String mode, |
| final @Nullable ILaunch launch, |
| final @Nullable IProgressMonitor monitor) throws CoreException { |
| final SubMonitor m= SubMonitor.convert(monitor, 5); |
| try { |
| final RunData data= getLaunchCommandData(configuration, m); |
| return (data != null) ? |
| LaunchUtils.generateCommandLine(data.command) : |
| ""; //$NON-NLS-1$ |
| } |
| finally { |
| m.done(); |
| } |
| } |
| |
| @Override |
| public void launch(final ILaunchConfiguration configuration, final String mode, |
| final ILaunch launch, |
| final @Nullable IProgressMonitor monitor) throws CoreException { |
| final SubMonitor m= LaunchUtils.initProgressMonitor(configuration, monitor, 25); |
| final long timestamp= System.currentTimeMillis(); |
| try { |
| if (m.isCanceled()) { |
| return; |
| } |
| |
| final RunData data= getLaunchCommandData(configuration, m); |
| if (data == null || m.isCanceled()) { |
| return; |
| } |
| |
| final ProcessBuilder builder= new ProcessBuilder(data.command); |
| builder.directory(data.workingDirectory.toLocalFile(EFS.NONE, null)); |
| builder.redirectErrorStream( |
| configuration.getAttribute(DebugPlugin.ATTR_MERGE_OUTPUT, false) ); |
| |
| // environment |
| final Map<String, String> envp= builder.environment(); |
| LaunchUtils.configureEnvironment(envp, configuration, |
| data.rEnvConfig.getEnvironmentsVariables() ); |
| |
| // exec process |
| UnterminatedLaunchAlerter.registerLaunchType(RCmdLaunching.R_CMD_CONFIGURATION_TYPE_ID); |
| Process p; |
| try { |
| p= builder.start(); |
| } catch (final IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, 0, |
| Messages.RCmd_LaunchDelegate_error_StartingExec, e )); |
| } |
| m.worked(10); |
| |
| // register process |
| final Map<String, String> processAttributes= new HashMap<>(); |
| processAttributes.put(IProcess.ATTR_PROCESS_TYPE, RCmdLaunching.R_CMD_PROCESS_TYPE); |
| final String processName= data.command.get(0) + ' ' + LaunchUtils.createProcessTimestamp(timestamp); |
| final String label; |
| { |
| final StringBuilder sb= new StringBuilder(200); |
| sb.append(LaunchUtils.createLaunchPrefix(configuration)); |
| sb.append(' ').append(data.rEnvConfig.getName()); |
| sb.append(" : R ").append(data.cmd); //$NON-NLS-1$ |
| if (data.resourcePathAbsolute != null) { |
| sb.append(' ').append(data.resourcePathAbsolute.toOSString()); |
| } |
| sb.append(" ~ ").append(processName); //$NON-NLS-1$ |
| label= sb.toString(); |
| } |
| |
| final IProcess process= DebugPlugin.newProcess(launch, p, processName, processAttributes); |
| if (process == null) { |
| p.destroy(); |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, 0, |
| Messages.RCmd_LaunchDelegate_error_ProcessHandle, null )); |
| } |
| process.setAttribute(IProcess.ATTR_CMDLINE, LaunchUtils.generateCommandLine(data.command)); |
| process.setAttribute(IProcess.ATTR_PROCESS_LABEL, label); |
| |
| m.worked(5); |
| if (!process.isTerminated() && !CommonTab.isLaunchInBackground(configuration)) { |
| m.subTask(Messages.RCmd_LaunchDelegate_Running_label); |
| } |
| |
| final IConsole console= DebugUITools.getConsole(process); |
| if (console instanceof TextConsole) { |
| final RErrorLineTracker lineMatcher= new RErrorLineTracker(data.workingDirectory); |
| ((TextConsole) console).addPatternMatchListener(lineMatcher); |
| } |
| |
| LaunchConfigUtils.launchResourceRefresh(configuration, process, m.newChild(5)); |
| } |
| finally { |
| m.done(); |
| } |
| } |
| |
| private @Nullable RunData getLaunchCommandData(final ILaunchConfiguration configuration, |
| final SubMonitor m) throws CoreException { |
| final List<String> command= new ArrayList<>(); |
| |
| // r env |
| final IREnvConfiguration rEnvConfig= RLaunching.getREnvConfig(configuration, true); |
| // renv.validate(); |
| |
| final String cmd= configuration.getAttribute(RCmdLaunching.R_CMD_COMMAND_ATTR_NAME, "").trim(); //$NON-NLS-1$ |
| if (!cmd.isEmpty()) { |
| command.addAll(Arrays.asList(cmd.split(" "))); //$NON-NLS-1$ |
| } |
| String arg1= null; |
| if (command.size() > 0) { |
| arg1= command.remove(0); |
| } |
| command.addAll(0, rEnvConfig.getExecCommand(arg1, EnumSet.of(Exec.CMD, Exec.TERM))); |
| |
| m.worked(1); |
| if (m.isCanceled()) { |
| return null; |
| } |
| |
| // working directory |
| final IFileStore workingDirectory= REnvTab.getWorkingDirectory(configuration); |
| |
| m.worked(1); |
| if (m.isCanceled()) { |
| return null; |
| } |
| |
| // arguments |
| command.addAll(Arrays.asList( |
| LaunchUtils.getProcessArguments(configuration, RCmdLaunching.R_CMD_OPTIONS_ATTR_NAME) )); |
| |
| final String resourceValue= configuration.getAttribute(RCmdLaunching.R_CMD_RESOURCE_ATTR_NAME, ""); //$NON-NLS-1$ |
| IFileStore resource= null; |
| IPath resourcePathAbsolute= null; |
| IPath resourcePathAuto= null; |
| if (resourceValue.length() > 0) { |
| resource= FileUtil.expandToLocalFileStore(resourceValue, workingDirectory, null); |
| final IPath workingDirectoryPath= nonNullAssert(URIUtil.toPath(workingDirectory.toURI())); |
| resourcePathAuto= resourcePathAbsolute= nonNullAssert(URIUtil.toPath(resource.toURI())); |
| if (workingDirectoryPath.isPrefixOf(resourcePathAuto)) { |
| resourcePathAuto= resourcePathAuto.setDevice(null); |
| resourcePathAuto= resourcePathAuto.removeFirstSegments(workingDirectoryPath.segmentCount()); |
| } |
| command.add(resourcePathAuto.toString()); |
| } |
| |
| m.worked(1); |
| |
| return new RunData(rEnvConfig, cmd, command, workingDirectory, resourcePathAbsolute); |
| } |
| |
| } |