/// <summary>
        /// Try to preload the library.
        /// This is useful when we want to have AnyCPU .NET and CPU-specific native code.
        /// Only available on Windows for now.
        /// </summary>
        /// <param name="libraryName">Name of the library.</param>
        /// <param name="owner">Type whose assembly location is related to the native library (we can't use GetCallingAssembly as it might be wrong due to optimizations).</param>
        /// <exception cref="System.InvalidOperationException">Library could not be loaded.</exception>
        public static void PreloadLibrary(string libraryName, Type owner)
        {
#if STRIDE_PLATFORM_DESKTOP
            lock (LoadedLibraries)
            {
                // If already loaded, just exit as we want to load it just once
                if (LoadedLibraries.ContainsKey(libraryName))
                {
                    return;
                }

                string cpu;
                string platform;

                switch (RuntimeInformation.ProcessArchitecture)
                {
                case Architecture.X86:
                    cpu = "x86";
                    break;

                case Architecture.X64:
                    cpu = "x64";
                    break;

                case Architecture.Arm:
                    cpu = "ARM";
                    break;

                default:
                    throw new PlatformNotSupportedException();
                }

                switch (Platform.Type)
                {
                case PlatformType.Windows:
                    platform = "win";
                    break;

                case PlatformType.Linux:
                    platform = "linux";
                    break;

                case PlatformType.macOS:
                    platform = "osx";
                    break;

                default:
                    throw new PlatformNotSupportedException();
                }

                // We are trying to load the dll from a shadow path if it is already registered, otherwise we use it directly from the folder
                {
                    foreach (var libraryPath in new[]
                    {
                        Path.Combine(Path.GetDirectoryName(owner.GetTypeInfo().Assembly.Location), $"{platform}-{cpu}"),
                        Path.Combine(Environment.CurrentDirectory, $"{platform}-{cpu}"),
                        // Also try without platform for Windows-only packages (backward compat for editor packages)
                        Path.Combine(Path.GetDirectoryName(owner.GetTypeInfo().Assembly.Location), $"{cpu}"),
                        Path.Combine(Environment.CurrentDirectory, $"{cpu}"),
                    })
                    {
                        var libraryFilename = Path.Combine(libraryPath, libraryName);
                        if (NativeLibrary.TryLoad(libraryFilename, out var result))
                        {
                            LoadedLibraries.Add(libraryName.ToLowerInvariant(), result);
                            return;
                        }
                    }
                }

                // Attempt to load it from PATH
                foreach (var p in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator))
                {
                    var libraryFilename = Path.Combine(p, libraryName);
                    if (NativeLibrary.TryLoad(libraryFilename, out var result))
                    {
                        LoadedLibraries.Add(libraryName.ToLowerInvariant(), result);
                        return;
                    }
                }

                throw new InvalidOperationException($"Could not load native library {libraryName} using CPU architecture {cpu}.");
            }
#endif
        }
예제 #2
0
        /// <summary>
        /// Try to preload the library.
        /// This is useful when we want to have AnyCPU .NET and CPU-specific native code.
        /// Only available on Windows for now.
        /// </summary>
        /// <param name="libraryName">Name of the library, without the extension.</param>
        /// <param name="owner">Type whose assembly location is related to the native library (we can't use GetCallingAssembly as it might be wrong due to optimizations).</param>
        /// <exception cref="System.InvalidOperationException">Library could not be loaded.</exception>
        public static void PreloadLibrary(string libraryName, Type owner)
        {
#if STRIDE_PLATFORM_DESKTOP
            lock (LoadedLibraries)
            {
                // If already loaded, just exit as we want to load it just once
                if (LoadedLibraries.ContainsKey(libraryName))
                {
                    return;
                }

                string cpu;
                string platform;
                string extension;

                switch (RuntimeInformation.ProcessArchitecture)
                {
                case Architecture.X86:
                    cpu = "x86";
                    break;

                case Architecture.X64:
                    cpu = "x64";
                    break;

                case Architecture.Arm:
                    cpu = "ARM";
                    break;

                default:
                    throw new PlatformNotSupportedException();
                }

                switch (Platform.Type)
                {
                case PlatformType.Windows:
                    platform = "win";
                    break;

                case PlatformType.Linux:
                    platform = "linux";
                    break;

                case PlatformType.macOS:
                    platform = "osx";
                    break;

                default:
                    throw new PlatformNotSupportedException();
                }

                switch (Platform.Type)
                {
                case PlatformType.Windows:
                    extension = ".dll";
                    break;

                case PlatformType.Linux:
                    extension = ".so";
                    break;

                case PlatformType.macOS:
                    extension = ".dylib";
                    break;

                default:
                    throw new PlatformNotSupportedException();
                }

                var libraryNameWithExtension = libraryName + extension;

                if (Platform.Type != PlatformType.Windows)
                {
                    // on linux/macos opening a library without a path will look it up in the global library locations
                    // e.g. /lib/x86_64-linux-gnu, /lib, /usr/lib, etc.
                    if (NativeLibrary.TryLoad(libraryNameWithExtension, out var result))
                    {
                        LoadedLibraries.Add(libraryName.ToLowerInvariant(), result);
                        return;
                    }
                    else if (!libraryName.StartsWith(UNIX_LIB_PREFIX) &&
                             NativeLibrary.TryLoad(UNIX_LIB_PREFIX + libraryNameWithExtension, out result))
                    {
                        LoadedLibraries.Add(libraryName.ToLowerInvariant(), result);
                        return;
                    }
                }

                // We are trying to load the dll from a shadow path if it is already registered, otherwise we use it directly from the folder
                {
                    var platformNativeLibsFolder = $"{platform}-{cpu}";
                    foreach (var libraryPath in new[]
                    {
                        Path.Combine(Path.GetDirectoryName(owner.GetTypeInfo().Assembly.Location) ?? string.Empty, platformNativeLibsFolder),
                        Path.Combine(Environment.CurrentDirectory ?? string.Empty, platformNativeLibsFolder),
                        Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) ?? string.Empty, platformNativeLibsFolder),
                        // Also try without platform for Windows-only packages (backward compat for editor packages)
                        Path.Combine(Path.GetDirectoryName(owner.GetTypeInfo().Assembly.Location) ?? string.Empty, cpu),
                        Path.Combine(Environment.CurrentDirectory ?? string.Empty, cpu),
                    })
                    {
                        var libraryFilename = Path.Combine(libraryPath, libraryNameWithExtension);
                        if (NativeLibrary.TryLoad(libraryFilename, out var result))
                        {
                            LoadedLibraries.Add(libraryName.ToLowerInvariant(), result);
                            return;
                        }
                    }
                }

                // Attempt to load it from PATH
                foreach (var p in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator))
                {
                    var libraryFilename = Path.Combine(p, libraryNameWithExtension);
                    if (NativeLibrary.TryLoad(libraryFilename, out var result))
                    {
                        LoadedLibraries.Add(libraryName.ToLowerInvariant(), result);
                        return;
                    }
                }

                throw new InvalidOperationException($"Could not load native library {libraryName} using CPU architecture {cpu}.");
            }
#endif
        }