graal conf
This commit is contained in:
79
README.md
79
README.md
@@ -37,6 +37,85 @@ tasks.withType<JavaExec>().configureEach {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## GraalVM Native Image Consumer
|
||||||
|
|
||||||
|
`jlibghostty` ships GraalVM reachability metadata at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
META-INF/native-image/dev.jlibghostty/jlibghostty/reachability-metadata.json
|
||||||
|
```
|
||||||
|
|
||||||
|
That metadata registers the FFM downcalls used by this library and includes the bundled native library resource. GraalVM 25 enables Native Image FFM support by default, but the build still needs native access:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
native-image --enable-native-access=ALL-UNNAMED ...
|
||||||
|
```
|
||||||
|
|
||||||
|
If your app uses the module path, prefer:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
native-image --enable-native-access=dev.jlibghostty ...
|
||||||
|
```
|
||||||
|
|
||||||
|
For a Nix-built downstream project, the usual shape is:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
jlibghostty.url = "path:/home/anon/Dev/jlibghostty";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { nixpkgs, jlibghostty, ... }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
jlib = jlibghostty.packages.${system}.jlibghostty;
|
||||||
|
in {
|
||||||
|
packages.${system}.default = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "my-native-app";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.graalvmPackages.graalvm-ce
|
||||||
|
pkgs.gradle
|
||||||
|
];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
export GRADLE_USER_HOME=$TMPDIR/gradle
|
||||||
|
gradle --offline -PjlibghosttyMavenRepo=${jlib}/maven installDist
|
||||||
|
native-image \
|
||||||
|
--enable-native-access=ALL-UNNAMED \
|
||||||
|
-cp build/install/my-app/lib/'*' \
|
||||||
|
-o my-app \
|
||||||
|
com.example.Main
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p "$out/bin"
|
||||||
|
cp my-app "$out/bin/"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In that downstream Gradle build:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url = uri(providers.gradleProperty("jlibghosttyMavenRepo").get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("dev.jlibghostty:jlibghostty:0.1.0-SNAPSHOT")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If the app runs on the module path, use:
|
If the app runs on the module path, use:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -64,6 +64,9 @@
|
|||||||
runHook preBuild
|
runHook preBuild
|
||||||
|
|
||||||
mkdir -p build/classes build/test-classes build/resources/${groupPath}/native/${platformName}
|
mkdir -p build/classes build/test-classes build/resources/${groupPath}/native/${platformName}
|
||||||
|
if [ -d src/main/resources ]; then
|
||||||
|
cp -R src/main/resources/. build/resources/
|
||||||
|
fi
|
||||||
|
|
||||||
ghostty_lib="$(find ${ghosttyVt} -type f -name '${sharedLibraryPattern}' -print -quit)"
|
ghostty_lib="$(find ${ghosttyVt} -type f -name '${sharedLibraryPattern}' -print -quit)"
|
||||||
if [ -z "$ghostty_lib" ]; then
|
if [ -z "$ghostty_lib" ]; then
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import dev.jlibghostty.GhosttyException;
|
|||||||
import dev.jlibghostty.TerminalOptions;
|
import dev.jlibghostty.TerminalOptions;
|
||||||
|
|
||||||
import java.lang.foreign.Arena;
|
import java.lang.foreign.Arena;
|
||||||
|
import java.lang.foreign.AddressLayout;
|
||||||
import java.lang.foreign.FunctionDescriptor;
|
import java.lang.foreign.FunctionDescriptor;
|
||||||
import java.lang.foreign.GroupLayout;
|
import java.lang.foreign.GroupLayout;
|
||||||
import java.lang.foreign.Linker;
|
import java.lang.foreign.Linker;
|
||||||
@@ -15,11 +16,7 @@ import java.lang.invoke.MethodHandle;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
|
||||||
import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
|
|
||||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
|
||||||
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
|
|
||||||
|
|
||||||
public final class GhosttyLibrary {
|
public final class GhosttyLibrary {
|
||||||
public static final int TERMINAL_DATA_COLS = 1;
|
public static final int TERMINAL_DATA_COLS = 1;
|
||||||
@@ -35,17 +32,21 @@ public final class GhosttyLibrary {
|
|||||||
private static final int GHOSTTY_NO_VALUE = -4;
|
private static final int GHOSTTY_NO_VALUE = -4;
|
||||||
|
|
||||||
private static final Linker LINKER = Linker.nativeLinker();
|
private static final Linker LINKER = Linker.nativeLinker();
|
||||||
|
private static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*");
|
||||||
|
private static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) LINKER.canonicalLayouts().get("bool");
|
||||||
|
private static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) LINKER.canonicalLayouts().get("short");
|
||||||
|
private static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) LINKER.canonicalLayouts().get("int");
|
||||||
private static final ValueLayout.OfLong C_SIZE_T = sizeTLayout();
|
private static final ValueLayout.OfLong C_SIZE_T = sizeTLayout();
|
||||||
|
|
||||||
private static final GroupLayout TERMINAL_OPTIONS = MemoryLayout.structLayout(
|
private static final GroupLayout TERMINAL_OPTIONS = MemoryLayout.structLayout(
|
||||||
JAVA_SHORT.withName("cols"),
|
C_SHORT.withName("cols"),
|
||||||
JAVA_SHORT.withName("rows"),
|
C_SHORT.withName("rows"),
|
||||||
MemoryLayout.paddingLayout(4),
|
MemoryLayout.paddingLayout(4),
|
||||||
C_SIZE_T.withName("max_scrollback")
|
C_SIZE_T.withName("max_scrollback")
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final GroupLayout GHOSTTY_STRING = MemoryLayout.structLayout(
|
private static final GroupLayout GHOSTTY_STRING = MemoryLayout.structLayout(
|
||||||
ADDRESS.withName("ptr"),
|
C_POINTER.withName("ptr"),
|
||||||
C_SIZE_T.withName("len")
|
C_SIZE_T.withName("len")
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,21 +64,21 @@ public final class GhosttyLibrary {
|
|||||||
SymbolLookup symbols = SymbolLookup.libraryLookup(libraryPath, Arena.global());
|
SymbolLookup symbols = SymbolLookup.libraryLookup(libraryPath, Arena.global());
|
||||||
|
|
||||||
terminalNew = downcall(symbols, "ghostty_terminal_new",
|
terminalNew = downcall(symbols, "ghostty_terminal_new",
|
||||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, TERMINAL_OPTIONS));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER, TERMINAL_OPTIONS));
|
||||||
terminalFree = downcall(symbols, "ghostty_terminal_free",
|
terminalFree = downcall(symbols, "ghostty_terminal_free",
|
||||||
FunctionDescriptor.ofVoid(ADDRESS));
|
FunctionDescriptor.ofVoid(C_POINTER));
|
||||||
terminalReset = downcall(symbols, "ghostty_terminal_reset",
|
terminalReset = downcall(symbols, "ghostty_terminal_reset",
|
||||||
FunctionDescriptor.ofVoid(ADDRESS));
|
FunctionDescriptor.ofVoid(C_POINTER));
|
||||||
terminalResize = downcall(symbols, "ghostty_terminal_resize",
|
terminalResize = downcall(symbols, "ghostty_terminal_resize",
|
||||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_SHORT, JAVA_SHORT, JAVA_INT, JAVA_INT));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_SHORT, C_SHORT, C_INT, C_INT));
|
||||||
terminalVtWrite = downcall(symbols, "ghostty_terminal_vt_write",
|
terminalVtWrite = downcall(symbols, "ghostty_terminal_vt_write",
|
||||||
FunctionDescriptor.ofVoid(ADDRESS, ADDRESS, C_SIZE_T));
|
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_SIZE_T));
|
||||||
terminalGet = downcall(symbols, "ghostty_terminal_get",
|
terminalGet = downcall(symbols, "ghostty_terminal_get",
|
||||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, ADDRESS));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
||||||
pasteIsSafe = downcall(symbols, "ghostty_paste_is_safe",
|
pasteIsSafe = downcall(symbols, "ghostty_paste_is_safe",
|
||||||
FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS, C_SIZE_T));
|
FunctionDescriptor.of(C_BOOL, C_POINTER, C_SIZE_T));
|
||||||
pasteEncode = downcall(symbols, "ghostty_paste_encode",
|
pasteEncode = downcall(symbols, "ghostty_paste_encode",
|
||||||
FunctionDescriptor.of(JAVA_INT, ADDRESS, C_SIZE_T, JAVA_BOOLEAN, ADDRESS, C_SIZE_T, ADDRESS));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_SIZE_T, C_BOOL, C_POINTER, C_SIZE_T, C_POINTER));
|
||||||
} catch (IllegalCallerException e) {
|
} catch (IllegalCallerException e) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"FFM native access is disabled. Run with --enable-native-access=dev.jlibghostty "
|
"FFM native access is disabled. Run with --enable-native-access=dev.jlibghostty "
|
||||||
@@ -93,17 +94,17 @@ public final class GhosttyLibrary {
|
|||||||
|
|
||||||
public MemorySegment terminalNew(TerminalOptions options) {
|
public MemorySegment terminalNew(TerminalOptions options) {
|
||||||
try (Arena arena = Arena.ofConfined()) {
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
MemorySegment out = arena.allocate(ADDRESS);
|
MemorySegment out = arena.allocate(C_POINTER);
|
||||||
MemorySegment nativeOptions = arena.allocate(TERMINAL_OPTIONS);
|
MemorySegment nativeOptions = arena.allocate(TERMINAL_OPTIONS);
|
||||||
|
|
||||||
nativeOptions.set(JAVA_SHORT, 0, (short) options.columns());
|
nativeOptions.set(C_SHORT, 0, (short) options.columns());
|
||||||
nativeOptions.set(JAVA_SHORT, 2, (short) options.rows());
|
nativeOptions.set(C_SHORT, 2, (short) options.rows());
|
||||||
nativeOptions.set(C_SIZE_T, 8, options.maxScrollback());
|
nativeOptions.set(C_SIZE_T, 8, options.maxScrollback());
|
||||||
|
|
||||||
int result = (int) terminalNew.invoke(MemorySegment.NULL, out, nativeOptions);
|
int result = (int) terminalNew.invoke(MemorySegment.NULL, out, nativeOptions);
|
||||||
checkResult("ghostty_terminal_new", result);
|
checkResult("ghostty_terminal_new", result);
|
||||||
|
|
||||||
MemorySegment terminal = out.get(ADDRESS, 0);
|
MemorySegment terminal = out.get(C_POINTER, 0);
|
||||||
if (terminal.address() == 0) {
|
if (terminal.address() == 0) {
|
||||||
throw new IllegalStateException("ghostty_terminal_new returned a null terminal handle");
|
throw new IllegalStateException("ghostty_terminal_new returned a null terminal handle");
|
||||||
}
|
}
|
||||||
@@ -160,10 +161,10 @@ public final class GhosttyLibrary {
|
|||||||
|
|
||||||
public int terminalGetU16(MemorySegment terminal, int key) {
|
public int terminalGetU16(MemorySegment terminal, int key) {
|
||||||
try (Arena arena = Arena.ofConfined()) {
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
MemorySegment out = arena.allocate(JAVA_SHORT);
|
MemorySegment out = arena.allocate(C_SHORT);
|
||||||
int result = (int) terminalGet.invoke(terminal, key, out);
|
int result = (int) terminalGet.invoke(terminal, key, out);
|
||||||
checkResult("ghostty_terminal_get", result);
|
checkResult("ghostty_terminal_get", result);
|
||||||
return Short.toUnsignedInt(out.get(JAVA_SHORT, 0));
|
return Short.toUnsignedInt(out.get(C_SHORT, 0));
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return rethrow(t);
|
return rethrow(t);
|
||||||
}
|
}
|
||||||
@@ -171,10 +172,10 @@ public final class GhosttyLibrary {
|
|||||||
|
|
||||||
public boolean terminalGetBoolean(MemorySegment terminal, int key) {
|
public boolean terminalGetBoolean(MemorySegment terminal, int key) {
|
||||||
try (Arena arena = Arena.ofConfined()) {
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
MemorySegment out = arena.allocate(JAVA_BOOLEAN);
|
MemorySegment out = arena.allocate(C_BOOL);
|
||||||
int result = (int) terminalGet.invoke(terminal, key, out);
|
int result = (int) terminalGet.invoke(terminal, key, out);
|
||||||
checkResult("ghostty_terminal_get", result);
|
checkResult("ghostty_terminal_get", result);
|
||||||
return out.get(JAVA_BOOLEAN, 0);
|
return out.get(C_BOOL, 0);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return rethrow(t);
|
return rethrow(t);
|
||||||
}
|
}
|
||||||
@@ -189,7 +190,7 @@ public final class GhosttyLibrary {
|
|||||||
}
|
}
|
||||||
checkResult("ghostty_terminal_get", result);
|
checkResult("ghostty_terminal_get", result);
|
||||||
|
|
||||||
MemorySegment ptr = out.get(ADDRESS, 0);
|
MemorySegment ptr = out.get(C_POINTER, 0);
|
||||||
long len = out.get(C_SIZE_T, 8);
|
long len = out.get(C_SIZE_T, 8);
|
||||||
if (ptr.address() == 0 || len == 0) {
|
if (ptr.address() == 0 || len == 0) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"glob": "dev/jlibghostty/native/**"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreign": {
|
||||||
|
"downcalls": [
|
||||||
|
{
|
||||||
|
"returnType": "int",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*",
|
||||||
|
"void*",
|
||||||
|
"struct(short, short, padding(4), size_t)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"returnType": "void",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"returnType": "int",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*",
|
||||||
|
"short",
|
||||||
|
"short",
|
||||||
|
"int",
|
||||||
|
"int"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"returnType": "void",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*",
|
||||||
|
"void*",
|
||||||
|
"size_t"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"returnType": "int",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*",
|
||||||
|
"int",
|
||||||
|
"void*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"returnType": "bool",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*",
|
||||||
|
"size_t"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"returnType": "int",
|
||||||
|
"parameterTypes": [
|
||||||
|
"void*",
|
||||||
|
"size_t",
|
||||||
|
"bool",
|
||||||
|
"void*",
|
||||||
|
"size_t",
|
||||||
|
"void*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user