Merge pull request #894 from filmor/drop-mono-linux-flag

Get the correct library loading functions at runtime
This commit is contained in:
Victor 2019-06-26 09:44:22 -07:00 коммит произвёл GitHub
Родитель fc7d8a4668 537ee5fae1
Коммит 7595fda74a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 263 добавлений и 136 удалений

Просмотреть файл

@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Platform;
namespace Python.EmbeddingTest
{
@ -26,10 +27,10 @@ namespace Python.EmbeddingTest
{
Runtime.Runtime.Initialize();
Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(Runtime.Runtime.MachineType.Other));
Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(MachineType.Other));
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.MachineName));
Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(Runtime.Runtime.OperatingSystemType.Other));
Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(OperatingSystemType.Other));
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.OperatingSystemName));
// Don't shut down the runtime: if the python engine was initialized
@ -39,7 +40,7 @@ namespace Python.EmbeddingTest
[Test]
public static void Py_IsInitializedValue()
{
Runtime.Runtime.Py_Finalize();
Runtime.Runtime.Py_Finalize();
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
Runtime.Runtime.Py_Initialize();
Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized());

Просмотреть файл

@ -140,6 +140,8 @@
<Compile Include="typemanager.cs" />
<Compile Include="typemethod.cs" />
<Compile Include="Util.cs" />
<Compile Include="platform\Types.cs" />
<Compile Include="platform\LibraryLoader.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(PythonInteropFile)' != '' ">
<Compile Include="$(PythonInteropFile)" />

Просмотреть файл

@ -0,0 +1,208 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Python.Runtime.Platform
{
interface ILibraryLoader
{
IntPtr Load(string dllToLoad);
IntPtr GetFunction(IntPtr hModule, string procedureName);
void Free(IntPtr hModule);
}
static class LibraryLoader
{
public static ILibraryLoader Get(OperatingSystemType os)
{
switch (os)
{
case OperatingSystemType.Windows:
return new WindowsLoader();
case OperatingSystemType.Darwin:
return new DarwinLoader();
case OperatingSystemType.Linux:
return new LinuxLoader();
default:
throw new PlatformNotSupportedException($"This operating system ({os}) is not supported");
}
}
}
class LinuxLoader : ILibraryLoader
{
private static int RTLD_NOW = 0x2;
private static int RTLD_GLOBAL = 0x100;
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
private const string NativeDll = "libdl.so";
public IntPtr Load(string dllToLoad)
{
var filename = $"lib{dllToLoad}.so";
ClearError();
var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (res == IntPtr.Zero)
{
var err = GetError();
throw new DllNotFoundException($"Could not load {filename} with flags RTLD_NOW | RTLD_GLOBAL: {err}");
}
return res;
}
public void Free(IntPtr handle)
{
dlclose(handle);
}
public IntPtr GetFunction(IntPtr dllHandle, string name)
{
// look in the exe if dllHandle is NULL
if (dllHandle == IntPtr.Zero)
{
dllHandle = RTLD_DEFAULT;
}
ClearError();
IntPtr res = dlsym(dllHandle, name);
if (res == IntPtr.Zero)
{
var err = GetError();
throw new MissingMethodException($"Failed to load symbol {name}: {err}");
}
return res;
}
void ClearError()
{
dlerror();
}
string GetError()
{
var res = dlerror();
if (res != IntPtr.Zero)
return Marshal.PtrToStringAnsi(res);
else
return null;
}
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr dlopen(string fileName, int flags);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern int dlclose(IntPtr handle);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr dlerror();
}
class DarwinLoader : ILibraryLoader
{
private static int RTLD_NOW = 0x2;
private static int RTLD_GLOBAL = 0x8;
private const string NativeDll = "/usr/lib/libSystem.dylib";
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
public IntPtr Load(string dllToLoad)
{
var filename = $"lib{dllToLoad}.dylib";
ClearError();
var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (res == IntPtr.Zero)
{
var err = GetError();
throw new DllNotFoundException($"Could not load {filename} with flags RTLD_NOW | RTLD_GLOBAL: {err}");
}
return res;
}
public void Free(IntPtr handle)
{
dlclose(handle);
}
public IntPtr GetFunction(IntPtr dllHandle, string name)
{
// look in the exe if dllHandle is NULL
if (dllHandle == IntPtr.Zero)
{
dllHandle = RTLD_DEFAULT;
}
ClearError();
IntPtr res = dlsym(dllHandle, name);
if (res == IntPtr.Zero)
{
var err = GetError();
throw new MissingMethodException($"Failed to load symbol {name}: {err}");
}
return res;
}
void ClearError()
{
dlerror();
}
string GetError()
{
var res = dlerror();
if (res != IntPtr.Zero)
return Marshal.PtrToStringAnsi(res);
else
return null;
}
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr dlopen(String fileName, int flags);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr dlsym(IntPtr handle, String symbol);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern int dlclose(IntPtr handle);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr dlerror();
}
class WindowsLoader : ILibraryLoader
{
private const string NativeDll = "kernel32.dll";
public IntPtr Load(string dllToLoad)
{
var res = WindowsLoader.LoadLibrary(dllToLoad);
if (res == IntPtr.Zero)
throw new DllNotFoundException($"Could not load {dllToLoad}", new Win32Exception());
return res;
}
public IntPtr GetFunction(IntPtr hModule, string procedureName)
{
var res = WindowsLoader.GetProcAddress(hModule, procedureName);
if (res == IntPtr.Zero)
throw new MissingMethodException($"Failed to load symbol {procedureName}", new Win32Exception());
return res;
}
public void Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule);
[DllImport(NativeDll, SetLastError = true)]
static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport(NativeDll, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport(NativeDll)]
static extern bool FreeLibrary(IntPtr hModule);
}
}

Просмотреть файл

@ -0,0 +1,22 @@
namespace Python.Runtime.Platform
{
public enum MachineType
{
i386,
x86_64,
armv7l,
armv8,
Other
};
/// <summary>
/// Operating system type as reported by Python.
/// </summary>
public enum OperatingSystemType
{
Windows,
Darwin,
Linux,
Other
}
}

Просмотреть файл

@ -4,99 +4,10 @@ using System.Security;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using Python.Runtime.Platform;
namespace Python.Runtime
{
[SuppressUnmanagedCodeSecurity]
internal static class NativeMethods
{
#if MONO_LINUX || MONO_OSX
#if NETSTANDARD
private static int RTLD_NOW = 0x2;
#if MONO_LINUX
private static int RTLD_GLOBAL = 0x100;
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
private const string NativeDll = "libdl.so";
public static IntPtr LoadLibrary(string fileName)
{
return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
}
#elif MONO_OSX
private static int RTLD_GLOBAL = 0x8;
private const string NativeDll = "/usr/lib/libSystem.dylib";
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
public static IntPtr LoadLibrary(string fileName)
{
return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
}
#endif
#else
private static int RTLD_NOW = 0x2;
private static int RTLD_SHARED = 0x20;
#if MONO_OSX
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
private const string NativeDll = "__Internal";
#elif MONO_LINUX
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
private const string NativeDll = "libdl.so";
#endif
public static IntPtr LoadLibrary(string fileName)
{
return dlopen(fileName, RTLD_NOW | RTLD_SHARED);
}
#endif
public static void FreeLibrary(IntPtr handle)
{
dlclose(handle);
}
public static IntPtr GetProcAddress(IntPtr dllHandle, string name)
{
// look in the exe if dllHandle is NULL
if (dllHandle == IntPtr.Zero)
{
dllHandle = RTLD_DEFAULT;
}
// clear previous errors if any
dlerror();
IntPtr res = dlsym(dllHandle, name);
IntPtr errPtr = dlerror();
if (errPtr != IntPtr.Zero)
{
throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
}
return res;
}
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr dlopen(String fileName, int flags);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr dlsym(IntPtr handle, String symbol);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern int dlclose(IntPtr handle);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr dlerror();
#else // Windows
private const string NativeDll = "kernel32.dll";
[DllImport(NativeDll)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport(NativeDll)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport(NativeDll)]
public static extern bool FreeLibrary(IntPtr hModule);
#endif
}
/// <summary>
/// Encapsulates the low-level Python C API. Note that it is
@ -197,17 +108,6 @@ namespace Python.Runtime
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
/// <summary>
/// Operating system type as reported by Python.
/// </summary>
public enum OperatingSystemType
{
Windows,
Darwin,
Linux,
Other
}
static readonly Dictionary<string, OperatingSystemType> OperatingSystemTypeMapping = new Dictionary<string, OperatingSystemType>()
{
{ "Windows", OperatingSystemType.Windows },
@ -225,14 +125,6 @@ namespace Python.Runtime
/// </summary>
public static string OperatingSystemName { get; private set; }
public enum MachineType
{
i386,
x86_64,
armv7l,
armv8,
Other
};
/// <summary>
/// Map lower-case version of the python machine name to the processor
@ -397,25 +289,25 @@ namespace Python.Runtime
Error = new IntPtr(-1);
IntPtr dllLocal = IntPtr.Zero;
if (_PythonDll != "__Internal")
{
dllLocal = NativeMethods.LoadLibrary(_PythonDll);
}
_PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dllLocal, "_PyObject_NextNotImplemented");
#if !(MONO_LINUX || MONO_OSX)
if (dllLocal != IntPtr.Zero)
{
NativeMethods.FreeLibrary(dllLocal);
}
#endif
// Initialize data about the platform we're running on. We need
// this for the type manager and potentially other details. Must
// happen after caching the python types, above.
InitializePlatformData();
IntPtr dllLocal = IntPtr.Zero;
var loader = LibraryLoader.Get(OperatingSystem);
if (_PythonDll != "__Internal")
{
dllLocal = loader.Load(_PythonDll);
}
_PyObject_NextNotImplemented = loader.GetFunction(dllLocal, "_PyObject_NextNotImplemented");
if (dllLocal != IntPtr.Zero)
{
loader.Free(dllLocal);
}
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
PyCLRMetaType = MetaType.Initialize();

Просмотреть файл

@ -3,9 +3,11 @@ using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Python.Runtime.Platform;
namespace Python.Runtime
{
/// <summary>
/// The TypeManager class is responsible for building binary-compatible
/// Python type objects that are implemented in managed code.
@ -504,15 +506,15 @@ namespace Python.Runtime
{
get
{
switch(Runtime.Machine)
switch (Runtime.Machine)
{
case Runtime.MachineType.i386:
case MachineType.i386:
return I386;
case Runtime.MachineType.x86_64:
case MachineType.x86_64:
return X86_64;
case Runtime.MachineType.armv7l:
case MachineType.armv7l:
return Armv7l;
case Runtime.MachineType.armv8:
case MachineType.armv8:
return Armv8;
default:
throw new NotImplementedException($"No support for {Runtime.MachineName}");
@ -635,9 +637,9 @@ namespace Python.Runtime
{
switch (Runtime.OperatingSystem)
{
case Runtime.OperatingSystemType.Darwin:
case OperatingSystemType.Darwin:
return 0x1000;
case Runtime.OperatingSystemType.Linux:
case OperatingSystemType.Linux:
return 0x20;
default:
throw new NotImplementedException($"mmap is not supported on {Runtime.OperatingSystemName}");
@ -668,10 +670,10 @@ namespace Python.Runtime
{
switch (Runtime.OperatingSystem)
{
case Runtime.OperatingSystemType.Darwin:
case Runtime.OperatingSystemType.Linux:
case OperatingSystemType.Darwin:
case OperatingSystemType.Linux:
return new UnixMemoryMapper();
case Runtime.OperatingSystemType.Windows:
case OperatingSystemType.Windows:
return new WindowsMemoryMapper();
default:
throw new NotImplementedException($"No support for {Runtime.OperatingSystemName}");