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.4 kB

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