gradle-native
gradle-native copied to clipboard
Provide sample of building a JNI library with Gradle native plugins
We should provide a sample (in the Gradle distribution) that builds a simple JNI library for the big three platforms (Linux, macOS, Windows) and packages it into the re-distributable jar.
Example of such request: https://discuss.gradle.org/t/subproject-with-both-java-and-c/30509
I think such a sample would be valuable. Java developers have to deal with some native libraries from time to time, including on Android. We do, in native-platform
. We currently offer no guidance on how to do that and it's quite a pain when you have to deal with JNI.
SO has more than 700 questions about Gradle and JNI https://stackoverflow.com/search?q=gradle+jni
Plugin portal shows ~10 plugins related to JNI where ~5 are about the general use case, after a quick look most of them are stale, work with the deprecated software model, I didn't find a compelling solution https://plugins.gradle.org/search?term=jni
Having such a sample could also be a way to promote our native support to Java devs.
Here is something Paul and I prototype together: https://github.com/eskatos/jni-library-sample
Thanks for the prototype! I must confess that I'm still having some difficulty understanding how everything works together. As I understand it, you're creating a custom Gradle plugin to handle building the native code. Is that the idiomatic way of doing this?
We are trying to follow a Sample Driven Development practice for newer features. Gradle is really mature and most feature should be implementable with what Gradle already offers. Ultimately, JNI library capability should be offered as part of plugin. The sample simply split how everything should be divided in the future. We aren't expecting each users to create a custom plugin to solve the use case. Instead, we hope to evolve the plugin into something users can use as-is in their project to benefit from the abstraction and simplicity of Gradle. In this specific example, we have the app
(consumer), jni-greeter
(producer and wrapper of a native library, kind of like the binding) and native-greeter
(a native dependency). The JNI library is abstracted by jni-library-plugin
. The important parts are app
(consumer) and jni-greeter
(producer). You could in theory ignore or merge native-greeter
inside jni-greeter
. We simply wanted to show the jni-binding
use case.
Some other use cases that we should also show is: custom JNI library loader, additional Java API inside the JNI library, packaging all possible native shared library within the JNI Jar vs relying on dependency resolution using variant aware.
With the old native plugin I can quite easily have C++ app that embeds JVM and there is a two way communication between C++ and Java code:
- C++ code starts JVM
- C++ code serves requests received by 3rd party native libraries by forwarding them to Java
- Java code invokes C++ code that delegates to 3rd party native libraries It's a single application and so is built as single project from src/main/java and src/main/cpp. Works like a charm.
Will the same be possible with new plugin? Do you plan to enhance the example in this direction?
In your case, it sounds like a JNI and NJI (native to Java calls). I can see that mixing the code within a single project is beneficial given it's released together. It's being discussed quite a bit internally and it's mostly a framing/mindset issue at this point. In the end there is nothing really preventing you from doing that. Should it work like that? Most likely not with the current plugin as we don't have tests that showcase this exact scenario. Should you be able to do it? Yes, but to simplify Gradle we usually frame it as one component type per project. That being said, Gradle should provide a lower level API to project composition for what you are trying to do.
One thing to keep in mind, when creating 2 projects, it doesn't necessarily means two project directories and splitting src/main/java
and src/main/cpp
in each project. The project are simply a virtual model that doesn't need to match 1-to-1 to the on disk layout. You can have 2 projects pointing at sources within a single project directory.
Doing java/jni/native in a single project layout would be great for one of our migration scenarios. I've never seen this in the documentation before. Is there a sample for it?
BTW, I found a way to reference native (cpp) artifacts from non-cpp projects (base and probably java, java-library)
val someCfg by configurations.creating {
attributes {
attribute(CppBinary.OPTIMIZED_ATTRIBUTE, false)
attribute(Usage.USAGE_ATTRIBUTE, namedAttribute(Usage.NATIVE_RUNTIME))
}
}
dependencies {
attributesSchema {
attribute(CppBinary.OPTIMIZED_ATTRIBUTE)
attribute(Usage.USAGE_ATTRIBUTE)
}
someCfg(project(":some_native_subproject"))
}
tasks {
register<Copy>("someNativeCp") {
from(someCfg) {
include("*.so")
}
into(...)
}
}
If you are interested in this use case, the Nokee plugins can help solve the JNI library scenario. Have a look at the samples here: https://nokee.dev/docs/current/samples/#sec:samples-jni
There some new API and convenience coming in the next version. Feel free to give it a try and open any issue you may have over at https://github.com/nokeedev/gradle-native