What?
Generating FFM API-compatible bindings for Java using jextract
Why?
The Java FFM API replaces JNI and other native binding approaches for native code/memory access.
Creating bindings for those native libraries can be highly-manual or inconsistent when performed manually. jextract
been provided that simplifies and harmonizes the process and provides consistent outputs.
How?
Get jextract
using SDK Manager:
sdk install jextract
Use the jextract
tool to generate bindings using c
headers and libraries.
$ jextract --help
Usage: jextract < option s > < header fil e > [<header fil e > ] [...]
Option Description
------ -----------
-?, -h, --help print help
-D --define-macro < macr o > = < valu e > define < macr o > to < valu e > (or 1 if < valu e > omitted )
-I, --include-dir < di r > add directory to the end of the list of include search paths
--dump-includes < fil e > dump included symbols into specified file
--header-class-name < nam e > name of the generated header class. If this option is not
specified, then header class name is derived from the header
file name. For example, class " foo_h " for header " foo.h " .
--include-function < nam e > name of function to include
--include-constant < nam e > name of macro or enum constant to include
--include-struct < nam e > name of struct definition to include
--include-typedef < nam e > name of type definition to include
--include-union < nam e > name of union definition to include
--include-var < nam e > name of global variable to include
-l, --library < libspe c > specify a shared library that should be loaded by the
generated header class. If < libspe c > starts with :, then
what follows is interpreted as a library path. Otherwise,
< libspec > denotes a library name. Examples:
-l GL
-l :libGL.so.1
-l :/usr/lib/libGL.so.1
--use-system-load-library libraries specified using -l are loaded in the loader symbol
lookup (using either System::loadLibrary, or System::load ).
Useful if the libraries must be loaded from one of the paths
in java.library.path.
--output < pat h > specify the directory to place generated files. If this
option is not specified, then current directory is used.
-t, --target-package < packag e > target package name for the generated classes. If this option
is not specified, then unnamed package is used.
--version print version information and exit
Example: Generating Bindings for an External Library (liburing)
$ jextract --output ./app/src/generated/java \
--target-package org.example.bindings.linux.liburing \
--library liburing.so /usr/include/liburing.h
$ tree ./app/src/generated/java
./app/src/generated/java
└── org
└── example
└── bindings
└── linux
└── liburing
...
├── liburing_h_1.java
├── liburing_h.java
...
5 directories, 108 files
liburing_h.java
// Generated by jextract
package org . example . bindings . linux . liburing ;
import java . lang . invoke . * ;
import java . lang . foreign . * ;
import java . nio . ByteOrder ;
import java . util . * ;
import java . util . function . * ;
import java . util . stream . * ;
import static java . lang . foreign . ValueLayout . * ;
import static java . lang . foreign . MemoryLayout . PathElement . * ;
// ...<snip>
/**
* {@snippet lang=c :
* extern int io_uring_queue_init(unsigned int entries, struct io_uring *ring, unsigned int flags)
* }
*/
public static int io_uring_queue_init ( int entries , MemorySegment ring , int flags ) {
var mh$ = io_uring_queue_init . HANDLE ;
try {
if ( TRACE_DOWNCALLS ) {
traceDowncall ( " io_uring_queue_init " , entries , ring , flags ) ;
}
return ( int ) mh$ . invokeExact ( entries , ring , flags ) ;
} catch ( Throwable ex $ ) {
throw new AssertionError ( " should not reach here " , ex$ ) ;
}
}
// ...<snip>
Partial file generated from headers with jextract
References/Other