You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

NativeLibraryUtils.cs 6.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using LLama.Abstractions;
  2. using LLama.Exceptions;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Runtime.InteropServices;
  7. namespace LLama.Native
  8. {
  9. internal static class NativeLibraryUtils
  10. {
  11. /// <summary>
  12. /// Try to load libllama/llava_shared, using CPU feature detection to try and load a more specialised DLL if possible
  13. /// </summary>
  14. /// <returns>The library handle to unload later, or IntPtr.Zero if no library was loaded</returns>
  15. internal static IntPtr TryLoadLibrary(NativeLibraryConfig config, out INativeLibrary? loadedLibrary)
  16. {
  17. #if NET6_0_OR_GREATER
  18. var description = config.CheckAndGatherDescription();
  19. var systemInfo = SystemInfo.Get();
  20. Log($"Loading library: '{config.NativeLibraryName.GetLibraryName()}'", LLamaLogLevel.Debug, config.LogCallback);
  21. // Get platform specific parts of the path (e.g. .so/.dll/.dylib, libName prefix or not)
  22. NativeLibraryUtils.GetPlatformPathParts(systemInfo.OSPlatform, out var os, out var ext, out var libPrefix);
  23. Log($"Detected OS Platform: '{systemInfo.OSPlatform}'", LLamaLogLevel.Info, config.LogCallback);
  24. Log($"Detected OS string: '{os}'", LLamaLogLevel.Debug, config.LogCallback);
  25. Log($"Detected extension string: '{ext}'", LLamaLogLevel.Debug, config.LogCallback);
  26. Log($"Detected prefix string: '{libPrefix}'", LLamaLogLevel.Debug, config.LogCallback);
  27. // Set the flag to ensure this config can no longer be modified
  28. config.LibraryHasLoaded = true;
  29. // Show the configuration we're working with
  30. Log(description.ToString(), LLamaLogLevel.Info, config.LogCallback);
  31. // Get the libraries ordered by priority from the selecting policy.
  32. var libraries = config.SelectingPolicy.Apply(description, systemInfo, config.LogCallback);
  33. foreach (var library in libraries)
  34. {
  35. // Prepare the local library file and get the path.
  36. var paths = library.Prepare(systemInfo, config.LogCallback);
  37. foreach (var path in paths)
  38. {
  39. Log($"Got relative library path '{path}' from local with {library.Metadata}, trying to load it...", LLamaLogLevel.Debug, config.LogCallback);
  40. var result = TryLoad(path, description.SearchDirectories, config.LogCallback);
  41. if (result != IntPtr.Zero)
  42. {
  43. loadedLibrary = library;
  44. return result;
  45. }
  46. }
  47. }
  48. // If fallback is allowed, we will make the last try (the default system loading) when calling the native api.
  49. // Otherwise we throw an exception here.
  50. if (!description.AllowFallback)
  51. {
  52. throw new RuntimeError("Failed to load the native library. Please check the log for more information.");
  53. }
  54. loadedLibrary = null;
  55. #else
  56. loadedLibrary = new UnknownNativeLibrary();
  57. #endif
  58. Log($"No library was loaded before calling native apis. " +
  59. $"This is not an error under netstandard2.0 but needs attention with net6 or higher.", LLamaLogLevel.Warning, config.LogCallback);
  60. return IntPtr.Zero;
  61. #if NET6_0_OR_GREATER
  62. // Try to load a DLL from the path.
  63. // Returns null if nothing is loaded.
  64. static IntPtr TryLoad(string path, IEnumerable<string> searchDirectories, NativeLogConfig.LLamaLogCallback? logCallback)
  65. {
  66. var fullPath = TryFindPath(path, searchDirectories);
  67. Log($"Found full path file '{fullPath}' for relative path '{path}'", LLamaLogLevel.Debug, logCallback);
  68. if (NativeLibrary.TryLoad(fullPath, out var handle))
  69. {
  70. Log($"Successfully loaded '{fullPath}'", LLamaLogLevel.Info, logCallback);
  71. return handle;
  72. }
  73. Log($"Failed Loading '{fullPath}'", LLamaLogLevel.Info, logCallback);
  74. return IntPtr.Zero;
  75. }
  76. #endif
  77. }
  78. // Try to find the given file in any of the possible search paths
  79. private static string TryFindPath(string filename, IEnumerable<string> searchDirectories)
  80. {
  81. // Try the configured search directories in the configuration
  82. foreach (var path in searchDirectories)
  83. {
  84. var candidate = Path.Combine(path, filename);
  85. if (File.Exists(candidate))
  86. return candidate;
  87. }
  88. // Try a few other possible paths
  89. var possiblePathPrefix = new[] {
  90. AppDomain.CurrentDomain.BaseDirectory,
  91. Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? ""
  92. };
  93. foreach (var path in possiblePathPrefix)
  94. {
  95. var candidate = Path.Combine(path, filename);
  96. if (File.Exists(candidate))
  97. return candidate;
  98. }
  99. return filename;
  100. }
  101. private static void Log(string message, LLamaLogLevel level, NativeLogConfig.LLamaLogCallback? logCallback)
  102. {
  103. if (!message.EndsWith("\n"))
  104. message += "\n";
  105. logCallback?.Invoke(level, message);
  106. }
  107. #if NET6_0_OR_GREATER
  108. public static void GetPlatformPathParts(OSPlatform platform, out string os, out string fileExtension, out string libPrefix)
  109. {
  110. if (platform == OSPlatform.Windows)
  111. {
  112. os = "win-x64";
  113. fileExtension = ".dll";
  114. libPrefix = "";
  115. return;
  116. }
  117. if (platform == OSPlatform.Linux)
  118. {
  119. os = "linux-x64";
  120. fileExtension = ".so";
  121. libPrefix = "lib";
  122. return;
  123. }
  124. if (platform == OSPlatform.OSX)
  125. {
  126. fileExtension = ".dylib";
  127. os = System.Runtime.Intrinsics.Arm.ArmBase.Arm64.IsSupported
  128. ? "osx-arm64"
  129. : "osx-x64";
  130. libPrefix = "lib";
  131. }
  132. else
  133. {
  134. throw new RuntimeError("Your operating system is not supported, please open an issue in LLamaSharp.");
  135. }
  136. }
  137. #endif
  138. }
  139. }