diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd861900..089d6905 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ All notable changes to NetVips will be documented in this file. See [here](CHANG
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.0.0] - TBD
+### Added
+- Add support for a single shared libvips binary on Windows ([#211](https://github.com/kleisauke/net-vips/issues/211)).
+
### Removed
- Drop support for .NET Standard 2.0 and Mono. NetVips now targets .NET 6 (`net6.0`) and .NET Framework 4.5.2 (`net452`) moving forward.
See https://devblogs.microsoft.com/dotnet/the-future-of-net-standard/ for more information.
diff --git a/build/Build.cs b/build/Build.cs
index 5f127d12..6d8ffd29 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -132,21 +132,10 @@ protected override void OnBuildInitialized()
.DependsOn(Clean)
.Executes(() =>
{
- // Need to build the macOS and *nix DLL first.
- DotNetBuild(c => c
- .SetProjectFile(Solution.NetVips)
- .SetConfiguration(Configuration)
- .SetFramework("netstandard2.0")
- .AddProperty("Platform", "AnyCPU")
- .CombineWith(
- new[] { "OSX", "Unix" },
- (_, os) => _.AddProperty("TargetOS", os)));
-
DotNetPack(c => c
.SetProject(Solution.NetVips)
.SetConfiguration(Configuration)
.SetOutputDirectory(ArtifactsDirectory)
- .AddProperty("TargetOS", "Windows")
);
});
diff --git a/src/NetVips/Interop/Libraries.OSX.cs b/src/NetVips/Interop/Libraries.OSX.cs
deleted file mode 100644
index 572edcf2..00000000
--- a/src/NetVips/Interop/Libraries.OSX.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace NetVips.Interop
-{
- internal static partial class Libraries
- {
- // We can safely define all these variables as `libvips.42.dylib` since
- // DLLImport uses dlsym() on macOS. This function also searches for named
- // symbols in the dependencies of the shared library. Therefore, we can
- // provide libvips as a single shared library with all dependencies
- // statically linked without breaking compatibility with shared builds
- // (i.e. what is usually installed via package managers).
- internal const string GLib = "libvips.42.dylib",
- GObject = "libvips.42.dylib",
- Vips = "libvips.42.dylib";
- }
-}
\ No newline at end of file
diff --git a/src/NetVips/Interop/Libraries.Unix.cs b/src/NetVips/Interop/Libraries.Unix.cs
deleted file mode 100644
index 93d2e274..00000000
--- a/src/NetVips/Interop/Libraries.Unix.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace NetVips.Interop
-{
- internal static partial class Libraries
- {
- // We can safely define all these variables as `libvips.so.42` since
- // DLLImport uses dlsym() on *nix. This function also searches for named
- // symbols in the dependencies of the shared library. Therefore, we can
- // provide libvips as a single shared library with all dependencies
- // statically linked without breaking compatibility with shared builds
- // (i.e. what is usually installed via package managers).
- internal const string GLib = "libvips.so.42",
- GObject = "libvips.so.42",
- Vips = "libvips.so.42";
- }
-}
\ No newline at end of file
diff --git a/src/NetVips/Interop/Libraries.Windows.cs b/src/NetVips/Interop/Libraries.Windows.cs
deleted file mode 100644
index 002daed4..00000000
--- a/src/NetVips/Interop/Libraries.Windows.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace NetVips.Interop
-{
- internal static partial class Libraries
- {
- // We cannot define all these variables as `libvips-42.dll` without
- // breaking compatibility with the shared Windows build. Therefore,
- // we always ship at least 3 DLLs.
- internal const string GLib = "libglib-2.0-0.dll",
- GObject = "libgobject-2.0-0.dll",
- Vips = "libvips-42.dll";
- }
-}
\ No newline at end of file
diff --git a/src/NetVips/Interop/Libraries.cs b/src/NetVips/Interop/Libraries.cs
new file mode 100644
index 00000000..dcb80b4a
--- /dev/null
+++ b/src/NetVips/Interop/Libraries.cs
@@ -0,0 +1,13 @@
+namespace NetVips.Interop
+{
+ internal static class Libraries
+ {
+ ///
+ /// These library names are remapped in a cross-platform manner,
+ /// .
+ ///
+ internal const string GLib = "libglib-2.0-0.dll",
+ GObject = "libgobject-2.0-0.dll",
+ Vips = "libvips-42.dll";
+ }
+}
\ No newline at end of file
diff --git a/src/NetVips/ModuleInitializer.cs b/src/NetVips/ModuleInitializer.cs
index 1f9e971b..e428d4c7 100644
--- a/src/NetVips/ModuleInitializer.cs
+++ b/src/NetVips/ModuleInitializer.cs
@@ -1,6 +1,10 @@
namespace NetVips
{
using System;
+ using System.Reflection;
+ using System.Collections.Generic;
+ using System.Runtime.InteropServices;
+ using Interop;
///
/// All code inside the method is ran as soon as the assembly is loaded.
@@ -22,6 +26,63 @@ public static class ModuleInitializer
///
public static int? Version;
+#if NET6_0_OR_GREATER
+ ///
+ /// Windows specific: is GLib statically-linked in `libvips-42.dll`?
+ ///
+ [System.Runtime.Versioning.SupportedOSPlatform("windows")]
+ private static bool _gLibStaticallyLinked = true;
+
+ ///
+ /// A cache for .
+ ///
+ internal static readonly Dictionary DllImportCache =
+ new Dictionary();
+
+ internal static string RemapLibraryName(string libraryName)
+ {
+ // For Windows, we try to locate the GLib symbols within
+ // `libvips-42.dll` first. If these symbols cannot be found there,
+ // we proceed to locate them within `libglib-2.0-0.dll` and
+ // `libgobject-2.0-0.dll`. Note that distributing a single shared
+ // library is only possible when we drop support for .NET Framework.
+ // Therefore, we always ship at least 3 DLLs for now.
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return _gLibStaticallyLinked ? Libraries.Vips : libraryName;
+ }
+
+ // We can safely remap the library names to `libvips.so.42` on *nix
+ // and `libvips.42.dylib` on macOS since DLLImport uses dlsym() there.
+ // This function also searches for named symbols in the dependencies
+ // of the shared library. Therefore, we can provide libvips as a
+ // single shared library with all dependencies statically linked
+ // without breaking compatibility with the shared builds
+ // (i.e. what is usually installed via package managers).
+ return RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
+ ? "libvips.42.dylib"
+ : "libvips.so.42";
+ }
+
+ internal static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ libraryName = RemapLibraryName(libraryName);
+ if (DllImportCache.TryGetValue(libraryName, out var cachedHandle))
+ {
+ return cachedHandle;
+ }
+
+ if (NativeLibrary.TryLoad(libraryName, assembly, searchPath, out var handle))
+ {
+ DllImportCache[libraryName] = handle;
+ return handle;
+ }
+
+ // Fallback to the default import resolver.
+ return IntPtr.Zero;
+ }
+#endif
+
///
/// Initializes the module.
///
@@ -30,6 +91,10 @@ public static class ModuleInitializer
#pragma warning restore CA2255
public static void Initialize()
{
+#if NET6_0_OR_GREATER
+ NativeLibrary.SetDllImportResolver(typeof(ModuleInitializer).Assembly, DllImportResolver);
+#endif
+
try
{
VipsInitialized = NetVips.Init();
@@ -38,6 +103,19 @@ public static void Initialize()
Version = NetVips.Version(0, false);
Version = (Version << 8) + NetVips.Version(1, false);
Version = (Version << 8) + NetVips.Version(2, false);
+
+#if NET6_0_OR_GREATER
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
+
+ try
+ {
+ _gLibStaticallyLinked = NetVips.TypeFromName("VipsImage") != IntPtr.Zero;
+ }
+ catch
+ {
+ _gLibStaticallyLinked = false;
+ }
+#endif
}
else
{
diff --git a/src/NetVips/NetVips.csproj b/src/NetVips/NetVips.csproj
index 4753f587..d2c763e9 100644
--- a/src/NetVips/NetVips.csproj
+++ b/src/NetVips/NetVips.csproj
@@ -11,30 +11,12 @@
x64;x86;ARM64;ARM32true
- $(DefaultItemExcludes);Interop\Libraries.*.csfalse
- falsetruetrue
-
-
- Unix
- OSX
- Windows
-
-
-
-
- $(MSBuildThisFileDirectory)bin\$(Platform)\$(Configuration)\$(TargetFramework)\$(TargetOS)
- $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\$(TargetFramework)\$(TargetOS)\
-
-
-
- Interop\Libraries.cs
-
@@ -43,16 +25,4 @@
-
-
-
- true
- runtimes\unix\lib\netstandard2.0
-
-
- true
- runtimes\osx\lib\netstandard2.0
-
-
-
\ No newline at end of file