diff --git a/README.md b/README.md index b63043a..d936005 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,11 @@ dependencies { } ``` +> [!NOTE] +> ### Alternative: Custom Native Library +> +> You can bring your own `libdave` binary by using `-Djdave.library.path=/path/to/libdave.so` as a JVM argument or setting it using `System.setProperty(NativeLibraryLoader.LIBRARY_PATH_PROPERTY, path)` before loading the `LibDave` class. + ## Example: JDA To use this library with [JDA](https://github.com/discord-jda/JDA), you can use the [JDaveSessionFactory](api/src/main/java/club/minnced/discord/jdave/interop/JDaveSessionFactory.java) to configure the audio module: diff --git a/api/src/main/java/club/minnced/discord/jdave/utils/NativeLibraryLoader.java b/api/src/main/java/club/minnced/discord/jdave/utils/NativeLibraryLoader.java index 1302b9f..7ed7999 100644 --- a/api/src/main/java/club/minnced/discord/jdave/utils/NativeLibraryLoader.java +++ b/api/src/main/java/club/minnced/discord/jdave/utils/NativeLibraryLoader.java @@ -11,8 +11,27 @@ import java.util.StringJoiner; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class NativeLibraryLoader { + public static final String LIBRARY_PATH_PROPERTY = "jdave.library.path"; + + private static final Logger log = LoggerFactory.getLogger(NativeLibraryLoader.class); + + /** + * Returns the path to a local libdave binary, if set. + * + *

Uses {@link #LIBRARY_PATH_PROPERTY} to determine the path from system properties. + * If the property is not set, this method returns null. + * + * @return The path to a local libdave binary, or null if not set. + */ + @Nullable + public static String getLibraryPath() { + return System.getProperty(LIBRARY_PATH_PROPERTY); + } + @NonNull public static NativeLibrary getNativeLibrary() { return resolveLibrary("dave"); @@ -46,6 +65,11 @@ public static Path createTemporaryFile() { @NonNull public static SymbolLookup getSymbolLookup() { + String customLibraryPath = getLibraryPath(); + if (customLibraryPath != null) { + return getSymbolLookupFromPath(customLibraryPath); + } + Path tempFile = createTemporaryFile(); return SymbolLookup.libraryLookup(tempFile, Arena.global()); } @@ -68,6 +92,23 @@ public static NativeLibrary resolveLibrary( return new NativeLibrary(os, arch, baseName); } + @NonNull + private static SymbolLookup getSymbolLookupFromPath(@NonNull String customLibraryPath) { + Path path = Path.of(customLibraryPath).toAbsolutePath(); + log.debug("Loading library from custom path: {}", path); + if (!Files.exists(path)) { + throw new IllegalStateException("Could not find library at path: " + path); + } + if (!Files.isRegularFile(path)) { + throw new IllegalStateException("Path is not a regular file: " + path); + } + if (!Files.isReadable(path)) { + throw new IllegalStateException("Path is not readable: " + path); + } + + return SymbolLookup.libraryLookup(path, Arena.global()); + } + @NonNull private static OperatingSystem getOperatingSystem(@NonNull String osName) { osName = osName.toLowerCase();