From d8405596b5a5c1c60864b1df5b917434354e4d74 Mon Sep 17 00:00:00 2001 From: Nigel Delaney Date: Sun, 19 Oct 2014 15:14:40 -0700 Subject: [PATCH] Mac OSX Compatiblity Several Changes =============== * Removed R.NET library loading logic, am leaving it to R.NET to * find the library and throw errors if it is unable to. * Upgraded TypeProvidersStarter Pack. * Changed logging file mechanism to be platform independent. * Run a local server if running in mono to avoid IPC when in * linux. --- .gitattributes | 1 + src/RProvider/Logging.fs | 5 +-- src/RProvider/RInit.fs | 60 +++------------------------------ src/RProvider/RInteropClient.fs | 23 ++++++++++--- src/RProvider/RInteropServer.fs | 10 ++---- src/RProvider/RTypeBuilder.fs | 14 +------- src/RProvider/packages.config | 10 +++--- 7 files changed, 35 insertions(+), 88 deletions(-) diff --git a/.gitattributes b/.gitattributes index 412eeda7..fe1bbb62 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ # Auto detect text files and perform LF normalization * text=auto +*.config text=crlf # Custom for Visual Studio *.cs diff=csharp diff --git a/src/RProvider/Logging.fs b/src/RProvider/Logging.fs index 71e3c443..6c6f6736 100644 --- a/src/RProvider/Logging.fs +++ b/src/RProvider/Logging.fs @@ -14,8 +14,9 @@ let [] 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 diff --git a/src/RProvider/RInit.fs b/src/RProvider/RInit.fs index 7d3a1e46..31f6bf92 100644 --- a/src/RProvider/RInit.fs +++ b/src/RProvider/RInit.fs @@ -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 |> 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 diff --git a/src/RProvider/RInteropClient.fs b/src/RProvider/RInteropClient.fs index 5314e6ed..478d1e85 100644 --- a/src/RProvider/RInteropClient.fs +++ b/src/RProvider/RInteropClient.fs @@ -9,14 +9,20 @@ open System.Threading open Microsoft.Win32 open System.IO open RProviderServer +open RDotNet.NativeLibrary module internal RInteropClient = [] 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" @@ -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); diff --git a/src/RProvider/RInteropServer.fs b/src/RProvider/RInteropServer.fs index 9f43c974..dbcc0b53 100644 --- a/src/RProvider/RInteropServer.fs +++ b/src/RProvider/RInteropServer.fs @@ -10,9 +10,7 @@ open System type RInteropServer() = inherit MarshalByRefObject() - - let initResultValue = RInit.initResult.Force() - + let exceptionSafe f = try f() @@ -20,11 +18,7 @@ type RInteropServer() = | 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 () -> diff --git a/src/RProvider/RTypeBuilder.fs b/src/RProvider/RTypeBuilder.fs index 3d7cc619..2fe2d1fb 100644 --- a/src/RProvider/RTypeBuilder.fs +++ b/src/RProvider/RTypeBuilder.fs @@ -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)) - let prop = ProvidedProperty("", typeof, 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" ] \ No newline at end of file diff --git a/src/RProvider/packages.config b/src/RProvider/packages.config index cee9e6e1..3bead8db 100644 --- a/src/RProvider/packages.config +++ b/src/RProvider/packages.config @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file