I have actualised the sample given on IDEA website's guide on JNI in this repo (you can see fork there) and now it runs with modern JDK and JUnit Tests even on my aarch64 Mac (hope it runs anywhere else). But I do not really understand what is going on in build.gradle
in hello
Gradle subproject folder. I can recognise compiler arguments there, but include options are set in separate methods, which gets me confused.
My aim is to have just wrapper C-file that doing dlopen
(or its Windows' alternative) and runs some function from dynamic library.
Eg
<ProjectRoot>/hello/src/main/c/ wrapper-with-JNI-call-convensions.c
libsomelib.dylib
libsomelib.so
somelib.dll
Calls looks like this: Java -[JNI]-> wrapper.dll -> somelib.dll
^ ^ ^
| | |
building wrapper.c using Gradle to this
So I have successfully managed to get JNI working (while not changing build.gradle
from sample) if I just run build with two C source files (wrapper that uses JNI and C source code of my library). Then I built dylib
using macOS built-in clang
compiler and wrote proper (I guess) dlopen
and dlsym
code. But when I build project I get NULL returned from dlopen
call.
I have tried to add some arguments to the build.gradle
that used to compile binary that uses dynamic library, but it did not help.
My test running exits here:
// my wrapper
void* dlHandle = dlopen("libsomelib.dylib", RTLD_LAZY);
if (dlHandle == NULL) {
printf("DLL NOT OPENED !!!\n");
exit(0);
}
I know I have to write proper platform-dependent calls in my wrapper, it's not a big deal for me.
So can you tell me how I can properly get this working using Gradle? Remember that goal is to have just JNI-ready *.c
wrapper and just dynamic linked libraries *.so & *.dylib & *.dll
in my case.
Update: I've decided not to use Gradle, I use VSCode tasks.json
, it is like 20 times more intuitive. I will write my approach as self-answer.
To get it running you can try following:
> cc -g -shared -fpic wrapper.c -L. -lsome -o libwrapper.dylib
I assume that you have libsome.dylib
inside current directory. Then you can load libwrapper.dylib
inside JNI
and it refer to this another lib: libsome.dylib
Update
Let's say you have these two files:
#include <stdio.h>
void anotherFunction () {
// we are printing message from another C file
printf ("Hello from another function!\n");
}
and corresponding header file
#ifndef another_h__
#define another_h__
void anotherFunction (void)
#endif // another_h__
You can build a library out of it, by calling something like this:
cc -shared -fpic recipeNo023_AnotherFunction.c -o libanother.dylib
This will be equivalent of your: somelib
Then, we can have something like this:
#include "recipeNo023_redux_HelloWorld.h"
void anotherFunction ();
JNIEXPORT void JNICALL Java_recipeNo023_redux_HelloWorld_displayMessage
(JNIEnv * env, jclass obj) {
printf ("Hello world!\n");
/* We are calling function from another source */
anotherFunction ();
}
We declare function and use it. Name will be resolved later, during linking phase.
Now, you can compile your wrapper
lib. In this sample it's HelloWorld
.
cc -shared -fpic recipeNo023_redux_HelloWorld.c -L. -lanother
Now, inside JNI
, all you do is:
System.loadLibrary("HelloWorld");
Due to the fact HelloWorld
was linked with shared library another
it will have access to its symbols. So, you don't need to use dlopen
.
Try the sample (there is even Docker based file so you don't have to struggle with setting up environment).
As @Oo.oO mentioned this recipe is exactly what I need. It's dynamic load approach and it works on Mac and Linux. Also, as he mentioned there is static linking approach which is demonstrated here and in his answer.
I would also add some compiler options into this:
-O3 \ # for optimisation of speed
-x c \ # to explicitly set language to C, in my case
-fvisibility=default \ # to export only that I need (read below)
-arch x86_64 \ # option ONLY for macOS built-in clang in case you want build Intel binary on Silicon Mac
In lib's source code you should also define
#define EXPORT __attribute__((visibility("default")))
and then write it in header before each function declaration or in source file before each definition for functions that you want to be used from outside or lib.
eg
// somelib.h
EXPORT void somefunc(int a, int b);
// somelib.c
#include "somelib.h" // somelib.c and somelib.h in same directory
void somefunc(int a, int b) { ... }
OR
// in case you don't want to use header (I recommend to use)
// somelib.c
EXPORT void somefunc(int a, int b) { ... }
This answer will be advanced with Windows cases later.
clang
& Linux+ gcc
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.