gradle-native icon indicating copy to clipboard operation
gradle-native copied to clipboard

Provide sample of building a JNI library with Gradle native plugins

Open big-guy opened this issue 7 years ago • 10 comments

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.

big-guy avatar Oct 09 '17 03:10 big-guy

Example of such request: https://discuss.gradle.org/t/subproject-with-both-java-and-c/30509

lacasseio avatar Sep 12 '19 13:09 lacasseio

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.

eskatos avatar Nov 19 '19 13:11 eskatos

Here is something Paul and I prototype together: https://github.com/eskatos/jni-library-sample

lacasseio avatar Nov 27 '19 14:11 lacasseio

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?

DanielSWolf avatar Jan 04 '20 16:01 DanielSWolf

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.

lacasseio avatar Jan 07 '20 10:01 lacasseio

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:

  1. C++ code starts JVM
  2. C++ code serves requests received by 3rd party native libraries by forwarding them to Java
  3. 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?

nikolay-martynov avatar Jan 07 '20 14:01 nikolay-martynov

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.

lacasseio avatar Jan 07 '20 14:01 lacasseio

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?

pedrolamarao avatar Jan 08 '20 15:01 pedrolamarao

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(...)
            }
       }

iharh avatar May 29 '20 13:05 iharh

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

lacasseio avatar May 29 '20 18:05 lacasseio