Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Auto detect text files and perform LF normalization
* text=auto
*.config text=crlf

# Custom for Visual Studio
*.cs diff=csharp
Expand Down
5 changes: 3 additions & 2 deletions src/RProvider/Logging.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ let [<Literal>] private loggingEnabled = false
let private logFile =
try
let appd = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
if not (Directory.Exists(appd + "\\RLogs")) then Directory.CreateDirectory(appd + "\\RLogs") |> ignore
appd + "\\RLogs\\log.txt"
let rlogDir = Path.Combine(appd,"RLogs")
if not (Directory.Exists(rlogDir)) then Directory.CreateDirectory(rlogDir) |> ignore
Path.Combine(rlogDir, "log.txt")
with _ -> (* Silently ignoring logging errors *) null

/// Append string to a log file
Expand Down
60 changes: 5 additions & 55 deletions src/RProvider/RInit.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,68 +12,18 @@ type RInitResult<'T> =
| RInitResult of 'T
| RInitError of string

/// Find the R installation. First check "R_HOME" environment variable, then look
/// at the SOFTWARE\R-core\R\InstallPath value (using HKCU or, as a second try HKLM root)
let private getRLocation () =
let getRLocationFromRCoreKey (rCore:RegistryKey) =
let key = rCore.OpenSubKey "R"
if key = null then RInitError "SOFTWARE\R-core exists but subkey R does not exist"
else key.GetValue "InstallPath" |> unbox<string> |> RInitResult

let locateRfromRegistry () =
match Registry.LocalMachine.OpenSubKey @"SOFTWARE\R-core", Registry.CurrentUser.OpenSubKey @"SOFTWARE\R-core" with
| null, null -> RInitError "Reg key Software\R-core does not exist; R is likely not installed on this computer"
| null, x
| x, _ -> getRLocationFromRCoreKey x

Logging.logf "getRLocation"
match Environment.GetEnvironmentVariable "R_HOME" with
| null -> locateRfromRegistry()
| rPath -> RInitResult rPath

/// Find the R installation using 'getRLocation' and add the directory to the
/// current environment varibale PATH (so that later loading can find 'R.dll')
let private setupPathVariable () =
try
Logging.logf "setupPathVariable"
match getRLocation() with
| RInitError error -> RInitError error
| RInitResult location ->
let isLinux =
let platform = Environment.OSVersion.Platform
// The guide at www.mono-project.com/FAQ:_Technical says to also check for the
// value 128, but that is only relevant to old versions of Mono without F# support
platform = PlatformID.MacOSX || platform = PlatformID.Unix
let binPath =
if isLinux then
Path.Combine(location, "lib")
else
Path.Combine(location, "bin", if Environment.Is64BitProcess then "x64" else "i386")
// Set the path
if not ((Path.Combine(binPath, "libR.so") |> File.Exists) || (Path.Combine(binPath,"R.dll") |> File.Exists)) then
RInitError (sprintf "No R engine at %s" binPath)
else
// Set the path
REngine.SetEnvironmentVariables(binPath, location)
Logging.logf "setupPathVariable completed"
RInitResult ()
with e ->
Logging.logf "setupPathVariable failed: %O" e
reraise()

/// Global interceptor that captures R console output
let internal characterDevice = new CharacterDeviceInterceptor()

/// Lazily initialized value that, when evaluated, sets the PATH variable
/// to include the R location, or fails and returns RInitError
let initResult = Lazy<_>(fun () -> setupPathVariable())

/// Lazily initialized R engine.
let internal engine = Lazy<_>(fun () ->
try
Logging.logf "engine: Creating and initializing instance"
initResult.Force() |> ignore
let engine = REngine.GetInstance(null, true, null, characterDevice)
(* R.NET needs to initialize the engine, find the shared library and
set the appropriate environmental variables. This is a common failure point,
but fixes should ideally pushed upstream to R.NET, rather than having redundant code here
*)
let engine = REngine.GetInstance("", true, null, characterDevice)
System.AppDomain.CurrentDomain.DomainUnload.Add(fun _ -> engine.Dispose())
Logging.logf "engine: Created & initialized instance"
engine
Expand Down
23 changes: 18 additions & 5 deletions src/RProvider/RInteropClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ open System.Threading
open Microsoft.Win32
open System.IO
open RProviderServer
open RDotNet.NativeLibrary

module internal RInteropClient =

[<Literal>]
let server = "RProvider.Server.exe"

// true to load the server in-process, false load the server out-of-process
let localServer = false
let runningInMono = if Type.GetType("Mono.Runtime") <> null then true else false

(* True to load the server in-process, false load the server out-of-process.
Because interprocess communication is very different on Mono versus Microsoft,
by default use a local server on unix/mac to avoid IPC compatibility issues.
*)
let localServer = if runningInMono then true else false

let mutable lastServer = None
let serverlock = "serverlock"
Expand Down Expand Up @@ -50,9 +56,16 @@ module internal RInteropClient =
let assem = Assembly.GetExecutingAssembly()
let assemblyLocation = assem |> RProvider.Internal.Configuration.getAssemblyLocation

let exePath = Path.Combine(Path.GetDirectoryName(assemblyLocation), server)
let arguments = channelName
let startInfo = ProcessStartInfo(UseShellExecute = false, CreateNoWindow = true, FileName=exePath, Arguments = arguments, WindowStyle = ProcessWindowStyle.Hidden)

let mutable exeName = Path.Combine(Path.GetDirectoryName(assemblyLocation), server)
let mutable arguments = channelName
// Open F# with call to Mono first if needed.
if NativeUtility.IsUnix then
arguments <- exeName + " "+ channelName
exeName <- "mono"


let startInfo = ProcessStartInfo(UseShellExecute = false, CreateNoWindow = true, FileName=exeName, Arguments = arguments, WindowStyle = ProcessWindowStyle.Hidden)
let p = Process.Start(startInfo, EnableRaisingEvents = true)
let maxSeconds = 15;
let maxTimeSpan = new TimeSpan(0, 0, maxSeconds);
Expand Down
10 changes: 2 additions & 8 deletions src/RProvider/RInteropServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,15 @@ open System

type RInteropServer() =
inherit MarshalByRefObject()

let initResultValue = RInit.initResult.Force()


let exceptionSafe f =
try
f()
with
| ex when ex.GetType().IsSerializable -> raise ex
| ex ->
failwith ex.Message

member x.RInitValue =
match initResultValue with
| RInit.RInitError error -> Some error
| _ -> None


member x.GetPackages() =
exceptionSafe <| fun () ->
Expand Down
14 changes: 1 addition & 13 deletions src/RProvider/RTypeBuilder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,5 @@ module internal RTypeBuilder =
[ // Get the assembly and namespace used to house the provided types
Logging.logf "initAndGenerate: starting"
let ns = "RProvider"

match GetServer().RInitValue with
| Some error ->
// add an error static property (shown when typing `R.`)
let pty = ProvidedTypeDefinition(providerAssembly, ns, "R", Some(typeof<obj>))
let prop = ProvidedProperty("<Error>", typeof<string>, IsStatic = true, GetterCode = fun _ -> <@@ error @@>)
prop.AddXmlDoc error
pty.AddMember prop
yield ns, [ pty ]
// add an error namespace (shown when typing `open RProvider.`)
yield ns + ".Error: " + error, [ pty ]
| _ ->
yield! generateTypes ns providerAssembly
yield! generateTypes ns providerAssembly
Logging.logf "initAndGenerate: finished" ]
10 changes: 5 additions & 5 deletions src/RProvider/packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.TypeProviders.StarterPack" version="1.1.0.6" targetFramework="net45" />
<package id="R.NET.Community" version="1.5.16" targetFramework="net45" />
<package id="R.NET.Community.FSharp" version="0.1.9" targetFramework="net45" />
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.TypeProviders.StarterPack" version="1.1.3.48" targetFramework="net45" />
<package id="R.NET.Community" version="1.5.16" targetFramework="net45" />
<package id="R.NET.Community.FSharp" version="0.1.9" targetFramework="net45" />
</packages>