The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
8.7 KiB
231 lines
8.7 KiB
#region Copyright notice and license |
|
|
|
// Copyright 2015 gRPC authors. |
|
// |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
#endregion |
|
|
|
using System; |
|
using System.IO; |
|
using System.Reflection; |
|
|
|
using Grpc.Core.Logging; |
|
|
|
namespace Grpc.Core.Internal |
|
{ |
|
/// <summary> |
|
/// Takes care of loading C# native extension and provides access to PInvoke calls the library exports. |
|
/// </summary> |
|
internal sealed class NativeExtension |
|
{ |
|
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeExtension>(); |
|
static readonly object staticLock = new object(); |
|
static volatile NativeExtension instance; |
|
|
|
readonly NativeMethods nativeMethods; |
|
|
|
private NativeExtension() |
|
{ |
|
this.nativeMethods = LoadNativeMethods(); |
|
|
|
// Redirect the native logs as the very first thing after loading the native extension |
|
// to make sure we don't lose any logs. |
|
NativeLogRedirector.Redirect(this.nativeMethods); |
|
|
|
DefaultSslRootsOverride.Override(this.nativeMethods); |
|
|
|
Logger.Debug("gRPC native library loaded successfully."); |
|
} |
|
|
|
/// <summary> |
|
/// Gets singleton instance of this class. |
|
/// The native extension is loaded when called for the first time. |
|
/// </summary> |
|
public static NativeExtension Get() |
|
{ |
|
if (instance == null) |
|
{ |
|
lock (staticLock) |
|
{ |
|
if (instance == null) { |
|
instance = new NativeExtension(); |
|
} |
|
} |
|
} |
|
return instance; |
|
} |
|
|
|
/// <summary> |
|
/// Provides access to the exported native methods. |
|
/// </summary> |
|
public NativeMethods NativeMethods |
|
{ |
|
get { return this.nativeMethods; } |
|
} |
|
|
|
/// <summary> |
|
/// Detects which configuration of native extension to load and load it. |
|
/// </summary> |
|
private static UnmanagedLibrary LoadUnmanagedLibrary() |
|
{ |
|
// TODO: allow customizing path to native extension (possibly through exposing a GrpcEnvironment property). |
|
// See https://github.com/grpc/grpc/pull/7303 for one option. |
|
var assemblyDirectory = Path.GetDirectoryName(GetAssemblyPath()); |
|
|
|
// With old-style VS projects, the native libraries get copied using a .targets rule to the build output folder |
|
// alongside the compiled assembly. |
|
// With dotnet cli projects targeting net45 framework, the native libraries (just the required ones) |
|
// are similarly copied to the built output folder, through the magic of Microsoft.NETCore.Platforms. |
|
var classicPath = Path.Combine(assemblyDirectory, GetNativeLibraryFilename()); |
|
|
|
// With dotnet cli project targeting netcoreapp1.0, projects will use Grpc.Core assembly directly in the location where it got restored |
|
// by nuget. We locate the native libraries based on known structure of Grpc.Core nuget package. |
|
// When "dotnet publish" is used, the runtimes directory is copied next to the published assemblies. |
|
string runtimesDirectory = string.Format("runtimes/{0}/native", GetPlatformString()); |
|
var netCorePublishedAppStylePath = Path.Combine(assemblyDirectory, runtimesDirectory, GetNativeLibraryFilename()); |
|
var netCoreAppStylePath = Path.Combine(assemblyDirectory, "../..", runtimesDirectory, GetNativeLibraryFilename()); |
|
|
|
// Look for the native library in all possible locations in given order. |
|
string[] paths = new[] { classicPath, netCorePublishedAppStylePath, netCoreAppStylePath}; |
|
return new UnmanagedLibrary(paths); |
|
} |
|
|
|
/// <summary> |
|
/// Loads native extension and return native methods delegates. |
|
/// </summary> |
|
private static NativeMethods LoadNativeMethods() |
|
{ |
|
if (PlatformApis.IsUnity) |
|
{ |
|
return LoadNativeMethodsUnity(); |
|
} |
|
if (PlatformApis.IsXamarin) |
|
{ |
|
return LoadNativeMethodsXamarin(); |
|
} |
|
return new NativeMethods(LoadUnmanagedLibrary()); |
|
} |
|
|
|
/// <summary> |
|
/// Return native method delegates when running on Unity platform. |
|
/// Unity does not use standard NuGet packages and the native library is treated |
|
/// there as a "native plugin" which is (provided it has the right metadata) |
|
/// automatically made available to <c>[DllImport]</c> loading logic. |
|
/// WARNING: Unity support is experimental and work-in-progress. Don't expect it to work. |
|
/// </summary> |
|
private static NativeMethods LoadNativeMethodsUnity() |
|
{ |
|
switch (PlatformApis.GetUnityRuntimePlatform()) |
|
{ |
|
case "IPhonePlayer": |
|
return new NativeMethods(new NativeMethods.DllImportsFromStaticLib()); |
|
default: |
|
// most other platforms load unity plugins as a shared library |
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib()); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Return native method delegates when running on the Xamarin platform. |
|
/// WARNING: Xamarin support is experimental and work-in-progress. Don't expect it to work. |
|
/// </summary> |
|
private static NativeMethods LoadNativeMethodsXamarin() |
|
{ |
|
if (PlatformApis.IsXamarinAndroid) |
|
{ |
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib()); |
|
} |
|
// not tested yet |
|
return new NativeMethods(new NativeMethods.DllImportsFromStaticLib()); |
|
} |
|
|
|
private static string GetAssemblyPath() |
|
{ |
|
var assembly = typeof(NativeExtension).GetTypeInfo().Assembly; |
|
#if NETSTANDARD1_5 |
|
// Assembly.EscapedCodeBase does not exist under CoreCLR, but assemblies imported from a nuget package |
|
// don't seem to be shadowed by DNX-based projects at all. |
|
return assembly.Location; |
|
#else |
|
// If assembly is shadowed (e.g. in a webapp), EscapedCodeBase is pointing |
|
// to the original location of the assembly, and Location is pointing |
|
// to the shadow copy. We care about the original location because |
|
// the native dlls don't get shadowed. |
|
|
|
var escapedCodeBase = assembly.EscapedCodeBase; |
|
if (IsFileUri(escapedCodeBase)) |
|
{ |
|
return new Uri(escapedCodeBase).LocalPath; |
|
} |
|
return assembly.Location; |
|
#endif |
|
} |
|
|
|
#if !NETSTANDARD1_5 |
|
private static bool IsFileUri(string uri) |
|
{ |
|
return uri.ToLowerInvariant().StartsWith(Uri.UriSchemeFile); |
|
} |
|
#endif |
|
|
|
private static string GetPlatformString() |
|
{ |
|
if (PlatformApis.IsWindows) |
|
{ |
|
return "win"; |
|
} |
|
if (PlatformApis.IsLinux) |
|
{ |
|
return "linux"; |
|
} |
|
if (PlatformApis.IsMacOSX) |
|
{ |
|
return "osx"; |
|
} |
|
throw new InvalidOperationException("Unsupported platform."); |
|
} |
|
|
|
// Currently, only Intel platform is supported. |
|
private static string GetArchitectureString() |
|
{ |
|
if (PlatformApis.Is64Bit) |
|
{ |
|
return "x64"; |
|
} |
|
else |
|
{ |
|
return "x86"; |
|
} |
|
} |
|
|
|
// platform specific file name of the extension library |
|
private static string GetNativeLibraryFilename() |
|
{ |
|
string architecture = GetArchitectureString(); |
|
if (PlatformApis.IsWindows) |
|
{ |
|
return string.Format("grpc_csharp_ext.{0}.dll", architecture); |
|
} |
|
if (PlatformApis.IsLinux) |
|
{ |
|
return string.Format("libgrpc_csharp_ext.{0}.so", architecture); |
|
} |
|
if (PlatformApis.IsMacOSX) |
|
{ |
|
return string.Format("libgrpc_csharp_ext.{0}.dylib", architecture); |
|
} |
|
throw new InvalidOperationException("Unsupported platform."); |
|
} |
|
} |
|
}
|
|
|