| /*=============================================================================# |
| # 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); |
| } |
| |
| } |