diff --git a/README.md b/README.md index 03e37dd..95ccc0a 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,12 @@ Java bindings to the [tree-sitter] parsing library. - Install JDK 22 and set `JAVA_HOME` to it - Download [jextract] and add it to your `PATH` +- Install the `tree-sitter` & `tree-sitter-java` libraries ```bash git clone https://github.com/tree-sitter/java-tree-sitter cd java-tree-sitter -git submodule init +git submodule update --init mvn test ``` diff --git a/scripts/TreeSitter_java.patch b/scripts/TreeSitter_java.patch index 5694788..ffe070c 100644 --- a/scripts/TreeSitter_java.patch +++ b/scripts/TreeSitter_java.patch @@ -1,23 +1,14 @@ --- a/generated-sources/jextract/io/github/treesitter/jtreesitter/internal/TreeSitter.java +++ b/generated-sources/jextract/io/github/treesitter/jtreesitter/internal/TreeSitter.java -@@ -55,9 +55,16 @@ public class TreeSitter { +@@ -55,9 +55,7 @@ }; } - + - static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("tree-sitter"), LIBRARY_ARENA) - .or(SymbolLookup.loaderLookup()) - .or(Linker.nativeLinker().defaultLookup()); -+ static final SymbolLookup SYMBOL_LOOKUP = findLibrary().or(Linker.nativeLinker().defaultLookup()); -+ -+ private static final SymbolLookup findLibrary() { -+ try { -+ String library = System.mapLibraryName("tree-sitter"); -+ return SymbolLookup.libraryLookup(library, LIBRARY_ARENA); -+ } catch (IllegalArgumentException e) { -+ return SymbolLookup.loaderLookup(); -+ } -+ } - ++ static final SymbolLookup SYMBOL_LOOKUP = ChainedLibraryLookup.INSTANCE.get(LIBRARY_ARENA); + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; @@ -8599,4 +8606,8 @@ public class TreeSitter { diff --git a/src/main/java/io/github/treesitter/jtreesitter/NativeLibraryLookup.java b/src/main/java/io/github/treesitter/jtreesitter/NativeLibraryLookup.java new file mode 100644 index 0000000..a84c033 --- /dev/null +++ b/src/main/java/io/github/treesitter/jtreesitter/NativeLibraryLookup.java @@ -0,0 +1,22 @@ +package io.github.treesitter.jtreesitter; + +import java.lang.foreign.Arena; +import java.lang.foreign.SymbolLookup; + +/** + * An interface implemented by clients that wish to customize the {@link SymbolLookup} + * used for the tree-sitter native library. Implementations must be registered + * by listing their fully qualified class name in a resource file named + * {@code META-INF/services/io.github.treesitter.jtreesitter.NativeLibraryLookup}. + * + * @see java.util.ServiceLoader + */ +@FunctionalInterface +public interface NativeLibraryLookup { + /** + * Get the {@link SymbolLookup} to be used for the tree-sitter native library. + * + * @param arena The arena that will manage the native memory. + */ + SymbolLookup get(Arena arena); +} diff --git a/src/main/java/io/github/treesitter/jtreesitter/internal/ChainedLibraryLookup.java b/src/main/java/io/github/treesitter/jtreesitter/internal/ChainedLibraryLookup.java new file mode 100644 index 0000000..ac03f19 --- /dev/null +++ b/src/main/java/io/github/treesitter/jtreesitter/internal/ChainedLibraryLookup.java @@ -0,0 +1,35 @@ +package io.github.treesitter.jtreesitter.internal; + +import io.github.treesitter.jtreesitter.NativeLibraryLookup; +import java.lang.foreign.Arena; +import java.lang.foreign.Linker; +import java.lang.foreign.SymbolLookup; +import java.util.Optional; +import java.util.ServiceLoader; + +@SuppressWarnings("unused") +final class ChainedLibraryLookup implements NativeLibraryLookup { + private ChainedLibraryLookup() {} + + static ChainedLibraryLookup INSTANCE = new ChainedLibraryLookup(); + + @Override + public SymbolLookup get(Arena arena) { + var serviceLoader = ServiceLoader.load(NativeLibraryLookup.class); + // NOTE: can't use _ because of palantir/palantir-java-format#934 + SymbolLookup lookup = (name) -> Optional.empty(); + for (var libraryLookup : serviceLoader) { + lookup = lookup.or(libraryLookup.get(arena)); + } + return lookup.or(findLibrary(arena)).or(Linker.nativeLinker().defaultLookup()); + } + + private static SymbolLookup findLibrary(Arena arena) { + try { + var library = System.mapLibraryName("tree-sitter"); + return SymbolLookup.libraryLookup(library, arena); + } catch (IllegalArgumentException e) { + return SymbolLookup.loaderLookup(); + } + } +} diff --git a/src/main/java/io/github/treesitter/jtreesitter/package-info.java b/src/main/java/io/github/treesitter/jtreesitter/package-info.java index c7733bd..c2f1282 100644 --- a/src/main/java/io/github/treesitter/jtreesitter/package-info.java +++ b/src/main/java/io/github/treesitter/jtreesitter/package-info.java @@ -15,8 +15,6 @@ *
  • Shared libraries for languages
  • * * - * The shared libraries must be installed system-wide or in {@systemProperty java.library.path} - * *

    Basic Usage

    * * {@snippet lang = java: @@ -31,6 +29,30 @@ * } *} * - * @see Library Loading + *

    Library Loading

    + * + * There are three ways to load the shared libraries: + * + *
      + *
    1. + * The libraries can be installed in the OS-specific library search path or in + * {@systemProperty java.library.path}. The search path can be amended using the + * {@code LD_LIBRARY_PATH} environment variable on Linux, {@code DYLD_LIBRARY_PATH} + * on macOS, or {@code PATH} on Windows. The libraries will be loaded automatically by + * {@link java.lang.foreign.SymbolLookup#libraryLookup(String, java.lang.foreign.Arena) + * SymbolLookup.libraryLookup(String, Arena)}. + *
    2. + *
    3. + * The libraries can be loaded manually by calling + * {@link java.lang.System#loadLibrary(String) System.loadLibrary(String)}, + * if the library is installed in {@systemProperty java.library.path}, + * or {@link java.lang.System#load(String) System.load(String)}. + *
    4. + *
    5. + * The libraries can be loaded manually by registering a custom implementation of + * {@link io.github.treesitter.jtreesitter.NativeLibraryLookup NativeLibraryLookup}. + * This can be used, for example, to load libraries from inside a JAR file. + *
    6. + *
    */ package io.github.treesitter.jtreesitter;