|
|
|
@ -80,28 +80,25 @@ namespace Grpc.Core.Internal |
|
|
|
|
/// <summary> |
|
|
|
|
/// Detects which configuration of native extension to load and load it. |
|
|
|
|
/// </summary> |
|
|
|
|
private static UnmanagedLibrary LoadUnmanagedLibrary() |
|
|
|
|
private static NativeMethods LoadNativeMethodsLegacyNetFramework() |
|
|
|
|
{ |
|
|
|
|
// 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()); |
|
|
|
|
var assemblyDirectory = GetAssemblyDirectory(); |
|
|
|
|
|
|
|
|
|
// With "classic" 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 netcoreappX.Y, 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", GetRuntimeIdString()); |
|
|
|
|
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); |
|
|
|
|
string[] paths = new[] { classicPath }; |
|
|
|
|
|
|
|
|
|
// TODO(jtattermusch): the UnmanagedLibrary mechanism for loading the native extension while avoiding |
|
|
|
|
// direct use of DllImport is quite complicated and is currently only needed to cover some niche scenarios |
|
|
|
|
// (such legacy .NET Framework projects that use assembly shadowing) - everything else can be covered |
|
|
|
|
// by using the [DllImport]. We should investigate the possibility of eliminating UnmanagedLibrary completely |
|
|
|
|
// in the future. |
|
|
|
|
return new NativeMethods(new UnmanagedLibrary(paths)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
@ -117,7 +114,43 @@ namespace Grpc.Core.Internal |
|
|
|
|
{ |
|
|
|
|
return LoadNativeMethodsXamarin(); |
|
|
|
|
} |
|
|
|
|
return new NativeMethods(LoadUnmanagedLibrary()); |
|
|
|
|
if (PlatformApis.IsNetCore) |
|
|
|
|
{ |
|
|
|
|
// On .NET Core, native libraries are a supported feature and the SDK makes |
|
|
|
|
// sure that the native library is made available in the right location and that |
|
|
|
|
// they will be discoverable by the [DllImport] default loading mechanism, |
|
|
|
|
// even in some of the more exotic situations such as single file apps. |
|
|
|
|
// |
|
|
|
|
// While in theory, we could just [DllImport("grpc_csharp_ext")] for all the platforms |
|
|
|
|
// and operating systems, the native libraries in the nuget package |
|
|
|
|
// need to be laid out in a way that still allows things to work well under |
|
|
|
|
// the legacy .NET Framework (where native libraries are a concept unknown to the runtime). |
|
|
|
|
// Therefore, we use several flavors of the DllImport attribute |
|
|
|
|
// (e.g. the ".x86" vs ".x64" suffix) and we choose the one we want at runtime. |
|
|
|
|
// The classes with the list of DllImport'd methods are code generated, |
|
|
|
|
// so having more than just one doesn't really bother us. |
|
|
|
|
|
|
|
|
|
// on Windows, the DllImport("grpc_csharp_ext.x64") doesn't work for some reason, |
|
|
|
|
// but DllImport("grpc_csharp_ext.x64.dll") does, so we need a special case for that. |
|
|
|
|
bool useDllSuffix = PlatformApis.IsWindows; |
|
|
|
|
if (PlatformApis.Is64Bit) |
|
|
|
|
{ |
|
|
|
|
if (useDllSuffix) |
|
|
|
|
{ |
|
|
|
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x64_dll()); |
|
|
|
|
} |
|
|
|
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x64()); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
if (useDllSuffix) |
|
|
|
|
{ |
|
|
|
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x86_dll()); |
|
|
|
|
} |
|
|
|
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x86()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return LoadNativeMethodsLegacyNetFramework(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
@ -139,6 +172,10 @@ namespace Grpc.Core.Internal |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Return native method delegates when running on the Xamarin platform. |
|
|
|
|
/// On Xamarin, the standard <c>[DllImport]</c> loading logic just works |
|
|
|
|
/// as the native library metadata is provided by the <c>AndroidNativeLibrary</c> or |
|
|
|
|
/// <c>NativeReference</c> items in the Xamarin projects (injected automatically |
|
|
|
|
/// by the Grpc.Core.Xamarin nuget). |
|
|
|
|
/// WARNING: Xamarin support is experimental and work-in-progress. Don't expect it to work. |
|
|
|
|
/// </summary> |
|
|
|
|
private static NativeMethods LoadNativeMethodsXamarin() |
|
|
|
@ -147,17 +184,23 @@ namespace Grpc.Core.Internal |
|
|
|
|
{ |
|
|
|
|
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib()); |
|
|
|
|
} |
|
|
|
|
// not tested yet |
|
|
|
|
return new NativeMethods(new NativeMethods.DllImportsFromStaticLib()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static string GetAssemblyPath() |
|
|
|
|
private static string GetAssemblyDirectory() |
|
|
|
|
{ |
|
|
|
|
var assembly = typeof(NativeExtension).GetTypeInfo().Assembly; |
|
|
|
|
#if NETSTANDARD |
|
|
|
|
// 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; |
|
|
|
|
var assemblyLocation = assembly.Location; |
|
|
|
|
if (!string.IsNullOrEmpty(assemblyLocation)) |
|
|
|
|
{ |
|
|
|
|
return Path.GetDirectoryName(assemblyLocation); |
|
|
|
|
} |
|
|
|
|
// In .NET5 single-file deployments, assembly.Location won't be available |
|
|
|
|
// Also see https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file#other-considerations |
|
|
|
|
return AppContext.BaseDirectory; |
|
|
|
|
#else |
|
|
|
|
// If assembly is shadowed (e.g. in a webapp), EscapedCodeBase is pointing |
|
|
|
|
// to the original location of the assembly, and Location is pointing |
|
|
|
@ -167,9 +210,9 @@ namespace Grpc.Core.Internal |
|
|
|
|
var escapedCodeBase = assembly.EscapedCodeBase; |
|
|
|
|
if (IsFileUri(escapedCodeBase)) |
|
|
|
|
{ |
|
|
|
|
return new Uri(escapedCodeBase).LocalPath; |
|
|
|
|
return Path.GetDirectoryName(new Uri(escapedCodeBase).LocalPath); |
|
|
|
|
} |
|
|
|
|
return assembly.Location; |
|
|
|
|
return Path.GetDirectoryName(assembly.Location); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|