blob: 55e393cf67f1b110667e99341a24ff52fa8cb3e0 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2013, 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.rj.services.util;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import org.eclipse.statet.jcommons.io.FileUtils;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.status.ErrorStatus;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.rj.data.RDataUtils;
import org.eclipse.statet.rj.data.RIntegerStore;
import org.eclipse.statet.rj.data.RVector;
import org.eclipse.statet.rj.data.UnexpectedRDataException;
import org.eclipse.statet.rj.renv.core.RNumVersion;
import org.eclipse.statet.rj.renv.core.RPkg;
import org.eclipse.statet.rj.renv.core.RPkgType;
import org.eclipse.statet.rj.renv.core.RPkgUtils;
import org.eclipse.statet.rj.services.FunctionCall;
import org.eclipse.statet.rj.services.RJServices;
import org.eclipse.statet.rj.services.RService;
/**
* Utility to install an R package from a local file.
* <p>
* Run {@link #install(RService, ProgressMonitor)} to install the package.</p>
* <p>
* The class can be reused.</p>
*/
@NonNullByDefault
public class RPkgInstallation {
private final RPkg pkgInfo;
private final Path file;
private final String fileName;
public RPkgInstallation(final Path file) throws StatusException {
this.file= nonNullAssert(file);
this.fileName= FileUtils.requireFileName(file).toString();
this.pkgInfo= RPkgUtils.checkPkgFileName(this.fileName);
}
public RPkgInstallation(final File file) throws StatusException {
this(file.toPath());
}
public RPkg getPkg() {
return this.pkgInfo;
}
protected String getPkgFileName() {
return this.fileName;
}
protected void uploadPkgFile(final String target,
final RService r, final ProgressMonitor m) throws StatusException, IOException {
try (final var in= Files.newInputStream(this.file)) {
r.uploadFile(in, Files.size(this.file), target, 0, m);
}
}
public void install(final RService r, final ProgressMonitor m) throws StatusException {
final String source= getPkgFileName();
final RPkgType pkgType= RPkgUtils.checkPkgType(source, r.getPlatform());
Exception error= null;
String serverFile= null;
String libLoc= null;
try {
{ final RVector<RIntegerStore> data= RDataUtils.checkRIntVector(r.evalData(
"rj:::renv.isValidLibLocation(.libPaths()[1])", m )); //$NON-NLS-1$
final int state= RDataUtils.checkSingleIntValue(data);
libLoc= data.getNames().getChar(0);
if (state != 0) {
throw new StatusException(new ErrorStatus(RJServices.BUNDLE_ID,
MessageFormat.format("The library location ''{0}'' is not writable.", libLoc) ));
}
}
{ final FunctionCall call= r.createFunctionCall("dir.create"); //$NON-NLS-1$
call.addChar("rpkgs"); //$NON-NLS-1$
call.addLogi("showWarnings", false); //$NON-NLS-1$
call.evalVoid(m);
serverFile= "rpkgs/" + source; //$NON-NLS-1$
}
uploadPkgFile(serverFile, r, m);
{ final FunctionCall call= r.createFunctionCall("install.packages"); //$NON-NLS-1$
call.addChar(serverFile);
call.addChar("lib", libLoc); //$NON-NLS-1$
call.addNull("repos"); //$NON-NLS-1$
call.addChar("type", RPkgUtils.getPkgTypeInstallKey(r.getPlatform(), pkgType)); //$NON-NLS-1$
call.evalVoid(m);
}
{ final FunctionCall call= r.createFunctionCall("packageDescription"); //$NON-NLS-1$
call.addChar("pkg", this.pkgInfo.getName()); //$NON-NLS-1$
call.addChar("lib.loc", libLoc); //$NON-NLS-1$
call.addChar("fields", "Version"); //$NON-NLS-1$ //$NON-NLS-2$
final RVector<?> data= RDataUtils.checkRVector(call.evalData(m));
try {
final String s= RDataUtils.checkSingleCharValue(data);
final RNumVersion installedVersion= RNumVersion.create(s);
if (!installedVersion.equals(this.pkgInfo.getVersion())) {
throw new StatusException(new ErrorStatus(RJServices.BUNDLE_ID,
String.format("Validation of package installation failed: " +
"installed package has different version (found= %1$s, expected= %2$s).",
installedVersion.toString(),
this.pkgInfo.getVersion().toString() )));
}
}
catch (final UnexpectedRDataException e) {
throw new StatusException(new ErrorStatus(RJServices.BUNDLE_ID,
"Validation of package installation failed: no installed package found." ));
}
}
clear(serverFile, r, m);
serverFile= null;
}
catch (final IOException e) {
error= e;
}
catch (final StatusException e) {
error= e;
}
catch (final UnexpectedRDataException e) {
error= e;
}
finally {
if (serverFile != null) {
try {
clear(serverFile, r, m);
}
catch (final Exception e) {}
}
}
if (error != null) {
throw new StatusException(new ErrorStatus(RJServices.BUNDLE_ID,
String.format("An error occurred when installing R package from %1$s.",
source ),
error ));
}
}
private void clear(final String target,
final RService r, final ProgressMonitor m) throws StatusException {
final FunctionCall call= r.createFunctionCall("file.remove"); //$NON-NLS-1$
call.addChar(target);
call.evalVoid(m);
}
}