Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load specific native libraries on desktop/netfx #1342

Merged
merged 52 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7cda786
Load specific native libraries on desktop/netfx
mattleibow Jun 18, 2020
6f35ed9
Don't break the paths
mattleibow Jun 19, 2020
f49a6d8
Use a new mono to test
mattleibow Jun 19, 2020
0d079f4
For some reason mac/linux fails the first time
mattleibow Jun 19, 2020
d5325f3
Make sure netstandard is referenced
mattleibow Jun 19, 2020
c126e6f
Looks like we have to get a bit weird
mattleibow Jun 19, 2020
d4269f4
Try DependsOnNETStandard
mattleibow Jun 19, 2020
e32ab17
That works, just need to spread the love
mattleibow Jun 19, 2020
32811fc
Do everything!
mattleibow Jun 19, 2020
eac6e09
Yup, that works...
mattleibow Jun 20, 2020
97014ac
trying everything now
mattleibow Jun 20, 2020
cd174cd
We need this
mattleibow Jun 20, 2020
b510fcd
darn
mattleibow Jun 20, 2020
6da54b4
switch back to delegates
mattleibow Jun 20, 2020
535f6fe
Try one test at a time
mattleibow Jun 20, 2020
f71ce90
Try the stream in general
mattleibow Jun 20, 2020
ba9fe25
Try with data
mattleibow Jun 20, 2020
0355157
that
mattleibow Jun 20, 2020
7b1399f
DataCanBeCreatedFromManagedStream
mattleibow Jun 20, 2020
ce67da5
Maybe this will work...
mattleibow Jun 20, 2020
2e785bd
maybe this?
mattleibow Jun 20, 2020
d46678a
sdfasdf
mattleibow Jun 20, 2020
98933ec
Do both
mattleibow Jun 20, 2020
89a9bad
sadfasd
mattleibow Jun 20, 2020
a0af851
Terminate the paths
mattleibow Jun 20, 2020
ef51c56
Build it all
mattleibow Jun 20, 2020
6f43108
undo everything
mattleibow Jun 20, 2020
fcb6e1b
revert cake
mattleibow Jun 20, 2020
ab069f7
Build it all!!!
mattleibow Jun 20, 2020
2ccf65f
Use a better define
mattleibow Jun 20, 2020
9db75c3
Merge remote-tracking branch 'origin/master' into dev/library-loading
mattleibow Jun 21, 2020
573be04
Fix the defines
mattleibow Jun 22, 2020
b4a190f
Clean a few things
mattleibow Jun 22, 2020
80b631d
improve the copying
mattleibow Jun 22, 2020
c0eab0f
fix targets
mattleibow Jun 22, 2020
453ac48
wrong item
mattleibow Jun 22, 2020
ba3bcbb
include libHarfBuzzSharp
mattleibow Jun 22, 2020
fe56e84
No more need for preferred
mattleibow Jun 22, 2020
dc6fdc7
add back
mattleibow Jun 22, 2020
2e957bb
Revert "add back"
mattleibow Jun 22, 2020
4f69269
harfbuzz is not ready yet
mattleibow Jun 22, 2020
148b937
ok
mattleibow Jun 22, 2020
46beef1
improvements
mattleibow Jun 22, 2020
23acc40
ok
mattleibow Jun 22, 2020
5e4c9e1
Add an API to check the versions
mattleibow Jun 24, 2020
a26d11b
Fix some ws
mattleibow Jun 24, 2020
15cf3ac
Make sure to handle the missing properties
mattleibow Jun 24, 2020
350993d
Build everything
mattleibow Jun 24, 2020
17a1fed
Whoops
mattleibow Jun 24, 2020
12888e0
Merge branch 'master' into dev/library-loading
mattleibow Jun 24, 2020
ed86f4d
Regen
mattleibow Jun 24, 2020
335d82b
Should be private
mattleibow Jun 25, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions VERSIONS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ SharpVk release 0.4.2
OpenTK.GLControl reference 1.1.2349.61993
Xamarin.Forms reference 4.4.0.991757

# native milestones
# this is related to the API versions, not the library versions
# - milestone: the skia milestone determined by Google/Chromium
# - increment: the C API version increment caused by new APIs
libSkiaSharp milestone 80
libSkiaSharp increment 0

# native sonames
libSkiaSharp soname 80.0.0
HarfBuzz soname 0.20601.0
Expand Down
198 changes: 198 additions & 0 deletions binding/Binding.Shared/LibraryLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
using System;
using System.IO;
using System.Runtime.InteropServices;

#if HARFBUZZ
namespace HarfBuzzSharp
#else
namespace SkiaSharp
#endif
{
#if USE_DELEGATES || USE_LIBRARY_LOADER
internal static class LibraryLoader
{
static LibraryLoader ()
{
if (PlatformConfiguration.IsWindows)
Extension = ".dll";
else if (PlatformConfiguration.IsMac)
Extension = ".dylib";
else
Extension = ".so";
}

public static string Extension { get; }

public static IntPtr LoadLocalLibrary<T> (string libraryName)
{
var libraryPath = GetLibraryPath (libraryName);

var handle = LoadLibrary (libraryPath);
if (handle == IntPtr.Zero)
throw new DllNotFoundException ($"Unable to load library '{libraryName}'.");

return handle;

static string GetLibraryPath (string libraryName)
{
var arch = IntPtr.Size == 8 ? "x64" : "x86";

var libWithExt = libraryName;
if (!libraryName.EndsWith (Extension, StringComparison.OrdinalIgnoreCase))
libWithExt += Extension;

// 1. try alongside managed assembly
var path = typeof (T).Assembly.Location;
if (!string.IsNullOrEmpty (path)) {
path = Path.GetDirectoryName (path);
// 1.1 in platform sub dir
var lib = Path.Combine (path, arch, libWithExt);
if (File.Exists (lib))
return lib;
// 1.2 in root
lib = Path.Combine (path, libWithExt);
if (File.Exists (lib))
return lib;
}

// 2. try current directory
path = Directory.GetCurrentDirectory ();
if (!string.IsNullOrEmpty (path)) {
// 2.1 in platform sub dir
var lib = Path.Combine (path, arch, libWithExt);
if (File.Exists (lib))
return lib;
// 2.2 in root
lib = Path.Combine (lib, libWithExt);
if (File.Exists (lib))
return lib;
}

// 3. use PATH or default loading mechanism
return libWithExt;
}
}

public static T GetSymbolDelegate<T> (IntPtr library, string name)
where T : Delegate
{
var symbol = GetSymbol (library, name);
if (symbol == IntPtr.Zero)
throw new EntryPointNotFoundException ($"Unable to load symbol '{name}'.");

#if NET45
return (T)Marshal.GetDelegateForFunctionPointer (symbol, typeof (T));
#else
return Marshal.GetDelegateForFunctionPointer<T> (symbol);
#endif
}

public static IntPtr LoadLibrary (string libraryName)
{
if (string.IsNullOrEmpty (libraryName))
throw new ArgumentNullException (nameof (libraryName));

IntPtr handle;
if (PlatformConfiguration.IsWindows)
handle = Win32.LoadLibrary (libraryName);
else if (PlatformConfiguration.IsLinux)
handle = Linux.dlopen (libraryName);
else if (PlatformConfiguration.IsMac)
handle = Mac.dlopen (libraryName);
else
throw new PlatformNotSupportedException ($"Current platform is unknown, unable to load library '{libraryName}'.");

return handle;
}

public static IntPtr GetSymbol (IntPtr library, string symbolName)
{
if (string.IsNullOrEmpty (symbolName))
throw new ArgumentNullException (nameof (symbolName));

IntPtr handle;
if (PlatformConfiguration.IsWindows)
handle = Win32.GetProcAddress (library, symbolName);
else if (PlatformConfiguration.IsLinux)
handle = Linux.dlsym (library, symbolName);
else if (PlatformConfiguration.IsMac)
handle = Mac.dlsym (library, symbolName);
else
throw new PlatformNotSupportedException ($"Current platform is unknown, unable to load symbol '{symbolName}' from library {library}.");

return handle;
}

public static void FreeLibrary (IntPtr library)
{
if (library == IntPtr.Zero)
return;

if (PlatformConfiguration.IsWindows)
Win32.FreeLibrary (library);
else if (PlatformConfiguration.IsLinux)
Linux.dlclose (library);
else if (PlatformConfiguration.IsMac)
Mac.dlclose (library);
else
throw new PlatformNotSupportedException ($"Current platform is unknown, unable to close library '{library}'.");
}

#pragma warning disable IDE1006 // Naming Styles
private static class Mac
{
private const string SystemLibrary = "/usr/lib/libSystem.dylib";

private const int RTLD_LAZY = 1;
private const int RTLD_NOW = 2;

public static IntPtr dlopen (string path, bool lazy = true) =>
dlopen (path, lazy ? RTLD_LAZY : RTLD_NOW);

[DllImport (SystemLibrary)]
public static extern IntPtr dlopen (string path, int mode);

[DllImport (SystemLibrary)]
public static extern IntPtr dlsym (IntPtr handle, string symbol);

[DllImport (SystemLibrary)]
public static extern void dlclose (IntPtr handle);
}

private static class Linux
{
private const string SystemLibrary = "libdl.so";

private const int RTLD_LAZY = 1;
private const int RTLD_NOW = 2;

public static IntPtr dlopen (string path, bool lazy = true) =>
dlopen (path, lazy ? RTLD_LAZY : RTLD_NOW);

[DllImport (SystemLibrary)]
public static extern IntPtr dlopen (string path, int mode);

[DllImport (SystemLibrary)]
public static extern IntPtr dlsym (IntPtr handle, string symbol);

[DllImport (SystemLibrary)]
public static extern void dlclose (IntPtr handle);
}

private static class Win32
{
private const string SystemLibrary = "Kernel32.dll";

[DllImport (SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibrary (string lpFileName);

[DllImport (SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress (IntPtr hModule, string lpProcName);

[DllImport (SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern void FreeLibrary (IntPtr hModule);
}
#pragma warning restore IDE1006 // Naming Styles
}
#endif
}
51 changes: 46 additions & 5 deletions binding/Binding.Shared/PlatformConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

#if HARFBUZZ
Expand All @@ -11,20 +10,62 @@ namespace SkiaSharp
internal static class PlatformConfiguration
{
public static bool IsUnix { get; }

public static bool IsWindows { get; }

public static bool IsMac { get; }

public static bool IsLinux { get; }

static PlatformConfiguration ()
{
#if WINDOWS_UWP
IsMac = false;
IsLinux = false;
IsUnix = false;
IsWindows = true;
#elif NET_STANDARD
IsUnix = RuntimeInformation.IsOSPlatform (OSPlatform.OSX) || RuntimeInformation.IsOSPlatform (OSPlatform.Linux);
IsWindows = RuntimeInformation.IsOSPlatform (OSPlatform.Windows);
#else
#elif NET45
IsUnix = Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix;
IsWindows = !IsUnix;
IsMac = IsUnix && MacPlatformDetector.IsMac.Value;
IsLinux = IsUnix && !IsMac;
#else
IsMac = RuntimeInformation.IsOSPlatform (OSPlatform.OSX);
IsLinux = RuntimeInformation.IsOSPlatform (OSPlatform.Linux);
IsUnix = IsMac || IsLinux;
IsWindows = RuntimeInformation.IsOSPlatform (OSPlatform.Windows);
#endif
}

#if NET45
#pragma warning disable IDE1006 // Naming Styles
private static class MacPlatformDetector
{
internal static readonly Lazy<bool> IsMac = new Lazy<bool> (IsRunningOnMac);

[DllImport ("libc")]
static extern int uname (IntPtr buf);

static bool IsRunningOnMac ()
{
IntPtr buf = IntPtr.Zero;
try {
buf = Marshal.AllocHGlobal (8192);
// This is a hacktastic way of getting sysname from uname ()
if (uname (buf) == 0) {
string os = Marshal.PtrToStringAnsi (buf);
if (os == "Darwin")
return true;
}
} catch {
} finally {
if (buf != IntPtr.Zero)
Marshal.FreeHGlobal (buf);
}
return false;
}
}
#pragma warning restore IDE1006 // Naming Styles
#endif
}
}
2 changes: 1 addition & 1 deletion binding/Binding/SKCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ public void Flush ()

public void DrawAnnotation (SKRect rect, string key, SKData value)
{
var bytes = StringUtilities.GetEncodedText (key, SKTextEncoding.Utf8);
var bytes = StringUtilities.GetEncodedText (key, SKTextEncoding.Utf8, true);
fixed (byte* b = bytes) {
SkiaApi.sk_canvas_draw_annotation (base.Handle, &rect, b, value == null ? IntPtr.Zero : value.Handle);
}
Expand Down
2 changes: 1 addition & 1 deletion binding/Binding/SKData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public static SKData Create (string filename)
if (string.IsNullOrEmpty (filename))
throw new ArgumentException ("The filename cannot be empty.", nameof (filename));

var utf8path = StringUtilities.GetEncodedText (filename, SKTextEncoding.Utf8);
var utf8path = StringUtilities.GetEncodedText (filename, SKTextEncoding.Utf8, true);
fixed (byte* u = utf8path) {
return GetObject (SkiaApi.sk_data_new_from_file (u));
}
Expand Down
2 changes: 1 addition & 1 deletion binding/Binding/SKFontManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public SKTypeface CreateTypeface (string path, int index = 0)
if (path == null)
throw new ArgumentNullException (nameof (path));

var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8, true);
fixed (byte* u = utf8path) {
return SKTypeface.GetObject (SkiaApi.sk_fontmgr_create_from_file (Handle, u, index));
}
Expand Down
2 changes: 2 additions & 0 deletions binding/Binding/SKObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ internal ConcurrentDictionary<IntPtr, SKObject> KeepAliveObjects {

static SKObject ()
{
SkiaSharpVersion.CheckNativeLibraryCompatible (true);

SKColorSpace.EnsureStaticInstanceAreInitialized ();
SKData.EnsureStaticInstanceAreInitialized ();
SKFontManager.EnsureStaticInstanceAreInitialized ();
Expand Down
4 changes: 2 additions & 2 deletions binding/Binding/SKStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public SKFileStream (string path)

private static IntPtr CreateNew (string path)
{
var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8, true);
fixed (byte* p = bytes) {
return SkiaApi.sk_filestream_new (p);
}
Expand Down Expand Up @@ -487,7 +487,7 @@ public SKFileWStream (string path)

private static IntPtr CreateNew (string path)
{
var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8, true);
fixed (byte* p = bytes) {
return SkiaApi.sk_filewstream_new (p);
}
Expand Down
2 changes: 1 addition & 1 deletion binding/Binding/SKTypeface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public static SKTypeface FromFile (string path, int index = 0)
if (path == null)
throw new ArgumentNullException (nameof (path));

var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8, true);
fixed (byte* u = utf8path) {
return GetObject (SkiaApi.sk_typeface_create_from_file (u, index));
}
Expand Down
18 changes: 13 additions & 5 deletions binding/Binding/SkiaApi.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace SkiaSharp
using System;

namespace SkiaSharp
{
internal partial class SkiaApi
{
Expand All @@ -12,14 +14,20 @@ internal partial class SkiaApi
private const string SKIA = "libSkiaSharp.so";
#elif __MACOS__
private const string SKIA = "libSkiaSharp.dylib";
#elif __DESKTOP__
private const string SKIA = "libSkiaSharp";
#elif WINDOWS_UWP
private const string SKIA = "libSkiaSharp.dll";
#elif NET_STANDARD
private const string SKIA = "libSkiaSharp";
#elif __TIZEN__
private const string SKIA = "libSkiaSharp.so";
#else
private const string SKIA = "libSkiaSharp";
#endif

#if USE_DELEGATES
private static readonly Lazy<IntPtr> libSkiaSharpHandle =
new Lazy<IntPtr> (() => LibraryLoader.LoadLocalLibrary<SkiaApi> (SKIA));

private static T GetSymbol<T> (string name) where T : Delegate =>
LibraryLoader.GetSymbolDelegate<T> (libSkiaSharpHandle.Value, name);
#endif
}
}
Loading