如何构建从另一个 DLL 调用函数的 JNI DLL? - JNI,Gradle
How to build JNI DLL that calls function from another DLL? - JNI, Gradle
我已经在这个 repo 中实现了 IDEA 网站关于 JNI 的指南中给出的示例(你可以在那里看到分支)现在它 运行s 与现代 JDK 和 JUnit 测试即使在我的 aarch64 Mac 上(希望它在其他任何地方 运行)。但是我真的不明白 build.gradle
在 hello
Gradle 子项目文件夹中发生了什么。我可以在那里识别编译器参数,但是包含选项是在单独的方法中设置的,这让我很困惑。
我的目标是让包装器 C 文件执行 dlopen
(或其 Windows 的替代方案)和 运行 动态库中的某些功能。
例如
<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
因此,如果我只是 运行 使用两个 C 源文件(使用 JNI 的包装器和我的图书馆)。然后我使用 macOS 内置的 clang
编译器构建了 dylib
,并编写了正确的(我猜)dlopen
和 dlsym
代码。但是当我构建项目时,我从 dlopen
调用中返回 NULL。
我曾尝试向用于编译使用动态库的二进制文件的 build.gradle
添加一些参数,但没有帮助。
我的测试运行ning 在这里退出:
// my wrapper
void* dlHandle = dlopen("libsomelib.dylib", RTLD_LAZY);
if (dlHandle == NULL) {
printf("DLL NOT OPENED !!!\n");
exit(0);
}
我知道我必须在我的包装器中编写适当的平台相关调用,这对我来说不是什么大问题。
那么你能告诉我如何使用 Gradle 正确地让它工作吗?请记住,在我的例子中,目标是只有 JNI-ready *.c
包装器和动态链接库 *.so & *.dylib & *.dll
。
更新:我决定不用Gradle,我用VSCodetasks.json
,好像直观了20倍.我会把我的方法写成自我回答。
要获取它 运行 您可以尝试以下操作:
> cc -g -shared -fpic wrapper.c -L. -lsome -o libwrapper.dylib
我假设您在当前目录中有 libsome.dylib
。然后你可以在 JNI
中加载 libwrapper.dylib
并且它引用另一个库:libsome.dylib
更新
假设您有这两个文件:
#include <stdio.h>
void anotherFunction () {
// we are printing message from another C file
printf ("Hello from another function!\n");
}
和相应的头文件
#ifndef another_h__
#define another_h__
void anotherFunction (void)
#endif // another_h__
你可以用它构建一个库,通过调用这样的东西:
cc -shared -fpic recipeNo023_AnotherFunction.c -o libanother.dylib
这相当于您的:somelib
然后,我们可以有这样的东西:
#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 ();
}
我们声明函数并使用它。稍后将在链接阶段解析名称。
现在,您可以编译 wrapper
库。在此示例中,它是 HelloWorld
.
cc -shared -fpic recipeNo023_redux_HelloWorld.c -L. -lanother
现在,在 JNI
里面,您要做的就是:
System.loadLibrary("HelloWorld");
由于 HelloWorld
与共享库 another
链接,它将可以访问其符号。所以,你不需要使用 dlopen
.
尝试示例(甚至还有基于 Docker 的文件,因此您不必为设置环境而苦恼)。
正如@Oo.oO 提到的 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 和他的回答。
我还会在其中添加一些编译器选项:
-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
在lib的源代码中你也应该定义
#define EXPORT __attribute__((visibility("default")))
然后将它写在每个函数声明之前的头文件中,或者写在源文件中每个要从外部或库中使用的函数定义之前。
例如
// 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) { ... }
或
// in case you don't want to use header (I recommend to use)
// somelib.c
EXPORT void somefunc(int a, int b) { ... }
此答案将在以后 Windows 个案例中提出。
总结
macOS+clang
& Linux+gcc
我已经在这个 repo 中实现了 IDEA 网站关于 JNI 的指南中给出的示例(你可以在那里看到分支)现在它 运行s 与现代 JDK 和 JUnit 测试即使在我的 aarch64 Mac 上(希望它在其他任何地方 运行)。但是我真的不明白 build.gradle
在 hello
Gradle 子项目文件夹中发生了什么。我可以在那里识别编译器参数,但是包含选项是在单独的方法中设置的,这让我很困惑。
我的目标是让包装器 C 文件执行 dlopen
(或其 Windows 的替代方案)和 运行 动态库中的某些功能。
例如
<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
因此,如果我只是 运行 使用两个 C 源文件(使用 JNI 的包装器和我的图书馆)。然后我使用 macOS 内置的 clang
编译器构建了 dylib
,并编写了正确的(我猜)dlopen
和 dlsym
代码。但是当我构建项目时,我从 dlopen
调用中返回 NULL。
我曾尝试向用于编译使用动态库的二进制文件的 build.gradle
添加一些参数,但没有帮助。
我的测试运行ning 在这里退出:
// my wrapper
void* dlHandle = dlopen("libsomelib.dylib", RTLD_LAZY);
if (dlHandle == NULL) {
printf("DLL NOT OPENED !!!\n");
exit(0);
}
我知道我必须在我的包装器中编写适当的平台相关调用,这对我来说不是什么大问题。
那么你能告诉我如何使用 Gradle 正确地让它工作吗?请记住,在我的例子中,目标是只有 JNI-ready *.c
包装器和动态链接库 *.so & *.dylib & *.dll
。
更新:我决定不用Gradle,我用VSCodetasks.json
,好像直观了20倍.我会把我的方法写成自我回答。
要获取它 运行 您可以尝试以下操作:
> cc -g -shared -fpic wrapper.c -L. -lsome -o libwrapper.dylib
我假设您在当前目录中有 libsome.dylib
。然后你可以在 JNI
中加载 libwrapper.dylib
并且它引用另一个库:libsome.dylib
更新
假设您有这两个文件:
#include <stdio.h>
void anotherFunction () {
// we are printing message from another C file
printf ("Hello from another function!\n");
}
和相应的头文件
#ifndef another_h__
#define another_h__
void anotherFunction (void)
#endif // another_h__
你可以用它构建一个库,通过调用这样的东西:
cc -shared -fpic recipeNo023_AnotherFunction.c -o libanother.dylib
这相当于您的:somelib
然后,我们可以有这样的东西:
#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 ();
}
我们声明函数并使用它。稍后将在链接阶段解析名称。
现在,您可以编译 wrapper
库。在此示例中,它是 HelloWorld
.
cc -shared -fpic recipeNo023_redux_HelloWorld.c -L. -lanother
现在,在 JNI
里面,您要做的就是:
System.loadLibrary("HelloWorld");
由于 HelloWorld
与共享库 another
链接,它将可以访问其符号。所以,你不需要使用 dlopen
.
尝试示例(甚至还有基于 Docker 的文件,因此您不必为设置环境而苦恼)。
正如@Oo.oO 提到的 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 和他的回答。
我还会在其中添加一些编译器选项:
-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
在lib的源代码中你也应该定义
#define EXPORT __attribute__((visibility("default")))
然后将它写在每个函数声明之前的头文件中,或者写在源文件中每个要从外部或库中使用的函数定义之前。
例如
// 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) { ... }
或
// in case you don't want to use header (I recommend to use)
// somelib.c
EXPORT void somefunc(int a, int b) { ... }
此答案将在以后 Windows 个案例中提出。