blob: aa6efdf039f42ed53fddbb019cde22c2b2b55dc2 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2021 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);
}
}