blob: 2afea2f5c8c3865c4d22b6b309c78eac629d12aa [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 2019 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.renv.core;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
import static org.eclipse.statet.jcommons.lang.SystemUtils.OS_MAC;
import static org.eclipse.statet.jcommons.lang.SystemUtils.OS_WIN;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.lang.SystemUtils;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.status.WarningStatus;
import org.eclipse.statet.internal.rj.renv.core.REnvCoreInternals;
@NonNullByDefault
public class DefaultLocalConfigurator {
/** Returns first directory which exists */
private static final @Nullable Path getFirstDirectory(
final Path baseDirectory, final String... names) {
for (final String name : names) {
final Path directory= baseDirectory.resolve(name);
if (Files.isDirectory(directory)) {
return directory;
}
}
return null;
}
/** Returns first directory which exists */
private static final @Nullable Path getFirstDirectoryWith(
final List<Path> directories, final String name) {
for (final Path directory : directories) {
final Path file= directory.resolve(name);
if (Files.isRegularFile(file)) {
return directory;
}
}
return null;
}
public static final int MINIMAL_SETUP= 1;
private final REnvConfiguration config;
public DefaultLocalConfigurator(final REnvConfiguration config) {
this.config= config;
}
protected REnvConfiguration getConfig() {
return this.config;
}
protected @Nullable String getSystemEnvVar(final String name) {
return System.getenv(name);
}
public @Nullable String getRArch() {
final String arch= this.config.getRArch();
if (arch == null) {
return null;
}
if (SystemUtils.getLocalOs() == OS_WIN) {
switch (arch) {
case SystemUtils.ARCH_X86_64:
return "x64"; //$NON-NLS-1$
case SystemUtils.ARCH_X86_32:
return "i386"; //$NON-NLS-1$
}
}
return arch;
}
public Map<String, String> getEnvironmentsVariables(final int options) throws StatusException {
if (!this.config.isLocal()
|| (this.config instanceof BasicREnvConfiguration
&& (((BasicREnvConfiguration) this.config).getFlags() & BasicREnvConfiguration.SPEC_SETUP) == 0 )) {
throw new UnsupportedOperationException();
}
if (this.config.getValidationStatus().getSeverity() == Status.ERROR) {
// ?
}
final Map<String, String> envp= new HashMap<>();
envp.put("R_HOME", getRHomeDirectory().toString()); //$NON-NLS-1$
switch (SystemUtils.getLocalOs()) {
case OS_WIN:
putPrepend(envp, "PATH", //$NON-NLS-1$
checkLibDirectory(getRBinDirectoryCandidates(), "R.dll") ); //$NON-NLS-1$
// libs in path
break;
case OS_MAC:
putPrepend(envp, "PATH", //$NON-NLS-1$
getRHomeDirectory().resolve("bin") ); //$NON-NLS-1$
if ((options & MINIMAL_SETUP) == 0) {
putPrepend(envp, "DYLD_LIBRARY_PATH", //$NON-NLS-1$
getRHomeDirectory().resolve("lib") ); //$NON-NLS-1$
}
break;
default:
putPrepend(envp, "PATH", //$NON-NLS-1$
getRHomeDirectory().resolve("bin") ); //$NON-NLS-1$
if ((options & MINIMAL_SETUP) == 0) {
putPrepend(envp, "LD_LIBRARY_PATH", //$NON-NLS-1$
checkLibDirectory(getRLibDirectoryCandidates(), "libR.so") ); //$NON-NLS-1$
}
break;
}
if ((options & MINIMAL_SETUP) == 0) {
final String rArch= getRArch();
if (rArch != null && isArchAvailable(rArch)) {
envp.put("R_ARCH", '/' + rArch);
}
putIfNonNull(envp, "R_SHARE_DIR", this.config.getRShareDirectoryPath());
putIfNonNull(envp, "R_INCLUDE_DIR", this.config.getRIncludeDirectoryPath());
putIfNonNull(envp, "R_DOC_DIR", this.config.getRDocDirectoryPath());
putIfNonNull(envp, "R_LIBS_SITE", //$NON-NLS-1$
getLibPathString(this.config.getRLibGroup(RLibGroup.R_SITE)) );
putIfNonNull(envp, "R_LIBS_USER", //$NON-NLS-1$
getLibPathString(this.config.getRLibGroup(RLibGroup.R_USER)) );
putIfNonNull(envp, "R_LIBS", //$NON-NLS-1$
getLibPathString(this.config.getRLibGroup(RLibGroup.R_OTHER)) );
}
envp.put("LC_NUMERIC", "C"); //$NON-NLS-1$ //$NON-NLS-2$
return envp;
}
protected final Path getRHomeDirectory() {
return nonNullAssert(this.config.getRHomeDirectoryPath());
}
protected boolean isArchAvailable(final String rArch) {
try {
final Path rHomeBin= getRHomeDirectory().resolve("bin"); //$NON-NLS-1$
final Path rHomeBinSub;
final String name;
switch (SystemUtils.getLocalOs()) {
case OS_WIN:
rHomeBinSub= rHomeBin;
name= "R.exe"; //$NON-NLS-1$
break;
default:
rHomeBinSub= rHomeBin.resolve("exec"); //$NON-NLS-1$
name= "R"; //$NON-NLS-1$
break;
}
final Path file= rHomeBinSub.resolve(rArch).resolve(name);
return Files.isRegularFile(file);
}
catch (final Exception e) {
return false;
}
}
protected @Nullable Path getArchDirectory(final Path baseDirectory) {
String arch= this.config.getRArch();
if (arch == null) {
arch= SystemUtils.getLocalArch();
}
switch (arch) {
case SystemUtils.ARCH_X86_64:
return getFirstDirectory(baseDirectory, "x86_64", "x64"); //$NON-NLS-1$ //$NON-NLS-2$
case SystemUtils.ARCH_X86_32:
return getFirstDirectory(baseDirectory, "x86", "i386", "i586", "i686"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
default:
return getFirstDirectory(baseDirectory, arch);
}
}
protected List<Path> getRBinDirectoryCandidates() {
final List<Path> directories= new ArrayList<>(4);
final Path rHomeBin= getRHomeDirectory().resolve("bin"); //$NON-NLS-1$
final Path rHomeBinSub;
switch (SystemUtils.getLocalOs()) {
case OS_WIN:
rHomeBinSub= rHomeBin;
break;
default: // use wrapper shell scripts
rHomeBinSub= null; // rHomeBin.getChild("exec");
break;
}
if (rHomeBinSub != null) {
final Path rHomeBinArch= getArchDirectory(rHomeBinSub);
if (rHomeBinArch != null) {
directories.add(rHomeBinArch);
}
}
directories.add(rHomeBin);
return directories;
}
protected List<Path> getRLibDirectoryCandidates() {
final List<Path> directories= new ArrayList<>(4);
final Path rHomeLib;
switch (SystemUtils.getLocalOs()) {
case OS_WIN:
rHomeLib= getRHomeDirectory().resolve("bin"); //$NON-NLS-1$
break;
default:
rHomeLib= getRHomeDirectory().resolve("lib"); //$NON-NLS-1$
break;
}
if (SystemUtils.getLocalOs() != OS_MAC) {
final Path rHomeLibArch= getArchDirectory(rHomeLib);
if (rHomeLibArch != null) {
directories.add(rHomeLibArch);
}
}
directories.add(rHomeLib);
return directories;
}
private Path checkLibDirectory(final List<Path> candidates, final String fileName)
throws StatusException {
Path directory= getFirstDirectoryWith(getRLibDirectoryCandidates(), fileName);
if (directory == null) {
// TODO error?
REnvCoreInternals.log(new WarningStatus(REnvCoreInternals.BUNDLE_ID,
String.format("Could not find R library directory (file= '%1$s') for R environment '%2$s.",
fileName, this.config.getREnv().getId() ) ));
directory= candidates.get(candidates.size() - 1);
}
return directory;
}
protected @Nullable String getLibPathString(final @Nullable RLibGroup group) {
if (group == null) {
return null;
}
final List<? extends RLibLocation> libLocations= group.getLibLocations();
if (libLocations.isEmpty()) {
return ""; //$NON-NLS-1$
}
final StringBuilder sb= new StringBuilder();
for (final RLibLocation lib : libLocations) {
final Path path= lib.getDirectoryPath();
if (path != null) {
sb.append(toEnvVarString(path));
}
sb.append(File.pathSeparatorChar);
}
return sb.substring(0, sb.length() - 1);
}
protected void putIfNonNull(final Map<String, String> envp,
final String name, final @Nullable Path path) {
if (path == null) {
return;
}
final String value= toEnvVarString(path);
envp.put(name, value);
}
protected void putIfNonNull(final Map<String, String> envp,
final String name, final @Nullable String value) {
if (value == null) {
return;
}
envp.put(name, value);
}
protected void putPrepend(final Map<String, String> envp,
final String name, final Path path) {
String value= toEnvVarString(path);
final String currentValue= getSystemEnvVar(name);
if (currentValue != null) {
value+= File.pathSeparatorChar + currentValue;
}
envp.put(name, value);
}
protected String toEnvVarString(final Path path) {
return path.toString();
}
}