diff --git a/RInterop/CommandLineOptions.cs b/RInterop/CommandLineOptions.cs index 5125067..19fe999 100644 --- a/RInterop/CommandLineOptions.cs +++ b/RInterop/CommandLineOptions.cs @@ -1,15 +1,16 @@ using CommandLine; +using System.Globalization; using System.IO; namespace RInterop { public class CommandLineOptions { - [Option('s', "schema", Required = false, + [Option('s', "schema", Required = true, HelpText = "Path to schema binary file containing types to serialize and deserialize input data and output data sent to and received from the R package, respectively")] public string SchemaBinaryPath { get; set; } - [Option('r', "rpackage", Required = false, + [Option('r', "rpackage", Required = true, HelpText = "Path to R package file containing statistical functions (optional if packages are already installed)")] public string RPackagePath { get; set; } @@ -19,20 +20,21 @@ namespace RInterop public static bool ParseArguments(string[] args, CommandLineOptions options) { + var logger = DependencyFactory.Resolve(); if (!Parser.Default.ParseArguments(args, options)) { return false; } - - if (!string.IsNullOrEmpty(options.RPackagePath) && !File.Exists(options.RPackagePath)) + + if (!File.Exists(options.RPackagePath)) { - DependencyFactory.Resolve().LogInformation("RPackagePath {0} not found", options.RPackagePath); + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "RPackage {0} not found", options.RPackagePath)); return false; } - if (!string.IsNullOrEmpty(options.SchemaBinaryPath) && !File.Exists(options.SchemaBinaryPath)) + if (!File.Exists(options.SchemaBinaryPath)) { - DependencyFactory.Resolve().LogInformation("SchemaBinaryPath {0} not found", options.SchemaBinaryPath); + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Schema binary {0} not found", options.SchemaBinaryPath)); return false; } diff --git a/RInterop/Config.cs b/RInterop/Config.cs index 3b504aa..98fbd70 100644 --- a/RInterop/Config.cs +++ b/RInterop/Config.cs @@ -5,8 +5,6 @@ public static string SchemaBinaryPath { get; set; } public static string RPackageName { get; set; } - - public static string RPackagePath { get; set; } public static SerializationTypeMaps SerializationTypeMaps { get; set; } } diff --git a/RInterop/DependencyFactory.cs b/RInterop/DependencyFactory.cs index ed95d2b..8ee9952 100644 --- a/RInterop/DependencyFactory.cs +++ b/RInterop/DependencyFactory.cs @@ -23,7 +23,7 @@ namespace RInterop .Resolve("TraceLoggerFactory") .Create("RInterop")); - Resolve().LogInformation("Type registrations completed"); + Resolve().LogInformation("Completed registering dependencies"); } public static T Resolve() diff --git a/RInterop/GlobalAssemblyInfo.cs b/RInterop/GlobalAssemblyInfo.cs new file mode 100644 index 0000000..8f0115d --- /dev/null +++ b/RInterop/GlobalAssemblyInfo.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +[assembly: System.Reflection.AssemblyCopyright("Copyright (c) Microsoft Corporation 2016")] +[assembly: System.CLSCompliant(false)] +[assembly: System.Reflection.AssemblyVersion("1.1.41577.4")] +[assembly: System.Reflection.AssemblyFileVersion("1.1.41577.4")] + + diff --git a/RInterop/Program.cs b/RInterop/Program.cs index 08f24e3..0ded1bf 100644 --- a/RInterop/Program.cs +++ b/RInterop/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using System.ServiceModel; using System.ServiceModel.Description; @@ -12,15 +13,16 @@ namespace RInterop { ILogger logger = DependencyFactory.Resolve(); + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Working directory: {0}", Environment.CurrentDirectory)); + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Arguments: {0}", string.Join(" ", args))); + var options = new CommandLineOptions(); if (!CommandLineOptions.ParseArguments(args, options)) { - DependencyFactory.Resolve().LogInformation("Invalid arguments", args); + DependencyFactory.Resolve().LogInformation(string.Format(CultureInfo.InvariantCulture, "Invalid arguments {0}", string.Join(" ", args))); - logger.LogInformation(@"Usage"); - logger.LogInformation(@"RInterop.exe --s """" --r """""); - logger.LogInformation(@"Example:"); - logger.LogInformation(@"RInterop.exe --s ""C:\Temp\Schemas.dll"" --r ""C:\Temp\RPackage.zip"""); + logger.LogInformation(@"Usage: RInterop.exe --s """" --r """""); + logger.LogInformation(@"Example: RInterop.exe --s ""C:\Temp\Schemas.dll"" --r ""C:\Temp\RPackage.zip"""); logger.LogInformation("Press any key to continue..."); Console.ReadLine(); return; @@ -36,13 +38,13 @@ namespace RInterop logger.LogInformation("Exception while extracting R package name: {0}", exception); return; } - - Config.RPackagePath = options.RPackagePath; + Config.SchemaBinaryPath = options.SchemaBinaryPath; + REngineWrapper.InstallPackages(options.RPackagePath, options.SchemaBinaryPath); StartService(); } - + private static void StartService() { EventWaitHandle startedEvent = new EventWaitHandle(false, EventResetMode.ManualReset, @"Global\RInteropStarted"); @@ -81,7 +83,7 @@ namespace RInterop host.AddServiceEndpoint(typeof(IR), new NetNamedPipeBinding(), ""); host.Open(); - + startedEvent.Set(); DependencyFactory.Resolve().LogInformation("Service is available. Press to exit."); diff --git a/RInterop/Properties/AssemblyInfo.cs b/RInterop/Properties/AssemblyInfo.cs index adc6fec..cb07273 100644 --- a/RInterop/Properties/AssemblyInfo.cs +++ b/RInterop/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ using System.Runtime.InteropServices; // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("RInterop")] -[assembly: AssemblyDescription("Abstraction layer for communicating with R")] +[assembly: AssemblyDescription("Statistical A/B testing library")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("RInterop")] -[assembly: AssemblyCopyright("Copyright © 2016")] +//[assembly: AssemblyCopyright("Copyright © 2016")] // Added in generated GlobalAssemblyInfo.cs file [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.4.7.0")] -[assembly: AssemblyFileVersion("0.4.7.0")] +//[assembly: AssemblyVersion("0.4.7.0")] // Added in generated GlobalAssemblyInfo.cs file +//[assembly: AssemblyFileVersion("0.4.7.0")] // Added in generated GlobalAssemblyInfo.cs file diff --git a/RInterop/R.cs b/RInterop/R.cs index 98ac694..1232fa8 100644 --- a/RInterop/R.cs +++ b/RInterop/R.cs @@ -62,12 +62,12 @@ namespace RInterop } catch (Exception e) { - _logger.LogInformation("Exception during evaluation {0}", e); + _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Exception during evaluation {0}", e)); return null; } } } - + public void Initialize(Dictionary inputTypeMap, Dictionary outputTypeMap) { Assembly assembly = Assembly.LoadFrom(Config.SchemaBinaryPath); @@ -77,6 +77,7 @@ namespace RInterop Config.SerializationTypeMaps.InputTypeMap[key] = assembly .GetTypes() .First(a => a.FullName.Equals(inputTypeMap[key])); + _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Got input type mapping: {0} > {1}", key, inputTypeMap[key])); } foreach (string key in outputTypeMap.Keys) @@ -84,6 +85,7 @@ namespace RInterop Config.SerializationTypeMaps.OutputTypeMap[key] = assembly .GetTypes() .First(a => a.FullName.Equals(outputTypeMap[key])); + _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Got output type mapping: {0} > {1}", key, outputTypeMap[key])); } } } diff --git a/RInterop/REngineWrapper.cs b/RInterop/REngineWrapper.cs index b3aeee6..1edf24a 100644 --- a/RInterop/REngineWrapper.cs +++ b/RInterop/REngineWrapper.cs @@ -21,28 +21,9 @@ namespace RInterop { if (_engine == null) { + var logger = DependencyFactory.Resolve(); + _engine = REngine.GetInstance(); - - _engine.Evaluate(string.Format( - CultureInfo.InvariantCulture, - @"install.packages(""{0}"", verbose = TRUE, dependencies = TRUE, type = ""win.binary"")", - Config.RPackagePath.Replace(@"\", @"\\"))); - - List imports = _engine - .Evaluate(@"packageDescription(""" + Config.RPackageName + @""")$Imports") - .AsCharacter() - .First() - .Replace(" ", string.Empty) - .Split(',') - .ToList(); - - foreach (string packageName in imports) - { - _engine.Evaluate(string.Format(CultureInfo.InvariantCulture, - @"if (!require(""{0}"")) -install.packages(""{0}"", repo = ""https://cran.rstudio.com/"", dependencies = TRUE)", - packageName)); - } } } } @@ -50,5 +31,35 @@ install.packages(""{0}"", repo = ""https://cran.rstudio.com/"", dependencies = T return _engine; } } + + public static void InstallPackages(string rPackagePath, string schemaBinaryPath) + { + var engine = REngineWrapper.REngine; + var logger = DependencyFactory.Resolve(); + engine.Evaluate(string.Format( + CultureInfo.InvariantCulture, + @"install.packages(""{0}"", verbose = TRUE, dependencies = TRUE, type = ""win.binary"")", + rPackagePath.Replace(@"\", @"\\"))); + + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Installed R package {0}", rPackagePath)); + + List imports = engine + .Evaluate(@"packageDescription(""" + Config.RPackageName + @""")$Imports") + .AsCharacter() + .First() + .Replace(" ", string.Empty) + .Split(',') + .ToList(); + + foreach (string packageName in imports) + { + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Installing dependent R package {0}", packageName)); + engine.Evaluate(string.Format(CultureInfo.InvariantCulture, + @"if (!require(""{0}"")) +install.packages(""{0}"", repo = ""https://cran.rstudio.com/"", dependencies = TRUE)", + packageName)); + logger.LogInformation(string.Format(CultureInfo.InvariantCulture, "Installed R package {0}", packageName)); + } + } } } diff --git a/RInterop/RInterop.csproj b/RInterop/RInterop.csproj index 41b9ca2..bc72485 100644 --- a/RInterop/RInterop.csproj +++ b/RInterop/RInterop.csproj @@ -111,6 +111,7 @@ + @@ -143,8 +144,34 @@ + + + + 1 + 1 + + + + + $([MSBuild]::Modulo($([System.DateTime]::UtcNow.ToString("yyyyMMdd")), 65536)) + 4 + $(Build).$(Revision) + + + + + $([MSBuild]::Modulo($([System.Math]::Floor($(BUILD_BUILDNUMBER))), 65536)) + $(BUILD_BUILDNUMBER.Substring($([MSBuild]::Add($(BUILD_BUILDNUMBER.IndexOf('.')), 1)))) + + + + + + + +