Explorar el Código

Improved NativeDependencyCompatibilityChecker

Marcin Ziąbek hace 1 año
padre
commit
a023706088

+ 20 - 0
Source/QuestPDF/Helpers/CompilerFeatureRequiredAttribute.cs

@@ -0,0 +1,20 @@
+#if !NET7_0_OR_GREATER
+
+namespace System.Runtime.CompilerServices
+{
+    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+    public sealed class CompilerFeatureRequiredAttribute : Attribute
+    {
+        public const string RefStructs = nameof(RefStructs);
+        public const string RequiredMembers = nameof(RequiredMembers);
+        public CompilerFeatureRequiredAttribute(string featureName)
+        {
+            FeatureName = featureName;
+        }
+        
+        public string FeatureName { get; }
+        public bool IsOptional { get; init; }
+    }
+}
+
+#endif

+ 29 - 8
Source/QuestPDF/Helpers/NativeDependencyCompatibilityChecker.cs

@@ -1,20 +1,36 @@
 using System;
 using System.Linq;
 using System.Runtime.InteropServices;
-using QuestPDF.Skia;
 
 namespace QuestPDF.Helpers
 {
-    internal static class NativeDependencyCompatibilityChecker
+    internal class NativeDependencyCompatibilityChecker
     {
-        public static void Test(Action executeNativeCode)
+        private bool IsCompatibilityChecked { get; set; } = false;
+
+        public Action ExecuteNativeCode { get; set; } = () => { };
+        public Func<string> ExceptionHint { get; set; } = () => string.Empty;
+        
+        public void Test()
+        {
+            if (IsCompatibilityChecked)
+                return;
+
+            TestOnce();
+            IsCompatibilityChecked = true;
+        }
+        
+        private void TestOnce()
         {
+            if (IsCompatibilityChecked)
+                return;
+            
             const string exceptionBaseMessage = "The QuestPDF library has encountered an issue while loading one of its dependencies.";
             const string paragraph = "\n\n";
                 
             // test with dotnet-based mechanism where native files are provided
             // in the "runtimes/{rid}/native" folder on Core, or by the targets file on .NET Framework
-            var innerException = CheckIfExceptionIsThrownWhenLoadingNativeDependencies(executeNativeCode);
+            var innerException = CheckIfExceptionIsThrownWhenLoadingNativeDependencies();
 
             if (innerException == null)
                 return;
@@ -25,14 +41,14 @@ namespace QuestPDF.Helpers
             // detect platform, copy appropriate native files and test compatibility again
             NativeDependencyProvider.EnsureNativeFileAvailability();
             
-            innerException = CheckIfExceptionIsThrownWhenLoadingNativeDependencies(executeNativeCode);
+            innerException = CheckIfExceptionIsThrownWhenLoadingNativeDependencies();
 
             if (innerException == null)
                 return;
 
             ThrowCompatibilityException(innerException);
             
-            static void ThrowCompatibilityException(Exception innerException)
+            void ThrowCompatibilityException(Exception innerException)
             {
                 var supportedRuntimes = string.Join(", ", NativeDependencyProvider.SupportedPlatforms);
                 var currentRuntime = NativeDependencyProvider.GetRuntimePlatform();
@@ -56,16 +72,21 @@ namespace QuestPDF.Helpers
                 
                 if (RuntimeInformation.ProcessArchitecture is Architecture.Arm)
                     message += $"{paragraph}Please consider setting the 'Platform target' property to 'Arm64' in your project settings.";
+
+                var hint = ExceptionHint.Invoke();
+                
+                if (!string.IsNullOrEmpty(hint))
+                    message += $"{paragraph}{ExceptionHint}";
                 
                 throw new Exception(message, innerException);
             }
         }
     
-        private static Exception? CheckIfExceptionIsThrownWhenLoadingNativeDependencies(Action executeNativeCode)
+        private Exception? CheckIfExceptionIsThrownWhenLoadingNativeDependencies()
         {
             try
             {
-                executeNativeCode();
+                ExecuteNativeCode();
                 return null;
             }
             catch (Exception exception)

+ 29 - 10
Source/QuestPDF/Qpdf/QpdfNativeDependencyCompatibilityChecker.cs

@@ -5,22 +5,41 @@ namespace QuestPDF.Qpdf;
 
 internal static class QpdfNativeDependencyCompatibilityChecker
 {
-    private static bool IsCompatibilityChecked = false;
+    private static NativeDependencyCompatibilityChecker Instance { get; } = new()
+    {
+        ExecuteNativeCode = ExecuteNativeCode,
+        ExceptionHint = GetHint
+    };
     
     public static void Test()
     {
-        if (IsCompatibilityChecked)
-            return;
+        Instance.Test();
+    }
+    
+    private static void ExecuteNativeCode()
+    {
+        var qpdfVersion = QpdfAPI.GetQpdfVersion();
         
-        NativeDependencyCompatibilityChecker.Test(ExecuteNativeCode);
-        IsCompatibilityChecked = true;
+        if (string.IsNullOrEmpty(qpdfVersion))
+            throw new Exception();
+    }
 
-        void ExecuteNativeCode()
+    private static string GetHint()
+    {
+        var platform = NativeDependencyProvider.GetRuntimePlatform();
+        
+        if (!platform.StartsWith("linux"))
+            return string.Empty;
+        
+        const string openSslHint = "Please also ensure that the OpenSSL library is installed on your system with version at least 3.0.0.";
+        
+        var command = platform switch
         {
-            var qpdfVersion = QpdfAPI.GetQpdfVersion();
+            "linux-x64" or "linux-arm64" => "apt install openssl-bin gnutls-bin libjpeg-dev",
+            "linux-musl-x64" => "apk add openssl gnutls libjpeg-turbo",
+            _ => throw new NotSupportedException()
+        };
         
-            if (string.IsNullOrEmpty(qpdfVersion))
-                throw new Exception();
-        }
+        return $"Installing additional dependencies may help. Likely command: '{command}'. {openSslHint}";
     }
 }

+ 15 - 16
Source/QuestPDF/Skia/SkNativeDependencyCompatibilityChecker.cs

@@ -7,29 +7,28 @@ namespace QuestPDF.Skia;
 
 internal static class SkNativeDependencyCompatibilityChecker
 {
-    private static bool IsCompatibilityChecked = false;
+    private static NativeDependencyCompatibilityChecker Instance { get; } = new()
+    {
+        ExecuteNativeCode = ExecuteNativeCode
+    };
     
     public static void Test()
     {
-        if (IsCompatibilityChecked)
-            return;
-        
-        NativeDependencyCompatibilityChecker.Test(ExecuteNativeCode);
-        IsCompatibilityChecked = true;
+        Instance.Test();
+    }
 
-        void ExecuteNativeCode()
-        {
-            var random = new Random();
+    private static void ExecuteNativeCode()
+    {
+        var random = new Random();
             
-            var a = random.Next();
-            var b = random.Next();
+        var a = random.Next();
+        var b = random.Next();
         
-            var expected = a + b;
-            var returned = API.check_compatibility_by_calculating_sum(a, b);
+        var expected = a + b;
+        var returned = API.check_compatibility_by_calculating_sum(a, b);
         
-            if (expected != returned)
-                throw new Exception();
-        }
+        if (expected != returned)
+            throw new Exception();
     }
     
     private static class API