Gradle java plugin - Add .so file to the classpath, how?


(Nunes) #1

Hello to all,

How to I add a native lib .so to the classpath in a java gradle project?

For the gradle ‘android’ plugin, we it’s just need to add

sourceSets{
    main.java {
        srcDir "src/main/java"
    }
    jniLibs.srcDir 'libs'
}

But, for simple java projects, this jniLibs doesn’t exist for the gradle ‘java’ plugin.
I’m always getting the following error:

   mainException in thread "main" java.lang.UnsatisfiedLinkError: no mylib in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886)
        at java.lang.Runtime.loadLibrary0(Runtime.java:849)
        at java.lang.System.loadLibrary(System.java:1088)

I understand that it’s a problem loading the .so file to the classpath.

Info:
Gradle 2.2.1


(François Guillot) #2

You probably want to set your native library for your unit test execution (correct me if I’m wrong)
In that case, referring tohttps://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:systemProperties
You can use the systemProperty key, value method of the test task, and set java.library.path
As a matter of fact you can set this system property in the command line (-Djava.library.path) or anywhere in your build, if needed outside of your Unit Tests execution


(Nunes) #3

Hi François, thanks for your response.

It’s not a Test execution. I want to use a task with type JavaExec.

Already tried with systemProperty, but it looks like it only works for unit test. How to do it for a JavaExec task?

task runMyTask(dependsOn: ['classes'], type: JavaExec) {
    main = 'com.product.MyDemo'
    classpath = sourceSets.main.runtimeClasspath
}

(François Guillot) #4

And if you use the Eclipse plugin to generate your .classpath file, it can be tweaked with the ‘withXml’ method to include manually the native library needed by your jars dependency.
e.g.

                    eclipse.classpath.file.withXml {XmlProvider provider ->
                        provider.asNode().classpathentry.findAll{it.'@path'.contains('/a/dependency/that/needs/native/library/')}
                        .each {
                            it.appendNode('attributes')
                                    .appendNode('attribute', [name:'org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY', value:'/path/to/native/lib'])
                        }
                    }

(Nunes) #5

Hello,

I’m not using Eclipse. I’m using IDEA. Also I want to run my code anywhere. I want my task to run by command line.

Does gradle java plugin supports native libs at all?


(François Guillot) #6

First things first.
What do you need your native library for ? Probably execute your unit tests during compilation + for the runtime execution of your java project, right ?


(Nunes) #7

I have a java project that has as dependency a c++ lib. I’m using a java wrapper to access my c++ lib.

This code works perfect with another android project I have, because I’m using the jniLibs.srcDir property of the ‘gradle android plugin’.

Now, Basically I created a java project, and I want to use the same lib to acesss the c++ lib. First, I noticed that there is no option jniLibs for the java plugin. The code is breaking when I do a System.loadLibrary(“myLib”); I understand why, because I need to add the native-lib .so file do the java path, right?


(François Guillot) #8

You need to tell the JVM created by Gradle where to find the native lib.
This can be done by setting the System property ‘java.library.path’ to the directory containing your lib.
Now you can do this in many ways:
the -D command line option (-Djava.library.path=/path/to/lib/directory)
Directly in the test { } extension, you can set specific system properties to be used by the JVM created for the test execution.

If you need something more specific, you need to explain when

is called


(Nunes) #9

Ok,
I got it. So it works if I run it as a Test.

How does this deals with different platforms architectures? x86, 64 (linux and windows).

For the android plugin we just need to add the correct folder names under the libs folder (like libs/armeabi), and the system will load the proper lib based on the current system.

For the java plugin, there is any defined folder structure? Or I need to build an architecture detection in my build.gradle?


(François Guillot) #10

Unfortunately there is nothing like that at the moment for the Java plug-in (that I am aware of). You need to add the logic yourself.

test {
 if( your test ){
  systemProperty 'java.library.path','path/to/64/directory'
}else{
  systemProperty 'java.library.path','path/to/32/directory'
}

Note that I don’t know if Gradle provide a way to check the 32/64 bits version of the JVM your running, so you will have to rely on the System properties (such as ‘os.arch’)
For the .so/.dll, you can just put them in the same directory (the 32 bits versions of the dll/.so in the same directory, and the 64 bits versions in another one)


(Nunes) #11

Thanks for the help.

Where can I see the latest java plugin developments? And maybe request some features?


(François Guillot) #12

https://docs.gradle.org/current/userguide/java_plugin.html summarizes the java plugin features, but I guess you already read it.

Here is a good place to discuss features in their early stages. The core Gradle Developers are always watching :smile:


(Nunes) #13

So, what gradle java plugin should have a pre-defined folder structure (like Android plugin). it’s something like this:

src/lib/native-libs/i386 or src/lib/native-libs/x86
-myLib32.so
-myLib32.dll
src/lib/native-libs/amd64 or src/lib/native-libs/x86_64
-myLib64.so
-myLib64.dll


(Nunes) #14

Any news regarding this topic? Is it possible to have a structured folder in the libs folder and have all architectures inside only on gradle java projects?

Something like:
src/libs/armeabi/MyLib.so
src/libs/arm64/MyLib.so
src/libs/x86/MyLib.so
src/libs/x86_64/MyLib.so
src/libs/mips/MyLib.so


(Marian Stránecký) #15

In case you want to distribute all native variants (e.g. linux, windows, 32/64bit etc.), then the best you can come with is to pack it within your jar (lets say in META-INF/natives/{platform}/) and then to use a custom ‘loader’ which will be responsible for extracting your native library (in detected platform variant) to some temporary dir and calling System.load(’/path/to/such/extracted/library/file’) instead of usual System.loadLibrary(‘libname’).

As a hint have a look to e.g. netty’s transport-native-epoll library sources.

Pros:

  • you (or better a developer/client using your library) don’t have to set ‘java.library.path’
  • all in one package

Cons:

  • you have to create such a loader on your own
  • you have to define a set of supported platform “keys” (and keep in mind similarities - e.g. amd64 = x86_64 etc.) - it definitely depends on the way you detect the platform in runtime
  • you have to take care of created temporary files (or at least to be aware of and let the OS to clean it up)

HTH,