From e39984f9bd23887180a64de7be603ca2578a6469 Mon Sep 17 00:00:00 2001 From: J-M Date: Thu, 11 Sep 2014 17:48:10 +1000 Subject: [PATCH 01/10] Add a unit test checking the root cause for issue 129 (check that C stack checking is disabled) --- RDotNet.Tests/REngineTest.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RDotNet.Tests/REngineTest.cs b/RDotNet.Tests/REngineTest.cs index eea64e24..73c8fdfb 100644 --- a/RDotNet.Tests/REngineTest.cs +++ b/RDotNet.Tests/REngineTest.cs @@ -7,6 +7,15 @@ namespace RDotNet { internal class REngineTest : RDotNetTestFixture { + + [Test] + public void TestCStackCheckDisabled() + { + var engine = this.Engine; + var cStackLimit = engine.GetDangerousInt32("R_CStackLimit"); + Assert.AreEqual(-1, cStackLimit); + } + [Test] public void TestSetCommandLineArguments() { From 22607860527fa5f9e20c3e45c3bca02ccba52722 Mon Sep 17 00:00:00 2001 From: J-M Date: Fri, 19 Sep 2014 17:58:14 +1000 Subject: [PATCH 02/10] Refactor to allow code reuse in rClr --- R.NET.sln | 10 +-- R.NET/ComplexMatrix.cs | 9 +-- R.NET/ComplexVector.cs | 5 +- R.NET/Environment.cs | 3 +- R.NET/IntegerMatrix.cs | 5 +- R.NET/LogicalMatrix.cs | 5 +- R.NET/NumericMatrix.cs | 5 +- R.NET/Properties/VersionInfo.cs | 4 +- R.NET/RDotNet.csproj | 4 +- R.NET/REngine.cs | 3 +- R.NET/RawMatrix.cs | 5 +- R.NET/Symbol.cs | 5 +- R.NET/SymbolicExpression.cs | 5 +- R.NET/Utility.cs | 107 -------------------------------- RDotNet.FSharp.nuspec | 6 +- RDotNet.FSharp/AssemblyInfo.fs | 4 +- RDotNet.Graphics.nuspec | 6 +- RDotNet.Tests/UtilityTests.cs | 3 +- RDotNet.nuspec | 4 +- build/build_R.NET_nuget.cmd | 1 + 20 files changed, 51 insertions(+), 148 deletions(-) delete mode 100644 R.NET/Utility.cs diff --git a/R.NET.sln b/R.NET.sln index 42c57bd6..eca5e59b 100644 --- a/R.NET.sln +++ b/R.NET.sln @@ -1,11 +1,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet", "R.NET\RDotNet.csproj", "{0923E1A0-2032-4997-AB73-49E42C4034A9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet.NativeLibrary", "RDotNet.NativeLibrary\RDotNet.NativeLibrary.csproj", "{2A089A59-0F22-4484-B442-0FE8BDA10879}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet.Graphics", "Graphics\RDotNet.Graphics.csproj", "{BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,10 +20,6 @@ Global {2A089A59-0F22-4484-B442-0FE8BDA10879}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A089A59-0F22-4484-B442-0FE8BDA10879}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A089A59-0F22-4484-B442-0FE8BDA10879}.Release|Any CPU.Build.0 = Release|Any CPU - {BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/R.NET/ComplexMatrix.cs b/R.NET/ComplexMatrix.cs index 6e0b4f32..85f2f93a 100644 --- a/R.NET/ComplexMatrix.cs +++ b/R.NET/ComplexMatrix.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Numerics; using System.Runtime.InteropServices; @@ -95,8 +96,8 @@ protected internal ComplexMatrix(REngine engine, IntPtr coerced) /// protected override void InitMatrixFastDirect(Complex[,] matrix) { - var vectorCplx = Utility.ArrayConvertOneDim(matrix); - var data = Utility.SerializeComplexToDouble(vectorCplx); + var vectorCplx = ArrayConverter.ArrayConvertOneDim(matrix); + var data = RTypesUtil.SerializeComplexToDouble(vectorCplx); Marshal.Copy(data, 0, DataPointer, data.Length); } @@ -109,8 +110,8 @@ protected override void InitMatrixFastDirect(Complex[,] matrix) int n = this.ItemCount; var data = new double[2 * n]; Marshal.Copy(DataPointer, data, 0, 2 * n); - var oneDim = Utility.DeserializeComplexFromDouble(data); - return Utility.ArrayConvertAllTwoDim(oneDim, this.RowCount, this.ColumnCount); + var oneDim = RTypesUtil.DeserializeComplexFromDouble(data); + return ArrayConverter.ArrayConvertAllTwoDim(oneDim, this.RowCount, this.ColumnCount); } /// diff --git a/R.NET/ComplexVector.cs b/R.NET/ComplexVector.cs index b5c84446..9358c67c 100644 --- a/R.NET/ComplexVector.cs +++ b/R.NET/ComplexVector.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Collections.Generic; using System.Numerics; @@ -89,7 +90,7 @@ protected override Complex[] GetArrayFast() int n = this.Length; var data = new double[2*n]; Marshal.Copy(DataPointer, data, 0, 2 * n); - return Utility.DeserializeComplexFromDouble(data); + return RTypesUtil.DeserializeComplexFromDouble(data); } /// @@ -97,7 +98,7 @@ protected override Complex[] GetArrayFast() /// protected override void SetVectorDirect(Complex[] values) { - double[] data = Utility.SerializeComplexToDouble(values); + double[] data = RTypesUtil.SerializeComplexToDouble(values); IntPtr pointer = IntPtr.Add(DataPointer, 0); Marshal.Copy(data, 0, pointer, data.Length); } diff --git a/R.NET/Environment.cs b/R.NET/Environment.cs index 8ea8e691..df10bb01 100644 --- a/R.NET/Environment.cs +++ b/R.NET/Environment.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Runtime.InteropServices; @@ -36,7 +37,7 @@ public REnvironment Parent { SEXPREC sexp = GetInternalStructure(); IntPtr parent = sexp.envsxp.enclos; - return Engine.CheckNil(parent) ? null : new REnvironment(Engine, parent); + return Engine.EqualsRNilValue(parent) ? null : new REnvironment(Engine, parent); } } diff --git a/R.NET/IntegerMatrix.cs b/R.NET/IntegerMatrix.cs index a69c8d5b..e581c2aa 100644 --- a/R.NET/IntegerMatrix.cs +++ b/R.NET/IntegerMatrix.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Runtime.InteropServices; using System.Security.Permissions; @@ -92,7 +93,7 @@ protected internal IntegerMatrix(REngine engine, IntPtr coerced) /// protected override void InitMatrixFastDirect(int[,] matrix) { - var values = Utility.ArrayConvertOneDim(matrix); + var values = ArrayConverter.ArrayConvertOneDim(matrix); Marshal.Copy(values, 0, DataPointer, values.Length); } @@ -104,7 +105,7 @@ protected override void InitMatrixFastDirect(int[,] matrix) { var values = new int[this.ItemCount]; Marshal.Copy(DataPointer, values, 0, values.Length); - return Utility.ArrayConvertAllTwoDim(values, this.RowCount, this.ColumnCount); + return ArrayConverter.ArrayConvertAllTwoDim(values, this.RowCount, this.ColumnCount); } /// diff --git a/R.NET/LogicalMatrix.cs b/R.NET/LogicalMatrix.cs index 802f65c1..78b9d140 100644 --- a/R.NET/LogicalMatrix.cs +++ b/R.NET/LogicalMatrix.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Runtime.InteropServices; using System.Security.Permissions; @@ -91,7 +92,7 @@ protected internal LogicalMatrix(REngine engine, IntPtr coerced) /// protected override void InitMatrixFastDirect(bool[,] matrix) { - var intValues = Utility.ArrayConvertAllOneDim(matrix, Convert.ToInt32); + var intValues = ArrayConverter.ArrayConvertAllOneDim(matrix, Convert.ToInt32); Marshal.Copy(intValues, 0, DataPointer, intValues.Length); } @@ -103,7 +104,7 @@ protected override void InitMatrixFastDirect(bool[,] matrix) { int[] intValues = new int[this.ItemCount]; Marshal.Copy(DataPointer, intValues, 0, intValues.Length); - return Utility.ArrayConvertAllTwoDim(intValues, Convert.ToBoolean, this.RowCount, this.ColumnCount); + return ArrayConverter.ArrayConvertAllTwoDim(intValues, Convert.ToBoolean, this.RowCount, this.ColumnCount); } /// diff --git a/R.NET/NumericMatrix.cs b/R.NET/NumericMatrix.cs index 73992706..d6053ad2 100644 --- a/R.NET/NumericMatrix.cs +++ b/R.NET/NumericMatrix.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Runtime.InteropServices; using System.Security.Permissions; @@ -100,7 +101,7 @@ protected internal NumericMatrix(REngine engine, IntPtr coerced) /// protected override void InitMatrixFastDirect(double[,] matrix) { - var values = Utility.ArrayConvertOneDim(matrix); + var values = ArrayConverter.ArrayConvertOneDim(matrix); Marshal.Copy(values, 0, DataPointer, values.Length); } @@ -112,7 +113,7 @@ protected override void InitMatrixFastDirect(double[,] matrix) { var values = new double[this.ItemCount]; Marshal.Copy(DataPointer, values, 0, values.Length); - return Utility.ArrayConvertAllTwoDim(values, this.RowCount, this.ColumnCount); + return ArrayConverter.ArrayConvertAllTwoDim(values, this.RowCount, this.ColumnCount); } /// diff --git a/R.NET/Properties/VersionInfo.cs b/R.NET/Properties/VersionInfo.cs index 2b975e4d..3b4bb186 100644 --- a/R.NET/Properties/VersionInfo.cs +++ b/R.NET/Properties/VersionInfo.cs @@ -10,5 +10,5 @@ // 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("1.5.16.*")] -[assembly: AssemblyInformationalVersion("1.5.16")] \ No newline at end of file +[assembly: AssemblyVersion("1.5.17.*")] +[assembly: AssemblyInformationalVersion("1.5.17")] \ No newline at end of file diff --git a/R.NET/RDotNet.csproj b/R.NET/RDotNet.csproj index 5641c387..fd1be208 100644 --- a/R.NET/RDotNet.csproj +++ b/R.NET/RDotNet.csproj @@ -115,7 +115,9 @@ - + + + diff --git a/R.NET/REngine.cs b/R.NET/REngine.cs index 02fcb0de..bab57fad 100644 --- a/R.NET/REngine.cs +++ b/R.NET/REngine.cs @@ -1,6 +1,7 @@ using RDotNet.Devices; using RDotNet.Internals; using RDotNet.NativeLibrary; +using RDotNet.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -758,7 +759,7 @@ internal string LastErrorMessage public void SetCommandLineArguments(string[] args) { CheckEngineIsRunning(); - var newArgs = Utility.AddFirst(ID, args); + var newArgs = ArrayConverter.Prepend(ID, args); GetFunction()(newArgs.Length, newArgs); } diff --git a/R.NET/RawMatrix.cs b/R.NET/RawMatrix.cs index 6ceefa4e..60d530ee 100644 --- a/R.NET/RawMatrix.cs +++ b/R.NET/RawMatrix.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Runtime.InteropServices; using System.Security.Permissions; @@ -89,7 +90,7 @@ protected internal RawMatrix(REngine engine, IntPtr coerced) /// protected override void InitMatrixFastDirect(byte[,] matrix) { - var values = Utility.ArrayConvertOneDim(matrix); + var values = ArrayConverter.ArrayConvertOneDim(matrix); Marshal.Copy(values, 0, DataPointer, values.Length); } @@ -101,7 +102,7 @@ protected override void InitMatrixFastDirect(byte[,] matrix) { byte[] values = new byte[this.ItemCount]; Marshal.Copy(DataPointer, values, 0, values.Length); - return Utility.ArrayConvertAllTwoDim(values, this.RowCount, this.ColumnCount); + return ArrayConverter.ArrayConvertAllTwoDim(values, this.RowCount, this.ColumnCount); } /// diff --git a/R.NET/Symbol.cs b/R.NET/Symbol.cs index 834fb688..91670727 100644 --- a/R.NET/Symbol.cs +++ b/R.NET/Symbol.cs @@ -1,4 +1,5 @@ using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Runtime.InteropServices; @@ -44,7 +45,7 @@ public SymbolicExpression Internal get { SEXPREC sexp = GetInternalStructure(); - if (Engine.CheckNil(sexp.symsxp.value)) + if (Engine.EqualsRNilValue(sexp.symsxp.value)) { return null; } @@ -60,7 +61,7 @@ public SymbolicExpression Value get { SEXPREC sexp = GetInternalStructure(); - if (Engine.CheckNil(sexp.symsxp.value)) + if (Engine.EqualsRNilValue(sexp.symsxp.value)) { return null; } diff --git a/R.NET/SymbolicExpression.cs b/R.NET/SymbolicExpression.cs index 9b7a4219..01b6167d 100644 --- a/R.NET/SymbolicExpression.cs +++ b/R.NET/SymbolicExpression.cs @@ -1,5 +1,6 @@ using RDotNet.Dynamic; using RDotNet.Internals; +using RDotNet.Utilities; using System; using System.Diagnostics; using System.Dynamic; @@ -156,7 +157,7 @@ public SymbolicExpression GetAttribute(string attributeName) IntPtr installedName = this.GetFunction()(attributeName); IntPtr attribute = this.GetFunction()(handle, installedName); - if (Engine.CheckNil(attribute)) + if (Engine.EqualsRNilValue(attribute)) { return null; } @@ -175,7 +176,7 @@ internal SymbolicExpression GetAttribute(SymbolicExpression symbol) } IntPtr attribute = this.GetFunction()(handle, symbol.handle); - if (Engine.CheckNil(attribute)) + if (Engine.EqualsRNilValue(attribute)) { return null; } diff --git a/R.NET/Utility.cs b/R.NET/Utility.cs deleted file mode 100644 index 8c959000..00000000 --- a/R.NET/Utility.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Numerics; - -namespace RDotNet -{ - /// - /// An internal helper class to convert types of arrays, primarily for data operations necessary for .NET types to/from R concepts. - /// - internal static class Utility - { - public static T[] AddFirst(T value, T[] array) - { - if (array == null) - { - return new[] { value }; - } - var newArray = new T[array.Length + 1]; - newArray[0] = value; - Array.Copy(array, 0, newArray, 1, array.Length); - return newArray; - } - - internal static bool CheckNil(this REngine engine, IntPtr pointer) - { - return engine.NilValue.DangerousGetHandle() == pointer; - } - - internal static bool CheckUnbound(this REngine engine, IntPtr pointer) - { - return engine.UnboundValue.DangerousGetHandle() == pointer; - } - - internal static U[,] ArrayConvertAll(T[,] array, Func fun) - { - int rows = array.GetLength(0); - int cols = array.GetLength(1); - U[,] res = new U[rows, cols]; - for (int i = 0; i < rows; i++) - for (int j = 0; j < cols; j++) - res[i, j] = fun(array[i, j]); - return res; - } - - internal static U[] ArrayConvertAllOneDim(T[,] array, Func fun) - { - int rows = array.GetLength(0); - int cols = array.GetLength(1); - U[] res = new U[rows * cols]; - for (int i = 0; i < rows; i++) - for (int j = 0; j < cols; j++) - res[rows * j + i] = fun(array[i, j]); - return res; - } - - // TODO: probably room for extension methods around Matrix inheritors - internal static U[,] ArrayConvertAllTwoDim(T[] array, Func fun, int rows, int cols) - { - U[,] res = new U[rows,cols]; - for (int i = 0; i < rows; i++) - for (int j = 0; j < cols; j++) - res[i, j] = fun(array[rows * j + i]); - return res; - } - - internal static U[] ArrayConvertOneDim(U[,] array) - { - return ArrayConvertAllOneDim(array, value => value); - } - - // TODO: probably room for extension methods around Matrix inheritors - internal static U[,] ArrayConvertAllTwoDim(U[] array, int rows, int cols) - { - return ArrayConvertAllTwoDim(array, value => value, rows, cols); - } - - internal static double[] SerializeComplexToDouble(Complex[] values) - { - var data = new double[2 * values.Length]; - for (int i = 0; i < values.Length; i++) - { - data[2 * i] = values[i].Real; - data[2 * i + 1] = values[i].Imaginary; - } - return data; - } - - internal static Complex[] DeserializeComplexFromDouble(double[] data) - { - int dblLen = data.Length; - if (dblLen % 2 != 0) throw new ArgumentException("Serialized definition of complexes must be of even length"); - int n = dblLen / 2; - var res = new Complex[n]; - for (int i = 0; i < n; i++) - res[i] = new Complex(data[2 * i], data[2 * i + 1]); - return res; - } - - internal static T[] Subset(T[] array, int from, int to) - { - var res = new T[(to - from) + 1]; - for (int i = 0; i < res.Length; i++) - res[i] = array[from + i]; - return res; - } - - } -} \ No newline at end of file diff --git a/RDotNet.FSharp.nuspec b/RDotNet.FSharp.nuspec index 0e5d5d2b..f41e8561 100644 --- a/RDotNet.FSharp.nuspec +++ b/RDotNet.FSharp.nuspec @@ -3,8 +3,8 @@ R.NET.Community.FSharp R.NET F# Utility - 0.1.9 - Update to R.NET.Community 1.5.16, which disables the C Stack checking in R, necessary to run R.NET on *nix platforms + 0.1.10 + Under development RecycleBin kos59125 http://rdotnet.codeplex.com/license @@ -17,7 +17,7 @@ - + R R.NET F# statistics diff --git a/RDotNet.FSharp/AssemblyInfo.fs b/RDotNet.FSharp/AssemblyInfo.fs index 9bbcd011..43a4779a 100644 --- a/RDotNet.FSharp/AssemblyInfo.fs +++ b/RDotNet.FSharp/AssemblyInfo.fs @@ -8,7 +8,7 @@ open System.Runtime.InteropServices [] [] [] -[] -[] +[] +[] () \ No newline at end of file diff --git a/RDotNet.Graphics.nuspec b/RDotNet.Graphics.nuspec index 2d56fe81..be82e31e 100644 --- a/RDotNet.Graphics.nuspec +++ b/RDotNet.Graphics.nuspec @@ -1,8 +1,8 @@ - RDotNet.Graphics - 0.1-alpha + R.NET.Community.Graphics + 0.1.2-alpha R.NET Graphics Engine RecycleBin kos59125 @@ -16,7 +16,7 @@ - + R R.NET graphics diff --git a/RDotNet.Tests/UtilityTests.cs b/RDotNet.Tests/UtilityTests.cs index f945330a..af0affbb 100644 --- a/RDotNet.Tests/UtilityTests.cs +++ b/RDotNet.Tests/UtilityTests.cs @@ -1,5 +1,6 @@ using System.Numerics; using NUnit.Framework; +using RDotNet.Utilities; namespace RDotNet { @@ -9,7 +10,7 @@ public class UtilityTests [Test] public void CanSerializeComplexValues() { - var result = Utility.SerializeComplexToDouble( new[] {new Complex( 1, 0 ), new Complex( 0, 1 ), new Complex( 1, 1 )} ); + var result = RTypesUtil.SerializeComplexToDouble( new[] {new Complex( 1, 0 ), new Complex( 0, 1 ), new Complex( 1, 1 )} ); Assert.That( result, Is.EqualTo( new[] {1d, 0, 0, 1, 1, 1} ) ); } diff --git a/RDotNet.nuspec b/RDotNet.nuspec index 1a70aab2..c5e4613b 100644 --- a/RDotNet.nuspec +++ b/RDotNet.nuspec @@ -3,8 +3,8 @@ R.NET.Community R.NET - 1.5.16 - FIX: Disables the C Stack checking in R, necessary to run R.NET on *nix platforms. A previous fix for Windows unfortunately also broke the library for Linux. + 1.5.17 + Under development RecycleBin, jperraud kos59125 http://rdotnet.codeplex.com/license diff --git a/build/build_R.NET_nuget.cmd b/build/build_R.NET_nuget.cmd index 05daa4d5..769866b7 100644 --- a/build/build_R.NET_nuget.cmd +++ b/build/build_R.NET_nuget.cmd @@ -83,6 +83,7 @@ if "%BuildConfiguration%"=="Release" set nuspec_file=RDotNet.nuspec if "%BuildConfiguration%"=="Debug" set nuspec_file=RDotNet_debug.nuspec %nuget_exe% pack %rdotnet_dir%%nuspec_file% %pack_options% %nuget_exe% pack %rdotnet_dir%RDotNet.FSharp.nuspec %pack_options% +%nuget_exe% pack %rdotnet_dir%RDotNet.Graphics.nuspec %pack_options% :: %repo_dir%RDotNet.FSharp.0.*.nupkg xcopy %rdotnet_dir%build\*.nupkg %repo_dir% %COPYOPTIONS% goto completed From 73624cb8b6a4364458983d0105fcf21bd041e39a Mon Sep 17 00:00:00 2001 From: skyguy94 Date: Mon, 22 Sep 2014 13:02:45 -0500 Subject: [PATCH 03/10] Fixup for crash on exit due to dev.off() call --- RDotNet.Graphics/GraphicsDeviceAdapter.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/RDotNet.Graphics/GraphicsDeviceAdapter.cs b/RDotNet.Graphics/GraphicsDeviceAdapter.cs index a81d7cce..7d37c05b 100644 --- a/RDotNet.Graphics/GraphicsDeviceAdapter.cs +++ b/RDotNet.Graphics/GraphicsDeviceAdapter.cs @@ -226,10 +226,18 @@ private void Resize(out double left, out double right, out double bottom, out do private void Close(IntPtr dd) { - this.device.OnClosed(this.description); + device.OnClosed(description); + ClearDevDesc(); } - private bool ConfirmNewFrame(IntPtr dd) + private void ClearDevDesc() + { + var geDevDesc = (GEDevDesc) Marshal.PtrToStructure(gdd, typeof (GEDevDesc)); + geDevDesc.dev = IntPtr.Zero; + Marshal.StructureToPtr(geDevDesc, gdd, false); + } + + private bool ConfirmNewFrame(IntPtr dd) { return this.device.ConfirmNewFrame(this.description); } From 9619cfbdf1d3414f75491f0cb5247f6251e60973 Mon Sep 17 00:00:00 2001 From: skyguy94 Date: Mon, 22 Sep 2014 13:05:03 -0500 Subject: [PATCH 04/10] Added correct graphics proj ref --- R.NET.sln | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R.NET.sln b/R.NET.sln index 42c57bd6..30487315 100644 --- a/R.NET.sln +++ b/R.NET.sln @@ -1,10 +1,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet", "R.NET\RDotNet.csproj", "{0923E1A0-2032-4997-AB73-49E42C4034A9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet.NativeLibrary", "RDotNet.NativeLibrary\RDotNet.NativeLibrary.csproj", "{2A089A59-0F22-4484-B442-0FE8BDA10879}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet.Graphics", "Graphics\RDotNet.Graphics.csproj", "{BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDotNet.Graphics", "RDotNet.Graphics\RDotNet.Graphics.csproj", "{BCADF6CF-2D63-4BD7-BC28-F0AC4F98AE78}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 4f3d2b3c4365439716b211e6f3358d3761f63032 Mon Sep 17 00:00:00 2001 From: skyguy94 Date: Wed, 24 Sep 2014 13:43:40 -0500 Subject: [PATCH 05/10] Initial stab and reordering environment variable logic. --- R.NET/Devices/CharacterDeviceAdapter.cs | 12 +- RDotNet.Graphics/RDotNet.Graphics.csproj | 1 + RDotNet.NativeLibrary/NativeUtility.cs | 136 ++++++++++------------- 3 files changed, 61 insertions(+), 88 deletions(-) diff --git a/R.NET/Devices/CharacterDeviceAdapter.cs b/R.NET/Devices/CharacterDeviceAdapter.cs index 83d85f5d..a0b15cba 100644 --- a/R.NET/Devices/CharacterDeviceAdapter.cs +++ b/R.NET/Devices/CharacterDeviceAdapter.cs @@ -8,7 +8,7 @@ namespace RDotNet.Devices { - internal class CharacterDeviceAdapter : IDisposable + internal class CharacterDeviceAdapter { private readonly ICharacterDevice device; @@ -45,15 +45,6 @@ protected TDelegate GetFunction() where TDelegate : class return Engine.GetFunction(); } - #region IDisposable Members - - public void Dispose() - { - GC.KeepAlive(this); - } - - #endregion IDisposable Members - internal void Install(REngine engine, StartupParameter parameter) { this.engine = engine; @@ -74,7 +65,6 @@ private void SetupWindowsDevice(StartupParameter parameter) { if (parameter.RHome == null) { - //string rhome = Marshal.PtrToStringAnsi(Engine.GetFunction("get_R_HOME")()); string rhome = NativeUtility.GetRHomeEnvironmentVariable(); parameter.start.rhome = Marshal.StringToHGlobalAnsi(ConvertSeparatorToUnixStylePath(rhome)); } diff --git a/RDotNet.Graphics/RDotNet.Graphics.csproj b/RDotNet.Graphics/RDotNet.Graphics.csproj index 554dfa7b..696e3112 100644 --- a/RDotNet.Graphics/RDotNet.Graphics.csproj +++ b/RDotNet.Graphics/RDotNet.Graphics.csproj @@ -35,6 +35,7 @@ + diff --git a/RDotNet.NativeLibrary/NativeUtility.cs b/RDotNet.NativeLibrary/NativeUtility.cs index ed604355..4764646d 100644 --- a/RDotNet.NativeLibrary/NativeUtility.cs +++ b/RDotNet.NativeLibrary/NativeUtility.cs @@ -2,7 +2,6 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; -using System.Linq; using Microsoft.Win32; namespace RDotNet.NativeLibrary @@ -24,46 +23,55 @@ public static class NativeUtility /// The current platform. public static PlatformID GetPlatform() { - if (!curPlatform.HasValue) { - var platform = Environment.OSVersion.Platform; - if (platform != PlatformID.Unix) { - curPlatform = platform; - } else { - try { - var kernelName = ExecCommand("uname", "-s"); - curPlatform = (kernelName == "Darwin" ? PlatformID.MacOSX : platform); - } catch (Win32Exception) { // probably no PATH to uname. + if (!curPlatform.HasValue) + { + var platform = Environment.OSVersion.Platform; + if (platform != PlatformID.Unix) + { curPlatform = platform; - } - } - } - return curPlatform.Value; + } + else + { + try + { + var kernelName = ExecCommand("uname", "-s"); + curPlatform = (kernelName == "Darwin" ? PlatformID.MacOSX : platform); + } + catch (Win32Exception) + { + // probably no PATH to uname. + curPlatform = platform; + } + } + } + return curPlatform.Value; } - private static PlatformID? curPlatform = null; + private static PlatformID? curPlatform; - /// - /// Execute a command in a new process - /// - /// Process name e.g. "uname" - /// Arguments e.g. "-s" - /// The output of the command to the standard output stream - public static string ExecCommand(string processName, string arguments) - { - using (var uname = new Process()) { - uname.StartInfo.FileName = processName; - uname.StartInfo.Arguments = arguments; - uname.StartInfo.RedirectStandardOutput = true; - uname.StartInfo.UseShellExecute = false; - uname.StartInfo.CreateNoWindow = true; - uname.Start(); - var kernelName = uname.StandardOutput.ReadLine(); - uname.WaitForExit(); - return kernelName; - } - } + /// + /// Execute a command in a new process + /// + /// Process name e.g. "uname" + /// Arguments e.g. "-s" + /// The output of the command to the standard output stream + public static string ExecCommand(string processName, string arguments) + { + using (var uname = new Process()) + { + uname.StartInfo.FileName = processName; + uname.StartInfo.Arguments = arguments; + uname.StartInfo.RedirectStandardOutput = true; + uname.StartInfo.UseShellExecute = false; + uname.StartInfo.CreateNoWindow = true; + uname.Start(); + var kernelName = uname.StandardOutput.ReadLine(); + uname.WaitForExit(); + return kernelName; + } + } - /// + /// /// Sets the PATH and R_HOME environment variables if needed. /// /// The path of the directory containing the R native library. @@ -73,47 +81,25 @@ public static string ExecCommand(string processName, string arguments) /// /// This function has been designed to limit the tedium for users, while allowing custom settings for unusual installations. /// - public static void SetEnvironmentVariables(string rPath = null, string rHome = null) + public static void SetEnvironmentVariables(string rHome = null) { var platform = GetPlatform(); - if (rPath != null) - CheckDirExists(rPath); - if (rHome != null) - CheckDirExists(rHome); - if (rPath == null) - rPath = FindRPath(); - SetenvPrependToPath(rPath); + if (string.IsNullOrEmpty(rHome)) + { + rHome = GetRHomeEnvironmentVariable(); - if (string.IsNullOrEmpty(rHome)) - rHome = GetRHomeEnvironmentVariable(); - if (string.IsNullOrEmpty(rHome)) { - // R_HOME is neither specified by the user nor as an environmental variable. Rely on default locations specific to platforms - rHome = FindRHome(rPath); - } - if (string.IsNullOrEmpty(rHome)) - throw new NotSupportedException("R_HOME was not provided and could not be found by R.NET"); - else - { - // It is highly recommended to use the 8.3 short path format on windows. - // See the manual page of R.home function in R. Solves at least the issue R.NET 97. - if (platform == PlatformID.Win32NT) - rHome = WindowsLibraryLoader.GetShortPath(rHome); - if (!Directory.Exists(rHome)) - throw new DirectoryNotFoundException("Directory for R_HOME does not exist"); - Environment.SetEnvironmentVariable("R_HOME", rHome); - } - if (platform == PlatformID.Unix) { - // Let's check that LD_LIBRARY_PATH is set if this is a custom installation of R. - // Normally in an R session from a custom build/install we get something typically like: - // > Sys.getenv('LD_LIBRARY_PATH') - // [1] "/usr/local/lib/R/lib:/usr/local/lib:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server" - // The R script sets LD_LIBRARY_PATH before it starts the native executable under e.g. /usr/local/lib/R/bin/exec/R - // This would be useless to set LD_LIBRARY_PATH in the current function: - // it must be set as en env var BEFORE the process is started (see man page for dlopen) - // so all we can do is an intelligible error message for the user, explaining he needs to set the LD_LIBRARY_PATH env variable - // Let's delay the notification about a missing LD_LIBRARY_PATH till loading libR.so fails, if it does. - } + // It is highly recommended to use the 8.3 short path format on windows. + // See the manual page of R.home function in R. Solves at least the issue R.NET 97. + if (platform == PlatformID.Win32NT) + rHome = WindowsLibraryLoader.GetShortPath(rHome); + } + + + CheckDirExists(rHome); + CheckDirExists(rPath); + Environment.SetEnvironmentVariable(rPath, PrependToPath(rPath, envVarName)); + Environment.SetEnvironmentVariable("R_HOME", rHome); } /// @@ -206,10 +192,6 @@ public static string FindRPath() } } - private static void SetenvPrependToPath(string rPath, string envVarName="PATH") - { - Environment.SetEnvironmentVariable(envVarName, PrependToPath(rPath, envVarName)); - } private static string PrependToPath(string rPath, string envVarName = "PATH") { From 5414bed9bfe3eb8891ceeb3f923df989ce9d19e7 Mon Sep 17 00:00:00 2001 From: skyguy94 Date: Wed, 24 Sep 2014 17:07:53 -0500 Subject: [PATCH 06/10] R_HOME usage improvements --- R.NET/Devices/CharacterDeviceAdapter.cs | 2 +- R.NET/REngine.cs | 45 +- .../IDynamicLibraryLoader.cs | 3 +- RDotNet.NativeLibrary/NativeUtility.cs | 445 +++++++----------- RDotNet.NativeLibrary/UnmanagedDll.cs | 80 ++-- RDotNet.NativeLibrary/WindowsLibraryLoader.cs | 135 +++--- 6 files changed, 307 insertions(+), 403 deletions(-) diff --git a/R.NET/Devices/CharacterDeviceAdapter.cs b/R.NET/Devices/CharacterDeviceAdapter.cs index a0b15cba..1be6c077 100644 --- a/R.NET/Devices/CharacterDeviceAdapter.cs +++ b/R.NET/Devices/CharacterDeviceAdapter.cs @@ -65,7 +65,7 @@ private void SetupWindowsDevice(StartupParameter parameter) { if (parameter.RHome == null) { - string rhome = NativeUtility.GetRHomeEnvironmentVariable(); + string rhome = Environment.GetEnvironmentVariable("R_HOME"); parameter.start.rhome = Marshal.StringToHGlobalAnsi(ConvertSeparatorToUnixStylePath(rhome)); } if (parameter.Home == null) diff --git a/R.NET/REngine.cs b/R.NET/REngine.cs index 3f3e46b1..7b8fac94 100644 --- a/R.NET/REngine.cs +++ b/R.NET/REngine.cs @@ -162,8 +162,8 @@ public SymbolicExpression UnboundValue } } - private static bool environmentIsSet = false; - private static REngine engine = null; + private static bool environmentIsSet; + private static REngine _engine; /// /// Gets the name of the R engine instance (singleton). @@ -199,15 +199,17 @@ public static REngine GetInstance(string dll = null, bool initialize = true, Sta { if (!environmentIsSet) // should there be a warning? and how? SetEnvironmentVariables(); - if (engine == null) + if (_engine == null) { - engine = CreateInstance(EngineName, dll); - if (initialize) - engine.Initialize(parameter, device); + _engine = CreateInstance(EngineName, dll); + if (initialize) + { + _engine.Initialize(parameter, device); + } } - if (engine.Disposed) - throw new Exception("The single REngine instance has already been disposed of (i.e. shut down). Multiple engine restart is not possible."); - return engine; + if (_engine.Disposed) + throw new InvalidOperationException("The single REngine instance has already been disposed of (i.e. shut down). Multiple engine restart is not possible."); + return _engine; } /// @@ -226,13 +228,8 @@ private static REngine CreateInstance(string id, string dll = null) { throw new ArgumentException("Empty ID is not allowed.", "id"); } - //if (instances.ContainsKey(id)) - //{ - // throw new ArgumentException(); - //} dll = ProcessRDllFileName(dll); var engine = new REngine(id, dll); - //instances.Add(id, engine); return engine; } @@ -243,27 +240,22 @@ private static REngine CreateInstance(string id, string dll = null) /// A candidate for the file name of the R shared library protected static string ProcessRDllFileName(string dll) { - if (string.IsNullOrEmpty(dll)) - { - dll = NativeUtility.GetRDllFileName(); - } - return dll; + if (!string.IsNullOrEmpty(dll)) return dll; + return NativeUtility.GetRLibraryFileName(); } /// /// Perform the necessary setup for the PATH and R_HOME environment variables. /// - /// The path of the directory containing the R native library. - /// If null (default), this function tries to locate the path via the Windows registry, or commonly used locations on MacOS and Linux /// The path for R_HOME. If null (default), the function checks the R_HOME environment variable. If none is set, /// the function uses platform specific sensible default behaviors. /// /// This function has been designed to limit the tedium for users, while allowing custom settings for unusual installations. /// - public static void SetEnvironmentVariables(string rPath = null, string rHome = null) + public static void SetEnvironmentVariables(string rHome = null) { environmentIsSet = true; - NativeUtility.SetEnvironmentVariables(rPath: rPath, rHome: rHome); + NativeUtility.SetEnvironmentVariables(rHome); } /// @@ -776,13 +768,6 @@ protected override void Dispose(bool disposing) Disposed = true; } - if (disposing && this.adapter != null) - { - this.adapter.Dispose(); - this.adapter = null; - } - if (Disposed) - return; GC.KeepAlive(this.parameter); base.Dispose(disposing); } diff --git a/RDotNet.NativeLibrary/IDynamicLibraryLoader.cs b/RDotNet.NativeLibrary/IDynamicLibraryLoader.cs index 2819b223..8246e32f 100644 --- a/RDotNet.NativeLibrary/IDynamicLibraryLoader.cs +++ b/RDotNet.NativeLibrary/IDynamicLibraryLoader.cs @@ -37,5 +37,4 @@ public interface IDynamicLibraryLoader /// Handle to the native function IntPtr GetFunctionAddress(IntPtr hModule, string lpProcName); } -} - +} \ No newline at end of file diff --git a/RDotNet.NativeLibrary/NativeUtility.cs b/RDotNet.NativeLibrary/NativeUtility.cs index 4764646d..e57e338a 100644 --- a/RDotNet.NativeLibrary/NativeUtility.cs +++ b/RDotNet.NativeLibrary/NativeUtility.cs @@ -2,286 +2,183 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; -using Microsoft.Win32; namespace RDotNet.NativeLibrary { - /// - /// Collection of utility methods for operating systems. - /// - public static class NativeUtility - { - /// - /// Gets the platform on which the current process runs. - /// - /// - /// 's platform is not even on Mac OS X. - /// This method returns when the current process runs on Mac OS X. - /// This method uses UNIX's uname command to check the operating system, - /// so this method cannot check the OS correctly if the PATH environment variable is changed (will returns ). - /// - /// The current platform. - public static PlatformID GetPlatform() - { - if (!curPlatform.HasValue) - { - var platform = Environment.OSVersion.Platform; - if (platform != PlatformID.Unix) - { - curPlatform = platform; - } - else - { - try - { - var kernelName = ExecCommand("uname", "-s"); - curPlatform = (kernelName == "Darwin" ? PlatformID.MacOSX : platform); - } - catch (Win32Exception) - { - // probably no PATH to uname. - curPlatform = platform; - } - } - } - return curPlatform.Value; - } - - private static PlatformID? curPlatform; - - /// - /// Execute a command in a new process - /// - /// Process name e.g. "uname" - /// Arguments e.g. "-s" - /// The output of the command to the standard output stream - public static string ExecCommand(string processName, string arguments) - { - using (var uname = new Process()) - { - uname.StartInfo.FileName = processName; - uname.StartInfo.Arguments = arguments; - uname.StartInfo.RedirectStandardOutput = true; - uname.StartInfo.UseShellExecute = false; - uname.StartInfo.CreateNoWindow = true; - uname.Start(); - var kernelName = uname.StandardOutput.ReadLine(); - uname.WaitForExit(); - return kernelName; - } - } - - /// - /// Sets the PATH and R_HOME environment variables if needed. - /// - /// The path of the directory containing the R native library. - /// If null (default), this function tries to locate the path via the Windows registry, or commonly used locations on MacOS and Linux - /// The path for R_HOME. If null (default), the function checks the R_HOME environment variable. If none is set, - /// the function uses platform specific sensible default behaviors. - /// - /// This function has been designed to limit the tedium for users, while allowing custom settings for unusual installations. - /// - public static void SetEnvironmentVariables(string rHome = null) - { - var platform = GetPlatform(); - - if (string.IsNullOrEmpty(rHome)) - { - rHome = GetRHomeEnvironmentVariable(); - - // It is highly recommended to use the 8.3 short path format on windows. - // See the manual page of R.home function in R. Solves at least the issue R.NET 97. - if (platform == PlatformID.Win32NT) - rHome = WindowsLibraryLoader.GetShortPath(rHome); - } - - - CheckDirExists(rHome); - CheckDirExists(rPath); - Environment.SetEnvironmentVariable(rPath, PrependToPath(rPath, envVarName)); - Environment.SetEnvironmentVariable("R_HOME", rHome); - } - - /// - /// Gets the value, if any, of the R_HOME environment variable of the current process - /// - /// The value, or null if not set - public static string GetRHomeEnvironmentVariable() - { - return Environment.GetEnvironmentVariable("R_HOME"); - } - - /// - /// Try to locate the directory path to use for the R_HOME environment variable. This is used by R.NET by default; users may want to use it to diagnose problematic behaviors. - /// - /// Optional path to the directory containing the R shared library. This is ignored unless on a Unix platform (i.e. ignored on Windows and MacOS) - /// The path that R.NET found suitable as a candidate for the R_HOME environment - public static string FindRHome(string rPath=null) - { - var platform = GetPlatform(); - string rHome; - switch (platform) - { - case PlatformID.Win32NT: - // We need here to guess, process and set R_HOME - // Rf_initialize_R for gnuwin calls get_R_HOME which scans the windows registry and figures out R_HOME; however for - // unknown reasons in R.NET we end up with long path names, whereas R.exe ends up with the short, 8.3 path format. - // Blanks in the R_HOME environment variable cause trouble (e.g. for Rcpp), so we really must make sure - // that rHome is a short path format. Here we retrieve the path possibly in long format, and process to short format later on - // to capture all possible sources of R_HOME specifications - // Behavior added to fix issue - rHome = GetRhomeWin32NT(); - break; - case PlatformID.MacOSX: - rHome = "/Library/Frameworks/R.framework/Resources"; - break; - case PlatformID.Unix: - // if rPath is e.g. /usr/local/lib/R/lib/ , - rHome = Path.GetDirectoryName(rPath); - if (!rHome.EndsWith("R")) - // if rPath is e.g. /usr/lib/ (symlink) then default - rHome = "/usr/lib/R"; - break; - default: - throw new NotSupportedException(platform.ToString()); - } - return rHome; - } - - private static string GetRhomeWin32NT() - { - RegistryKey rCoreKey = GetRCoreRegistryKeyWin32(); - return GetRInstallPathFromRCoreKegKey(rCoreKey); - } - - private static void CheckDirExists(string rPath) - { - if (!Directory.Exists(rPath)) - throw new ArgumentException(string.Format("Specified directory not found: '{0}'", rPath)); - } - - /// - /// Attempt to find a suitable path to the R shared library. This is used by R.NET by default; users may want to use it to diagnose problematic behaviors. - /// - /// The path to the directory where the R shared library is expected to be - public static string FindRPath() - { - var shlibFilename = GetRDllFileName(); - var platform = GetPlatform(); - switch (platform) { - case PlatformID.Win32NT: - return FindRPathFromRegistry(); - case PlatformID.MacOSX: // TODO: is there a way to detect installations on MacOS - return "/Library/Frameworks/R.framework/Libraries"; - case PlatformID.Unix: - var rexepath = ExecCommand("which", "R"); // /usr/bin/R, or /usr/local/bin/R - if (!string.IsNullOrEmpty(rexepath)) { - var bindir = Path.GetDirectoryName(rexepath); // /usr/local/bin - // Trying to emulate the start of the R shell script - // /usr/local/lib/R/lib/libR.so - var libdir = Path.Combine(Path.GetDirectoryName(bindir), "lib", "R", "lib"); - if (File.Exists(Path.Combine(libdir, shlibFilename))) - return libdir; - libdir = Path.Combine(Path.GetDirectoryName(bindir), "lib64", "R", "lib"); - if (File.Exists(Path.Combine(libdir, shlibFilename))) - return libdir; + /// + /// Collection of utility methods for operating systems. + /// + public static class NativeUtility + { + /// + /// Gets the platform on which the current process runs. + /// + /// + /// 's platform is not even on Mac OS X. + /// This method returns when the current process runs on Mac OS X. + /// This method uses UNIX's uname command to check the operating system, + /// so this method cannot check the OS correctly if the PATH environment variable is changed (will returns ). + /// + /// The current platform. + public static PlatformID GetPlatform() + { + var platform = Environment.OSVersion.Platform; + if (platform == PlatformID.Unix) + { + try + { + var kernelName = ExecCommand("uname", "-s"); + platform = (kernelName == "Darwin" ? PlatformID.MacOSX : platform); + } + catch (Win32Exception) + { } } - return "/usr/lib"; - default: - throw new NotSupportedException(platform.ToString()); - } - } - - private static string PrependToPath(string rPath, string envVarName = "PATH") - { - var currentPathEnv = Environment.GetEnvironmentVariable(envVarName); - var paths = currentPathEnv.Split(new[]{Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries); - if (paths[0] == rPath) - return currentPathEnv; - return rPath + Path.PathSeparator + currentPathEnv; - } - - /// - /// Windows-only function; finds in the Windows registry the path to the most recently installed R binaries. - /// - /// The path, such as - public static string FindRPathFromRegistry() - { - CheckPlatformWin32(); - bool is64Bit = Environment.Is64BitProcess; - RegistryKey rCoreKey = GetRCoreRegistryKeyWin32(); - var installPath = GetRInstallPathFromRCoreKegKey(rCoreKey); - var currentVersion = new Version((string)rCoreKey.GetValue("Current Version")); - var bin = Path.Combine(installPath, "bin"); - // Up to 2.11.x, DLLs are installed in R_HOME\bin. - // From 2.12.0, DLLs are installed in the one level deeper directory. - return currentVersion < new Version(2, 12) ? bin : Path.Combine(bin, is64Bit ? "x64" : "i386"); - } - - private static string GetRInstallPathFromRCoreKegKey(RegistryKey rCoreKey) - { - var installPath = (string)rCoreKey.GetValue("InstallPath"); - return installPath; - } - - private static void CheckPlatformWin32() - { - if (Environment.OSVersion.Platform != PlatformID.Win32NT) - throw new NotSupportedException("This method is supported only on the Win32NT platform"); - } - - private static RegistryKey GetRCoreRegistryKeyWin32() - { - CheckPlatformWin32(); - var rCore = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\R-core"); - if (rCore == null) - { - rCore = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\R-core"); - if (rCore == null) - throw new ApplicationException("Windows Registry key 'SOFTWARE\\R-core' not found in HKEY_LOCAL_MACHINE nor HKEY_CURRENT_USER"); - } - bool is64Bit = Environment.Is64BitProcess; - var subKey = is64Bit ? "R64" : "R"; - var r = rCore.OpenSubKey(subKey); - if (r == null) - { - throw new ApplicationException( string.Format( - "Windows Registry sub-key '{0}' of key '{1}' was not found", subKey , rCore.ToString())); - } - return r; - } - - /// - /// Gets the default file name of the R library on the supported platforms. - /// - /// R dll file name - public static string GetRDllFileName() - { - var p = GetPlatform(); - switch (p) { - case PlatformID.Win32NT: - return "R.dll"; + return platform; + } + + /// + /// Sets the PATH to the R binaries and R_HOME environment variables if needed. + /// + /// The path for R_HOME. If null (default), the function checks the R_HOME environment variable. If none is set, + /// the function uses platform specific sensible default behaviors. + /// + /// This function has been designed to limit the tedium for users, while allowing custom settings for unusual installations. + /// + public static void SetEnvironmentVariables(string rHome = null) + { + if (string.IsNullOrEmpty(rHome)) + { + rHome = Environment.GetEnvironmentVariable("R_HOME"); + if (string.IsNullOrEmpty(rHome)) + { + rHome = FindRHome(); + } + + if (string.IsNullOrEmpty(rHome)) + throw new InvalidOperationException("Could not find R_HOME in current environment or registry."); + } + var rPath = ConstructRPath(rHome); + + if (!Directory.Exists(rHome)) + throw new DirectoryNotFoundException(string.Format("R_HOME directory does not exist: '{0}'", rHome)); + + if (!Directory.Exists(rPath)) + throw new DirectoryNotFoundException(string.Format("Directory for R binaries does not exist: '{0}'", rPath)); + + Environment.SetEnvironmentVariable("R_HOME", rHome); + Environment.SetEnvironmentVariable("PATH", rPath + ";" + Environment.GetEnvironmentVariable("PATH")); + } + + private static string FindRHome(string rPath = null) + { + var platform = GetPlatform(); + string rHome; + switch (platform) + { + case PlatformID.Win32NT: + rHome = WindowsLibraryLoader.GetRHomeFromRegistry(); + break; + case PlatformID.MacOSX: + rHome = "/Library/Frameworks/R.framework/Resources"; + break; + case PlatformID.Unix: + // if rPath is e.g. /usr/local/lib/R/lib/ , + rHome = Path.GetDirectoryName(rPath); + if (!rHome.EndsWith("R")) + // if rPath is e.g. /usr/lib/ (symlink) then default + rHome = "/usr/lib/R"; + break; + default: + throw new NotSupportedException(platform.ToString()); + } + return rHome; + } + + private static string ConstructRPath(string rHome) + { + var shlibFilename = GetRLibraryFileName(); + var platform = GetPlatform(); + switch (platform) + { + case PlatformID.Win32NT: + var rPath = Path.Combine(rHome, "bin"); + var rVersion = WindowsLibraryLoader.GetRVersionFromRegistry(); + if (rVersion.Major > 2 || (rVersion.Major == 2 && rVersion.Minor >= 12)) + { + var bitness = Environment.Is64BitProcess ? "x64" : "i386"; + rPath = Path.Combine(rPath, bitness); + } + + return rPath; + + case PlatformID.MacOSX: // TODO: is there a way to detect installations on MacOS + return "/Library/Frameworks/R.framework/Libraries"; + + case PlatformID.Unix: + var rexepath = ExecCommand("which", "R"); // /usr/bin/R, or /usr/local/bin/R + if (string.IsNullOrEmpty(rexepath)) return "/usr/lib"; + var bindir = Path.GetDirectoryName(rexepath); // /usr/local/bin + // Trying to emulate the start of the R shell script + // /usr/local/lib/R/lib/libR.so + var libdir = Path.Combine(Path.GetDirectoryName(bindir), "lib", "R", "lib"); + if (File.Exists(Path.Combine(libdir, shlibFilename))) + return libdir; + libdir = Path.Combine(Path.GetDirectoryName(bindir), "lib64", "R", "lib"); + if (File.Exists(Path.Combine(libdir, shlibFilename))) + return libdir; + return "/usr/lib"; + + default: + throw new PlatformNotSupportedException(); + } + } - case PlatformID.MacOSX: - return "libR.dylib"; + public static string GetRLibraryFileName() + { + var p = GetPlatform(); + switch (p) + { + case PlatformID.Win32NT: + return "R.dll"; - case PlatformID.Unix: - return "libR.so"; + case PlatformID.MacOSX: + return "libR.dylib"; - default: - throw new NotSupportedException("Platform is not supported: " + p.ToString()); - } - } + case PlatformID.Unix: + return "libR.so"; - /// - /// Is the platform a unix like (Unix or MacOX) - /// - public static bool IsUnix { - get { - var p = GetPlatform(); - return p == PlatformID.MacOSX || p == PlatformID.Unix; - } - } - } -} \ No newline at end of file + default: + throw new PlatformNotSupportedException(); + } + } + + /// + /// Is the platform a unix like (Unix or MacOX) + /// + public static bool IsUnix + { + get + { + var p = GetPlatform(); + return p == PlatformID.MacOSX || p == PlatformID.Unix; + } + } + + private static string ExecCommand(string processName, string arguments) + { + using (var uname = new Process()) + { + uname.StartInfo.FileName = processName; + uname.StartInfo.Arguments = arguments; + uname.StartInfo.RedirectStandardOutput = true; + uname.StartInfo.UseShellExecute = false; + uname.StartInfo.CreateNoWindow = true; + uname.Start(); + var kernelName = uname.StandardOutput.ReadLine(); + uname.WaitForExit(); + return kernelName; + } + } + } +} diff --git a/RDotNet.NativeLibrary/UnmanagedDll.cs b/RDotNet.NativeLibrary/UnmanagedDll.cs index dcc7c021..88d98229 100644 --- a/RDotNet.NativeLibrary/UnmanagedDll.cs +++ b/RDotNet.NativeLibrary/UnmanagedDll.cs @@ -13,46 +13,46 @@ namespace RDotNet.NativeLibrary [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] public class UnmanagedDll : SafeHandle { - /// + private readonly IDynamicLibraryLoader _libraryLoader; + + /// /// Gets whether the current handle is equal to the invalid handle /// public override bool IsInvalid { get { return handle == IntPtr.Zero; } } - - private IDynamicLibraryLoader libraryLoader; - - /// - /// Creates a proxy for the specified dll. - /// - /// The DLL's name. - public UnmanagedDll(string dllName) - : base(IntPtr.Zero, true) - { - if (dllName == null) - { - throw new ArgumentNullException("dllName", "The name of the library to load is a null reference"); - } - if (dllName == string.Empty) - { - throw new ArgumentException("The name of the library to load is an empty string", "dllName"); - } - if (IsUnix) - libraryLoader = new UnixLibraryLoader(); - else - libraryLoader = new WindowsLibraryLoader (); - - IntPtr handle = libraryLoader.LoadLibrary(dllName); - if (handle == IntPtr.Zero) - { - ReportLoadLibError(dllName); - } - SetHandle(handle); - this.DllFilename = dllName; - } - - /// + /// + /// Creates a proxy for the specified dll. + /// + /// The DLL's name. + public UnmanagedDll(string dllName) + : base(IntPtr.Zero, true) + { + if (dllName == null) + { + throw new ArgumentNullException("dllName", "The name of the library to load is a null reference"); + } + if (dllName == string.Empty) + { + throw new ArgumentException("The name of the library to load is an empty string", "dllName"); + } + + if (NativeUtility.IsUnix) + _libraryLoader = new UnixLibraryLoader(); + else + _libraryLoader = new WindowsLibraryLoader(); + + IntPtr handle = _libraryLoader.LoadLibrary(dllName); + if (handle == IntPtr.Zero) + { + ReportLoadLibError(dllName); + } + SetHandle(handle); + DllFilename = dllName; + } + + /// /// Gets the Dll file name used for this native Dll wrapper. /// public string DllFilename { get; private set; } @@ -123,7 +123,7 @@ private void ThrowFailedLibraryLoad(string dllFullName) { var strMsg = string.Format("This {0}-bit process failed to load the library {1}", (Environment.Is64BitProcess ? "64" : "32"), dllFullName); - var nativeError = libraryLoader.GetLastError(); + var nativeError = _libraryLoader.GetLastError(); if (!string.IsNullOrEmpty(nativeError)) strMsg = strMsg + string.Format(". Native error message is '{0}'", nativeError); var ldLibPathMsg = createLdLibPathMsg(); @@ -178,19 +178,15 @@ private void throwEntryPointNotFound(string entryPoint) throw new EntryPointNotFoundException(string.Format("Function {0} not found in native library {1}", entryPoint, this.DllFilename)); } - private bool IsUnix { - get { return NativeUtility.IsUnix; } - } - private IntPtr GetFunctionAddress(string lpProcName) { - return libraryLoader.GetFunctionAddress(handle, lpProcName); + return _libraryLoader.GetFunctionAddress(handle, lpProcName); } private bool FreeLibrary() { bool freed = false; - if (libraryLoader == null) + if (_libraryLoader == null) { if (!this.IsInvalid) { @@ -208,7 +204,7 @@ private bool FreeLibrary() return freed; } else - return libraryLoader.FreeLibrary(handle); + return _libraryLoader.FreeLibrary(handle); } /// diff --git a/RDotNet.NativeLibrary/WindowsLibraryLoader.cs b/RDotNet.NativeLibrary/WindowsLibraryLoader.cs index 3c98bbe4..28cf6f8c 100644 --- a/RDotNet.NativeLibrary/WindowsLibraryLoader.cs +++ b/RDotNet.NativeLibrary/WindowsLibraryLoader.cs @@ -4,74 +4,101 @@ using System.ComponentModel; using System.Security.Permissions; using System.Text; +using Microsoft.Win32; namespace RDotNet.NativeLibrary { - [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] - internal class WindowsLibraryLoader : IDynamicLibraryLoader - { - public IntPtr LoadLibrary(string filename) - { - return InternalLoadLibrary(filename); - } + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal class WindowsLibraryLoader : IDynamicLibraryLoader + { + public IntPtr LoadLibrary(string filename) + { + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + var handle = Win32.LoadLibrary(filename); + if (handle == IntPtr.Zero) + { + var error = new Win32Exception(Marshal.GetLastWin32Error()).Message; + Console.WriteLine(error); + } + return handle; + } + + public string GetLastError() + { + // see for instance http://blogs.msdn.com/b/shawnfa/archive/2004/09/10/227995.aspx + // and http://blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx + // TODO: does this work as expected with Mono+Windows stack? + return new Win32Exception().Message; + } - public string GetLastError() - { - // see for instance http://blogs.msdn.com/b/shawnfa/archive/2004/09/10/227995.aspx - // and http://blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx - // TODO: does this work as expected with Mono+Windows stack? - return new Win32Exception().Message; - } + public bool FreeLibrary(IntPtr handle) + { + return Win32.FreeLibrary(handle); + } - public bool FreeLibrary(IntPtr handle) - { - return InternalFreeLibrary(handle); - } + public IntPtr GetFunctionAddress(IntPtr hModule, string lpProcName) + { + return Win32.GetProcAddress(hModule, lpProcName); + } - public IntPtr GetFunctionAddress(IntPtr hModule, string lpProcName) - { - return InternalGetProcAddress(hModule, lpProcName); - } + internal protected static string GetShortPath(string path) + { + var shortPath = new StringBuilder(Win32.MaxPathLength); + Win32.GetShortPathName(path, shortPath, Win32.MaxPathLength); + return shortPath.ToString(); + } - [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", SetLastError=true)] - private static extern IntPtr InternalLoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); + public static string GetRHomeFromRegistry() + { + var rCoreKey = GetRCoreRegistryKey(); + var path = rCoreKey.GetValue("InstallPath") as string; + return GetShortPath(path); + } - [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool InternalFreeLibrary(IntPtr hModule); + public static Version GetRVersionFromRegistry() + { + var rCoreKey = GetRCoreRegistryKey(); + var version = rCoreKey.GetValue("Current Version") as string; + if (string.IsNullOrEmpty(version)) return null; + return new Version(version); + } - [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")] - private static extern IntPtr InternalGetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName); + private static RegistryKey GetRCoreRegistryKey() + { + if (Environment.OSVersion.Platform != PlatformID.Win32NT) return null; - [DllImport("kernel32.dll", EntryPoint = "GetLastError")] - [return: MarshalAs(UnmanagedType.LPStr)] - private static extern string InternalGetLastError(); + var rCore = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\R-core"); + if (rCore == null) + { + rCore = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\R-core"); + if (rCore == null) return null; + } - const int MAX_PATH_LENGTH = 255; + var subKey = Environment.Is64BitProcess ? "R64" : "R"; + var r = rCore.OpenSubKey(subKey); + return r; + } + } + + internal static class Win32 + { + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - private static extern int GetShortPathName( - [MarshalAs(UnmanagedType.LPTStr)] - string path, - [MarshalAs(UnmanagedType.LPTStr)] - StringBuilder shortPath, - int shortPathLength - ); + [DllImport("kernel32.dll")] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FreeLibrary(IntPtr hModule); - /// - /// Gets the old style DOS short path (8.3 format) given a path name - /// - /// A path - /// The short path name according to the Windows kernel32 API - internal protected static string GetShortPath(string path) - { - var shortPath = new StringBuilder(MAX_PATH_LENGTH); - GetShortPathName(path, shortPath, MAX_PATH_LENGTH); - return shortPath.ToString(); - } + [DllImport("kernel32.dll")] + public static extern IntPtr GetProcAddress(IntPtr hModule, + [MarshalAs(UnmanagedType.LPStr)] string lpProcName); + public const int MaxPathLength = 248; //MaxPath is 248. MaxFileName is 260. + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + public static extern int GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string path, + [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath, + int shortPathLength); } } - From ad19510ebdf0c2e79f51472497c4324c54856f2c Mon Sep 17 00:00:00 2001 From: skyguy94 Date: Sat, 27 Sep 2014 09:28:54 -0500 Subject: [PATCH 07/10] Implement autoprint --- R.NET/Internals/Delegates.cs | 17 ++++++++++------- R.NET/REngine.cs | 14 +++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/R.NET/Internals/Delegates.cs b/R.NET/Internals/Delegates.cs index 441198ed..24b4731b 100644 --- a/R.NET/Internals/Delegates.cs +++ b/R.NET/Internals/Delegates.cs @@ -45,15 +45,15 @@ namespace RDotNet.Internals /// arguments passed to the embedded engine /// /// - /// int Rf_initEmbeddedR(int argc, char **argv) - ///{ + /// int Rf_initEmbeddedR(int argc, char **argv) + ///{ /// Rf_initialize_R(argc, argv); - /// // R_Interactive is set to true in unix Rembedded.c, not gnuwin - /// R_Interactive = TRUE; /* Rf_initialize_R set this based on isatty */ - /// setup_Rmainloop(); - /// return(1); + /// // R_Interactive is set to true in unix Rembedded.c, not gnuwin + /// R_Interactive = TRUE; /* Rf_initialize_R set this based on isatty */ + /// setup_Rmainloop(); + /// return(1); ///} - /// + /// /// /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -140,6 +140,9 @@ namespace RDotNet.Internals [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr R_tryEval(IntPtr statement, IntPtr environment, out bool errorOccurred); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void Rf_PrintValue(IntPtr value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr R_ParseVector(IntPtr statement, int statementCount, out ParseStatus status, IntPtr _); diff --git a/R.NET/REngine.cs b/R.NET/REngine.cs index 02fcb0de..d1283028 100644 --- a/R.NET/REngine.cs +++ b/R.NET/REngine.cs @@ -690,6 +690,11 @@ private SymbolicExpression Parse(string statement, StringBuilder incompleteState { throw new EvaluationException(LastErrorMessage); } + + if (!result.IsInvalid && GetVisible()) + { + GetFunction()(result.DangerousGetHandle()); + } return result; } case ParseStatus.Incomplete: @@ -708,8 +713,15 @@ private SymbolicExpression Parse(string statement, StringBuilder incompleteState } } + private bool GetVisible() + { + var symbol = DangerousGetHandle("R_Visible"); + var value = Marshal.ReadInt32(symbol); + var result = Convert.ToBoolean(value); + return result; + } - /// + /// /// A cache of the unevaluated R expression 'geterrmessage' /// /// do_geterrmessage is in Rdll.hide, so we cannot access at the C API level. From 5660cf01eea881380983cd708fb9d241a79efcef Mon Sep 17 00:00:00 2001 From: J-M Date: Mon, 13 Oct 2014 17:01:30 +1100 Subject: [PATCH 08/10] Fix formatting --- R.NET/Internals/Delegates.cs | 4 +-- R.NET/REngine.cs | 52 ++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/R.NET/Internals/Delegates.cs b/R.NET/Internals/Delegates.cs index 24b4731b..682dadad 100644 --- a/R.NET/Internals/Delegates.cs +++ b/R.NET/Internals/Delegates.cs @@ -196,8 +196,8 @@ namespace RDotNet.Internals [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate bool Rf_isOrdered(IntPtr sexp); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate IntPtr R_lsInternal(IntPtr environment, bool all); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate IntPtr R_lsInternal(IntPtr environment, bool all); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr Rf_applyClosure(IntPtr call, IntPtr value, IntPtr arguments, IntPtr environment, IntPtr suppliedEnvironment); diff --git a/R.NET/REngine.cs b/R.NET/REngine.cs index 6041dba7..1f3379dc 100644 --- a/R.NET/REngine.cs +++ b/R.NET/REngine.cs @@ -238,8 +238,8 @@ private static REngine CreateInstance(string id, string dll = null) /// A candidate for the file name of the R shared library protected static string ProcessRDllFileName(string dll) { - if (!string.IsNullOrEmpty(dll)) return dll; - return NativeUtility.GetRLibraryFileName(); + if (!string.IsNullOrEmpty(dll)) return dll; + return NativeUtility.GetRLibraryFileName(); } /// @@ -308,10 +308,10 @@ public string GetDangerousChar(string varname) /// if true, call the functions to initialise the embedded R public void Initialize(StartupParameter parameter = null, ICharacterDevice device = null, bool setupMainLoop = true) { -// Console.WriteLine("REngine.Initialize start"); + // Console.WriteLine("REngine.Initialize start"); if (this.isRunning) return; -// Console.WriteLine("REngine.Initialize, after isRunning checked as false"); + // Console.WriteLine("REngine.Initialize, after isRunning checked as false"); this.parameter = parameter ?? new StartupParameter(); this.adapter = new CharacterDeviceAdapter(device ?? DefaultDevice); // Disabling the stack checking here, to try to avoid the issue on Linux. @@ -319,7 +319,7 @@ public void Initialize(StartupParameter parameter = null, ICharacterDevice devic // function to cater for disabling on Windows, @ rev 305, however this may have // re-broken on Linux. so we may need to call it twice. SetCstackChecking(); -// Console.WriteLine("Initialize-SetCstackChecking; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); + // Console.WriteLine("Initialize-SetCstackChecking; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); if (!setupMainLoop) { @@ -332,7 +332,7 @@ public void Initialize(StartupParameter parameter = null, ICharacterDevice devic //rdotnet_app --quiet --interactive --no-save --no-restore-data --max-mem-size=18446744073709551615 --max-ppsize=50000 GetFunction()(); int R_argc = R_argv.Length; -// Console.WriteLine("Initialize-R_setStartTime; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); + // Console.WriteLine("Initialize-R_setStartTime; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); if (NativeUtility.GetPlatform() == PlatformID.Win32NT) { @@ -343,11 +343,11 @@ public void Initialize(StartupParameter parameter = null, ICharacterDevice devic // GetFunction()(R_argc, R_argv); // GetFunction()(R_argc, R_argv); } - + var status = GetFunction()(R_argc, R_argv); if (status != 0) throw new Exception("A call to Rf_initialize_R returned a non-zero; status=" + status); -// Console.WriteLine("Initialize-Rf_initialize_R; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); + // Console.WriteLine("Initialize-Rf_initialize_R; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); SetCstackChecking(); // following in RInside: may not be needed. @@ -369,7 +369,7 @@ public void Initialize(StartupParameter parameter = null, ICharacterDevice devic } GetFunction()(); //Console.WriteLine("Initialize-after setup_Rmainloop; R_CStackLimit value is " + GetDangerousInt32("R_CStackLimit")); - + // See comments in the first call to SetCstackChecking in this function as to why we (may) need it twice. SetCstackChecking(); this.isRunning = true; @@ -379,7 +379,7 @@ public void Initialize(StartupParameter parameter = null, ICharacterDevice devic // Partial Workaround (hopefully temporary) for https://rdotnet.codeplex.com/workitem/110 if (NativeUtility.GetPlatform() == PlatformID.Win32NT) { - Evaluate( string.Format( "memory.limit({0})", (this.parameter.MaxMemorySize / 1048576UL))); + Evaluate(string.Format("memory.limit({0})", (this.parameter.MaxMemorySize / 1048576UL))); } } @@ -392,11 +392,11 @@ void SetCstackChecking() SetDangerousInt32("R_CStackLimit", -1); switch (NativeUtility.GetPlatform()) { - case PlatformID.MacOSX: - case PlatformID.Unix: - SetDangerousInt32("R_SignalHandlers", 0); - // RInside does this for non-WIN32. - break; + case PlatformID.MacOSX: + case PlatformID.Unix: + SetDangerousInt32("R_SignalHandlers", 0); + // RInside does this for non-WIN32. + break; } } @@ -684,9 +684,9 @@ private SymbolicExpression Parse(string statement, StringBuilder incompleteState throw new EvaluationException(LastErrorMessage); } - if (!result.IsInvalid && GetVisible()) + if (!result.IsInvalid && GetVisible()) { - GetFunction()(result.DangerousGetHandle()); + GetFunction()(result.DangerousGetHandle()); } return result; } @@ -706,15 +706,15 @@ private SymbolicExpression Parse(string statement, StringBuilder incompleteState } } - private bool GetVisible() - { - var symbol = DangerousGetHandle("R_Visible"); - var value = Marshal.ReadInt32(symbol); - var result = Convert.ToBoolean(value); - return result; - } + private bool GetVisible() + { + var symbol = DangerousGetHandle("R_Visible"); + var value = Marshal.ReadInt32(symbol); + var result = Convert.ToBoolean(value); + return result; + } - /// + /// /// A cache of the unevaluated R expression 'geterrmessage' /// /// do_geterrmessage is in Rdll.hide, so we cannot access at the C API level. @@ -807,7 +807,7 @@ protected override void Dispose(bool disposing) if (disposing && this.adapter != null) { -// Console.WriteLine("Disposing of an existing console adapter"); + // Console.WriteLine("Disposing of an existing console adapter"); this.adapter.Dispose(); this.adapter = null; } From f0c02e8a5576ede8b3e76619d4545f90d59cf595 Mon Sep 17 00:00:00 2001 From: J-M Date: Sun, 9 Nov 2014 16:43:02 +1100 Subject: [PATCH 09/10] Diagnosis for Linux related issues; https://rdotnet.codeplex.com/workitem/146 --- .nuget/NuGet.targets | 4 +++- RDotNet.NativeLibrary/NativeUtility.cs | 8 ++++++-- RDotNet.TestBase/RDotNet.TestBase.csproj | 16 ++++++++-------- RDotNet.TestBase/RDotNetTestFixture.cs | 7 +++++++ RDotNet.TestBase/packages.config | 2 +- RDotNet.Tests/DataFrameTest.cs | 13 +++++++++++++ RDotNet.Tests/ExceptionHandlingTest.cs | 6 ++++++ RDotNet.Tests/RDotNet.Tests.csproj | 14 ++++++-------- RDotNet.Tests/REngineInitTest.cs | 6 ++++-- RDotNet.Tests/REngineTest.cs | 2 ++ RDotNet.Tests/packages.config | 2 +- packages/repositories.config | 5 ++--- 12 files changed, 59 insertions(+), 26 deletions(-) diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets index d41ddfbe..59aa5e33 100644 --- a/.nuget/NuGet.targets +++ b/.nuget/NuGet.targets @@ -3,6 +3,8 @@ $(MSBuildProjectDirectory)\..\ + + false false @@ -130,4 +132,4 @@ - \ No newline at end of file + diff --git a/RDotNet.NativeLibrary/NativeUtility.cs b/RDotNet.NativeLibrary/NativeUtility.cs index f92b1a9b..6b873d15 100644 --- a/RDotNet.NativeLibrary/NativeUtility.cs +++ b/RDotNet.NativeLibrary/NativeUtility.cs @@ -169,8 +169,11 @@ public static string FindRHome(string rPath = null) rHome = "/Library/Frameworks/R.framework/Resources"; break; case PlatformID.Unix: - // if rPath is e.g. /usr/local/lib/R/lib/ , - rHome = Path.GetDirectoryName(rPath); + if(!string.IsNullOrEmpty(rPath)) + // if rPath is e.g. /usr/local/lib/R/lib/ , + rHome = Path.GetDirectoryName(rPath); + else + rHome = "/usr/lib/R"; if (!rHome.EndsWith("R")) // if rPath is e.g. /usr/lib/ (symlink) then default rHome = "/usr/lib/R"; @@ -235,6 +238,7 @@ public static string FindRPath(string rHome=null) private static string FindRPathUnix(string rHome) { + // TODO: too many default strings here. R.NET should not try to overcome variance in Unix setups. var shlibFilename = GetRLibraryFileName(); var rexepath = ExecCommand("which", "R"); // /usr/bin/R, or /usr/local/bin/R if (string.IsNullOrEmpty(rexepath)) return "/usr/lib"; diff --git a/RDotNet.TestBase/RDotNet.TestBase.csproj b/RDotNet.TestBase/RDotNet.TestBase.csproj index e199fd85..8b74ea48 100644 --- a/RDotNet.TestBase/RDotNet.TestBase.csproj +++ b/RDotNet.TestBase/RDotNet.TestBase.csproj @@ -33,9 +33,6 @@ 4 - - ..\packages\NUnit.2.6.2\lib\nunit.framework.dll - @@ -43,6 +40,9 @@ + + ..\packages\NUnit.2.6.3\lib\nunit.framework.dll + @@ -50,16 +50,13 @@ - - - - {0923e1a0-2032-4997-ab73-49e42c4034a9} + {0923E1A0-2032-4997-AB73-49E42C4034A9} RDotNet - {2a089a59-0f22-4484-b442-0fe8bda10879} + {2A089A59-0F22-4484-B442-0FE8BDA10879} RDotNet.NativeLibrary @@ -72,4 +69,7 @@ --> + + + \ No newline at end of file diff --git a/RDotNet.TestBase/RDotNetTestFixture.cs b/RDotNet.TestBase/RDotNetTestFixture.cs index d7d4a185..8768cdc6 100644 --- a/RDotNet.TestBase/RDotNetTestFixture.cs +++ b/RDotNet.TestBase/RDotNetTestFixture.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using NUnit.Framework; +using RDotNet.NativeLibrary; namespace RDotNet { @@ -19,6 +20,12 @@ public class RDotNetTestFixture private readonly bool initializeOnceOnly = true; + protected static void ReportFailOnLinux(string additionalMsg) + { + if (NativeUtility.IsUnix) + throw new NotSupportedException("On at least one Linux platform, this creates a crash " + additionalMsg); + } + [TestFixtureSetUp] protected virtual void SetUpFixture() { diff --git a/RDotNet.TestBase/packages.config b/RDotNet.TestBase/packages.config index 32171d33..5a3253fc 100644 --- a/RDotNet.TestBase/packages.config +++ b/RDotNet.TestBase/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/RDotNet.Tests/DataFrameTest.cs b/RDotNet.Tests/DataFrameTest.cs index a1754758..c2bcf770 100644 --- a/RDotNet.Tests/DataFrameTest.cs +++ b/RDotNet.Tests/DataFrameTest.cs @@ -1,6 +1,8 @@ using NUnit.Framework; using System.Collections; using System.Collections.Generic; +using RDotNet.NativeLibrary; +using System; namespace RDotNet { @@ -41,6 +43,14 @@ private static DataFrame getMpgDataFrame(REngine engine) return mpg; } + //[Test] + public void TestDataFrameInMemoryCreationTwice() + { + // https://rdotnet.codeplex.com/workitem/146 + TestDataFrameInMemoryCreation(); + TestDataFrameInMemoryCreation(); + } + [Test] public void TestDataFrameInMemoryCreation() { @@ -54,6 +64,9 @@ public void TestDataFrameInMemoryCreation() df = engine.CreateDataFrame(columns, columnNames: null); checkDataFrameContent(df); + string additionalMsg = "https://rdotnet.codeplex.com/workitem/146"; + ReportFailOnLinux(additionalMsg); + columns[1] = new int[] { 1, 2, 3, 4, 5, 6, 7 }; // NOTE: on at least one machine, this fails at the first test run with an OutOfMemoryException // It is unclear at what level this occurs; if I follow the instructions at http://stackoverflow.com/questions/36014/why-is-net-exception-not-caught-by-try-catch-block diff --git a/RDotNet.Tests/ExceptionHandlingTest.cs b/RDotNet.Tests/ExceptionHandlingTest.cs index 003cd81c..f4e2ddcd 100644 --- a/RDotNet.Tests/ExceptionHandlingTest.cs +++ b/RDotNet.Tests/ExceptionHandlingTest.cs @@ -31,6 +31,9 @@ public void TestFailedExpressionEvaluation() //> fail <- function(msg) {stop(paste( 'the message is', msg))} //> fail('bailing out') //Error in fail("bailing out") : the message is bailing out + + ReportFailOnLinux("https://rdotnet.codeplex.com/workitem/146"); + var engine = this.Engine; engine.Evaluate("fail <- function(msg) {stop(paste( 'the message is', msg))}"); object expr = engine.Evaluate("fail('bailing out')"); @@ -41,12 +44,14 @@ public void TestFailedExpressionEvaluation() public void TestFailedExpressionUnboundSymbol() { var engine = this.Engine; + ReportFailOnLinux("https://rdotnet.codeplex.com/workitem/146"); var x = engine.GetSymbol("x"); } [Test, ExpectedExceptionAttribute(typeof(EvaluationException), ExpectedMessage = "Error: object 'x' not found\n")] public void TestFailedExpressionUnboundSymbolEvaluation() { + ReportFailOnLinux("https://rdotnet.codeplex.com/workitem/146"); var engine = this.Engine; var x = engine.Evaluate("x"); } @@ -54,6 +59,7 @@ public void TestFailedExpressionUnboundSymbolEvaluation() [Test, ExpectedExceptionAttribute(typeof(EvaluationException), ExpectedMessage = "Error: object 'x' not found\n")] public void TestFailedExpressionParsingMissingParenthesis() { + ReportFailOnLinux("https://rdotnet.codeplex.com/workitem/146"); //> x <- rep(c(TRUE,FALSE), 55 //+ //+ x diff --git a/RDotNet.Tests/RDotNet.Tests.csproj b/RDotNet.Tests/RDotNet.Tests.csproj index d2630916..9eb53e0d 100644 --- a/RDotNet.Tests/RDotNet.Tests.csproj +++ b/RDotNet.Tests/RDotNet.Tests.csproj @@ -3,8 +3,6 @@ Debug AnyCPU - 8.0.30703 - 2.0 {6C456A2E-FBB5-44DA-B0D2-FE76C49FCD06} Library Properties @@ -33,9 +31,6 @@ 4 - - ..\packages\NUnit.2.6.2\lib\nunit.framework.dll - @@ -44,6 +39,9 @@ + + ..\packages\NUnit.2.6.3\lib\nunit.framework.dll + @@ -81,9 +79,6 @@ RDotNet.TestBase - - - @@ -96,4 +91,7 @@ --> + + + \ No newline at end of file diff --git a/RDotNet.Tests/REngineInitTest.cs b/RDotNet.Tests/REngineInitTest.cs index 4ce2577e..e6bd038e 100644 --- a/RDotNet.Tests/REngineInitTest.cs +++ b/RDotNet.Tests/REngineInitTest.cs @@ -40,8 +40,10 @@ public void TestFindRBinPath() public void TestFindRHomePath() { string rHomePath = NativeUtility.FindRHome(); - var files = Directory.GetFiles(rHomePath); - var fnmatch = files.Where(fn => Path.GetFileName(fn) == "CHANGES"); + var files = Directory.GetDirectories(rHomePath); + var fnmatch = files.Where(fn => Path.GetFileName(fn) == "library"); + Assert.AreEqual(1, fnmatch.Count()); + fnmatch = Directory.GetDirectories(fnmatch.First()).Where(fn => Path.GetFileName(fn) == "base"); Assert.AreEqual(1, fnmatch.Count()); } } diff --git a/RDotNet.Tests/REngineTest.cs b/RDotNet.Tests/REngineTest.cs index 73c8fdfb..7f339abe 100644 --- a/RDotNet.Tests/REngineTest.cs +++ b/RDotNet.Tests/REngineTest.cs @@ -186,6 +186,8 @@ public void TestParseCodeBlock() public void TestReadConsole() { var engine = this.Engine; + string additionalMsg = "https://rdotnet.codeplex.com/workitem/146"; + ReportFailOnLinux(additionalMsg); Device.Input = "Hello, World!"; Assert.That(engine.Evaluate("readline()").AsCharacter()[0], Is.EqualTo(Device.Input)); } diff --git a/RDotNet.Tests/packages.config b/RDotNet.Tests/packages.config index 1faaf3fe..5a3253fc 100644 --- a/RDotNet.Tests/packages.config +++ b/RDotNet.Tests/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/packages/repositories.config b/packages/repositories.config index 8c03a43b..467b2ce5 100644 --- a/packages/repositories.config +++ b/packages/repositories.config @@ -1,6 +1,5 @@  - - - + + \ No newline at end of file From d9dbf8d88d7d5ca042b5ea7e32b86cc74fd01e99 Mon Sep 17 00:00:00 2001 From: J-M Date: Mon, 10 Nov 2014 17:41:31 +1100 Subject: [PATCH 10/10] Make a few evaluations 'invisible' to avoid noise in the console outputs. --- R.NET/S4Object.cs | 2 +- R.NET/SymbolicExpressionExtension.cs | 2 +- RDotNet.Tests/REngineTest.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/R.NET/S4Object.cs b/R.NET/S4Object.cs index 7b822435..7b5af42c 100644 --- a/R.NET/S4Object.cs +++ b/R.NET/S4Object.cs @@ -38,7 +38,7 @@ protected internal S4Object(REngine engine, IntPtr pointer) : base(engine, pointer) { if (dotSlotNamesFunc == null) - dotSlotNamesFunc = Engine.Evaluate(".slotNames").AsFunction(); + dotSlotNamesFunc = Engine.Evaluate("invisible(.slotNames)").AsFunction(); } /// diff --git a/R.NET/SymbolicExpressionExtension.cs b/R.NET/SymbolicExpressionExtension.cs index b84d0d0c..901c5e97 100644 --- a/R.NET/SymbolicExpressionExtension.cs +++ b/R.NET/SymbolicExpressionExtension.cs @@ -50,7 +50,7 @@ private static GenericVector asList(SymbolicExpression expression) asListFunction = null; } if(asListFunction==null) - asListFunction = engine.Evaluate("as.list").AsFunction(); + asListFunction = engine.Evaluate("invisible(as.list)").AsFunction(); var newExpression = asListFunction.Invoke(expression); return new GenericVector(newExpression.Engine, newExpression.DangerousGetHandle()); } diff --git a/RDotNet.Tests/REngineTest.cs b/RDotNet.Tests/REngineTest.cs index 7f339abe..e617b276 100644 --- a/RDotNet.Tests/REngineTest.cs +++ b/RDotNet.Tests/REngineTest.cs @@ -120,7 +120,7 @@ private void CheckProperMemoryReclaimR(string statementCreateX, double expect { var engine = this.Engine; engine.Evaluate("if (exists('x')) {rm(x)}"); - //Thread.Sleep(100); + Thread.Sleep(100); var memoryInitial = GetBaselineRengineMemory(engine); engine.Evaluate(statementCreateX); T sexp = coercionFun(engine.GetSymbol("x")); @@ -128,7 +128,7 @@ private void CheckProperMemoryReclaimR(string statementCreateX, double expect Assert.That(memoryAfterAlloc - memoryInitial, Is.GreaterThan(expectedMinMegaBytesDifference)); engine.Evaluate("rm(x)"); sexp = null; - //Thread.Sleep(100); + Thread.Sleep(100); var memoryAfterGC = GetBaselineRengineMemory(engine); Assert.That(memoryAfterAlloc - memoryAfterGC, Is.GreaterThan(expectedMinMegaBytesDifference)); // x should be collected. } @@ -143,7 +143,7 @@ private static double GetBaselineRengineMemory(REngine engine) { GarbageCollectRandClr(engine); var tmp = GetRMemorySize(engine); - //Thread.Sleep(100); + Thread.Sleep(100); GarbageCollectRandClr(engine); return GetRMemorySize(engine); }