This plugin enables developing Java code that makes use of native libraries using the new Foreign Function & Memory API. FFM API can be considered as a more modern and secure alternative to JNI for native access in Java. The plugin generates Java bindings for native libraries using the FFM-related Jextract tool and makes them accessible in java compilation.
- Downloads Jextract for the current build platform and architecture, no additional installation steps needed.
- Preset conventions for Jextract versions 19 up to 22.
- Download locations can be customized for more restrictive environments.
- Alternately, use a local installation of Jextract.
- Generated source code is available in a user-selected Java source set (
main
by default). - Multiple libs can be configured in one project and are built concurrently.
- Whitelisting of included symbols via DSL extension
- Can dump includes and use arg files
- Compatible with Configuration Cache.
- Tasks are cacheable.
You might want to apply this plugin after one of the Java plugins.
It tries to get the correct Java version from the toolchain extension or falls back to the JDK Gradle is running on.
On JDk 23, Jextract 22 is used, because FFM has been finalized, so there are effectively no differences between version 22 and 23.
After running ./gradlew build
, the generated code will be available in the main source set.
In build.gradle.kts:
plugins {
id("application")
id("de.infolektuell.jextract") version "0.4.0"
}
jextract.libraries {
create("bass") {
header = layout.projectDirectory.file("bass.h")
targetPackage = "com.un4seen.bass"
headerClassName = "Bass"
includes.add(layout.projectDirectory.dir("src/main/public"))
libraries = listOf("bass")
useSystemLoadLibrary = true
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(22)
}
}
The plugin can dump includes encountered in the headers and use arg files.
Running gradlew dumpIncludes
produces a -includes.txt file for each configured lib under build/reports/jextract/.
To use this as an arg file for storing your whitelisted includes, you should copy, modify, and put it under source control.
Add this copy to the plugin's extension.
Instead of using an arg file, included symbols can also be configured directly in the whitelist extension via respective properties.
jextract.libraries {
create("bass") {
header = layout.projectDirectory.file("bass.h")
targetPackage = "com.un4seen.bass"
headerClassName = "Bass"
whitelist {
argFile = layout.projectDirectory.file("src/main/includes/bass-includes.txt")
// Include more symbols via DSL extension
functions.addAll("bass_get_version")
}
libraries = listOf("bass")
}
}
See Filtering section in the Jextract guide for more information.
The extension offers a property to select a version without relying on a Java plugin.
jextract {
generator {
javaLanguageVersion = JavaLanguageVersion.of(21)
}
}
Instead of downloading Jextract, a local installation directory can be configured.
jextract {
generator {
local = layout.projectDirectory.dir("/usr/local/opt/jextract-22/")
}
}
Jextract is downloaded from the official page by default. If custom locations are needed, the download task must be configured (url, checksum, and verification algorithm). The plugin implements its own decision logic to select appropriate conventions depending on the current build platform and JDK version.
import de.infolektuell.gradle.jextract.tasks.DownloadTask
tasks.withType(DownloadTask::class).configureEach {
resource.url = uri("https://my-company.com/jextract/file.tgz")
resource.checksum = "xyz"
resource.algorithm = "SHA-512" // SHA-256 by default
}
In some situations, the main
source set is not available or the sources should be added to another one, e.g., test
.
This must be configured in the extension:
jextract {
sourceSet = sourceSets.named("test")
}