Skip to content

Commit 2a57b4d

Browse files
authored
Fix ProbingLocations for plugins. (#235)
* Fix ProbingLocations - Remove ConfigurationManager dependency - Get .config file from single-file server exes - Get .config file from normal nuget dll * Add Deedle plugin sample * Allow server exes to run on latest major .net release
1 parent 3690e6e commit 2a57b4d

13 files changed

+94
-28
lines changed

build.fsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ let license = "BSD-2-Clause"
4747
let iconUrl = "https://raw.githubusercontent.com/fslaborg/RProvider/master/docs/files/misc/logo.png"
4848
let copyright = "(C) 2014 BlueMountain Capital"
4949

50-
let packageProjectUrl = "https://fslaborg.org/RProvider/"
50+
let packageProjectUrl = "https://fslab.org/RProvider/"
5151
let repositoryType = "git"
5252
let repositoryUrl = "https://github.com/fslaborg/RProvider"
5353
let repositoryContentUrl = "https://raw.githubusercontent.com/fslaborg/RProvider"

docs/diagnostics.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ issue](https://github.com/fslaborg/RProvider/issues).
1616

1717
**TL;DR** The logging is enabled by setting an environment variable
1818
`RPROVIDER_LOG` to a file name where the log should be saved. The file does
19-
not have to exist, but the folder where it is located has to.
19+
not have to exist, but the folder where it is located has to. **You should use
20+
an absolute (full) path, as otherwise the server will create a seperate log in the
21+
nuget package directory.**
2022

2123
Enabling logging on Windows
2224
---------------------------

paket.dependencies

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ nuget Newtonsoft.Json 13.0.1
1818
nuget R.NET 1.9.0
1919
nuget R.NET.FSharp 1.9.0
2020
nuget System.ComponentModel.Composition 5.0.0
21-
nuget System.Configuration.ConfigurationManager 5.0.0
2221
nuget System.Reflection.MetadataLoadContext 5.0.0
2322

2423
github fsprojects/FSharp.TypeProviders.SDK:f4aca36af04aa84b16ec04df6f6bf55ac2f17a73 src/ProvidedTypes.fsi

samples/deedle.fsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#i "nuget:https://www.nuget.org/api/v2"
2+
#i @"nuget:/Volumes/Server HD/GitHub Projects/RProvider/bin"
3+
#r "nuget:RProvider,2.0.0-beta"
4+
#r "nuget:Deedle,2.4.3"
5+
//#r "nuget:Deedle.RProvider.Plugin,2.4.3"
6+
7+
(* This sample shows a plugin for RProvider, which converts
8+
R values into .NET types. Here, the Deedle RProvider plugin
9+
(from the `Deedle.RProvider.Plugin` package) automatically
10+
converts an R data frame into a Deedle frame by adding a
11+
type signature in both directions. *)
12+
13+
open RProvider
14+
open RProvider.``base``
15+
open RProvider.datasets
16+
open Deedle
17+
18+
do fsi.AddPrinter(fun (synexpr:RDotNet.SymbolicExpression) -> synexpr.Print())
19+
20+
// Get mtcars as an untyped object
21+
R.mtcars.Value
22+
23+
// Get mtcars as a typed Deedle frame
24+
let mtcars : Frame<string, string> = R.mtcars.GetValue()
25+
26+
// Pass Deedle data to R and print the R output
27+
R.as_data_frame(mtcars)
28+
29+
// Pass Deedle data to R and get column means
30+
R.colMeans(mtcars)

src/RProvider.Runtime/Configuration.fs

+43-15
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ module RProvider.Internal.Configuration
44
open System
55
open System.IO
66
open System.Reflection
7-
open System.Configuration
87
open System.Collections.Generic
8+
open System.Xml
99

1010
/// Returns the Assembly object of RProvider.Runtime.dll (this needs to
11-
/// work when called from RProvider.DesignTime.dll and also RProvider.Server.exe)
11+
/// work when called from RProvider.DesignTime.dll and also RProvider.Server.exe/dll)
1212
let getRProviderRuntimeAssembly() =
1313
AppDomain.CurrentDomain.GetAssemblies()
1414
|> Seq.find (fun a -> a.FullName.StartsWith("RProvider.Runtime,"))
@@ -33,23 +33,51 @@ let getAssemblyLocation (assem:Assembly) =
3333
(Uri(assem.Location)).LocalPath
3434
else assem.Location
3535

36+
/// Returns the real config file location even when shadow copying is enabled.
37+
/// To account for single-file server executables, we use AppContext.BaseDirectory
38+
/// and navigate up two directories to get to the original RProvider.Runtime.dll
39+
/// location where the config file is (from server/{platform}/)
40+
let getConfigFileLocation (assem:Assembly) =
41+
if String.IsNullOrEmpty assem.Location
42+
then Path.GetFullPath(Path.Combine(AppContext.BaseDirectory,
43+
@"../../", assem.GetName().Name + ".dll.config"))
44+
else getAssemblyLocation assem + ".config"
45+
46+
/// Load a .config XML file and obtain the value of the AppSetting
47+
/// called 'ProbingLocations'.
48+
let probingLocationsFromXmlConfig file =
49+
if not <| File.Exists file then Error "Configuration file missing"
50+
else
51+
let doc = XmlDocument()
52+
doc.LoadXml(File.ReadAllText file)
53+
let appSettings = doc.SelectSingleNode "//appSettings"
54+
let setting = appSettings.SelectSingleNode "//add[@key='ProbingLocations']"
55+
if isNull setting then Error "Appsetting not set: ProbingLocations"
56+
else
57+
let locations = setting.Attributes.GetNamedItem("value")
58+
if isNull locations then Error "Appsetting not set: ProbingLocations"
59+
else locations.Value |> Ok
60+
3661
/// Reads the 'RProvider.dll.config' file and gets the 'ProbingLocations'
3762
/// parameter from the configuration file. Resolves the directories and returns
3863
/// them as a list.
39-
let getProbingLocations() =
64+
let getProbingLocations() =
4065
try
41-
let root = getRProviderRuntimeAssembly() |> getAssemblyLocation
42-
let config = ConfigurationManager.OpenExeConfiguration(root)
43-
let pattern = config.AppSettings.Settings.["ProbingLocations"]
44-
if not <| isNull pattern then
45-
[ let pattern = pattern.Value.Split(';', ',') |> List.ofSeq
46-
for pat in pattern do
47-
let roots = [ Path.GetDirectoryName(root) ]
48-
for dir in roots |> searchDirectories (List.ofSeq (pat.Split('/','\\'))) do
49-
if Directory.Exists(dir) then yield dir ]
50-
else []
51-
with :? ConfigurationErrorsException | :? KeyNotFoundException -> []
52-
66+
let configLocation = getRProviderRuntimeAssembly() |> getConfigFileLocation
67+
Logging.logf "RProvider configuration file is %s" configLocation
68+
if String.IsNullOrEmpty configLocation then []
69+
else
70+
Logging.logf "Attempting to load config file '%s'" configLocation
71+
let pattern = probingLocationsFromXmlConfig configLocation
72+
match pattern with
73+
| Ok pattern ->
74+
[ let pattern = pattern.Split(';', ',') |> List.ofSeq
75+
for pat in pattern do
76+
let roots = [ Path.GetDirectoryName(configLocation) ]
77+
for dir in roots |> searchDirectories (List.ofSeq (pat.Split('/','\\'))) do
78+
if Directory.Exists(dir) then yield dir ]
79+
| Error _ -> []
80+
with :? KeyNotFoundException -> []
5381

5482
/// Given an assembly name, try to find it in either assemblies
5583
/// loaded in the current AppDomain, or in one of the specified

src/RProvider.Runtime/RInterop.fs

+7-4
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,23 @@ module internal RInteropInternal =
9999
lazy
100100
// Look for plugins co-located with RProvider.dll
101101
let assem = typeof<IConvertToR<_>>.Assembly
102-
let assemblyLocation = AppContext.BaseDirectory
102+
let assemblyLocation =
103+
if String.IsNullOrEmpty assem.Location
104+
then AppContext.BaseDirectory
105+
else assem.Location
103106
Logging.logf "[DEBUG] MEF Container 1: RProvider.dll is at %s" assemblyLocation
104107

105108
let dirs = getProbingLocations()
106-
Logging.logf "[DEBUG] MEF Container 2: %O" assemblyLocation
109+
Logging.logf "[DEBUG] MEF Container 2: Probing locations = %A" dirs
107110
let catalogs : seq<Primitives.ComposablePartCatalog> =
108111
seq { yield upcast new DirectoryCatalog(Path.GetDirectoryName assemblyLocation,"*.Plugin.dll")
109112
for d in dirs do
110113
yield upcast new DirectoryCatalog(d,"*.Plugin.dll")
111114
yield upcast new AssemblyCatalog(assem) }
112-
Logging.logf "[DEBUG] MEF Container 3: %O" catalogs
115+
Logging.logf "[DEBUG] MEF Container 3: Catalog count = %O" (catalogs.Count())
113116
new CompositionContainer(new AggregateCatalog(catalogs))
114117

115-
let internal toRConv = Collections.Generic.Dictionary<Type, REngine -> obj -> SymbolicExpression>()
118+
let internal toRConv = Dictionary<Type, REngine -> obj -> SymbolicExpression>()
116119

117120
/// Register a function that will convert from a specific type to a value in R.
118121
/// Alternatively, you can build a MEF plugin that exports IConvertToR.

src/RProvider.Runtime/paket.references

-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@ Newtonsoft.Json
22
R.NET
33
R.NET.FSharp
44
System.ComponentModel.Composition
5-
System.Configuration.ConfigurationManager
65
System.Reflection.MetadataLoadContext
76
PipeMethodCalls

src/RProvider.Server/Program.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ let main argv =
6060
Logging.logf "Starting 'RProvider.Server' with arguments '%A'" argv
6161

6262
// When RProvider is installed via NuGet, the RDotNet assembly and plugins
63-
// will appear typically in "../../*/lib/net40". To support this, we look at
63+
// will appear typically in "../../*/lib/netstandard*.*". To support this, we look at
6464
// RProvider.dll.config which has this pattern in custom key "ProbingLocations".
6565
// Here, we resolve assemblies by looking into the specified search paths.
6666
AppDomain.CurrentDomain.add_AssemblyResolve(fun source args ->

src/RProvider.Server/RProvider.Server.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
77
<PublishSingleFile>true</PublishSingleFile>
88
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
9+
<RollForward>LatestMajor</RollForward>
910
</PropertyGroup>
1011
<ItemGroup>
1112
<Compile Include="../Common/AssemblyInfo.fs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
<appSettings>
4+
<add key="ProbingLocations" value="../../../../*/*/lib/netstandard1.2;../../../../*/*/lib/netstandard2.0;../../../../*/*/lib/netstandard2.1;../../../../*/*/lib/net5.0" />
5+
</appSettings>
6+
</configuration>

src/RProvider/RProvider.fsproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<None Include="bin/Release/net5.0/server/**" Pack="true" PackagePath="/lib/net5.0/server/">
2222
<PackageCopyToOutput>true</PackageCopyToOutput>
2323
</None>
24-
<None Include="RProvider.dll.config" Pack="true" PackagePath="/lib/net5.0/">
24+
<None Include="RProvider.Runtime.dll.config" Pack="true" PackagePath="/lib/net5.0/">
2525
<PackageCopyToOutput>true</PackageCopyToOutput>
2626
</None>
2727
</ItemGroup>

src/RProvider/paket.references

-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ PipeMethodCalls
44
R.NET
55
R.NET.FSharp
66
System.ComponentModel.Composition
7-
System.Configuration.ConfigurationManager
87
System.Reflection.MetadataLoadContext
+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
R.NET
22
R.NET.FSharp
3-
System.ComponentModel.Composition
4-
System.Configuration.ConfigurationManager
3+
System.ComponentModel.Composition

0 commit comments

Comments
 (0)