简体   繁体   English

如何构建从另一个 DLL 调用函数的 JNI DLL? - JNI,摇篮

[英]How to build JNI DLL that calls function from another DLL? - JNI, Gradle

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).我已经在这个repo 中实现了 IDEA 网站关于 JNI 的指南中给出的示例(你可以在那里看到 fork),现在它甚至可以在我的 aarch64 Mac 上运行现代 JDK 和 JUnit 测试(希望它可以在其他任何地方运行)。 But I do not really understand what is going on in build.gradle in hello Gradle subproject folder.但我真的不明白在hello Gradle 子项目文件夹中的build.gradle中发生了什么。 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.我的目标是只包装 C 文件来执行dlopen (或其 Windows 的替代方案)并运行动态库中的一些函数。

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).因此,如果我只使用两个 C 源文件(使用 JNI 和我的库的 C 源代码的包装器)运行构建,我已经成功地使 JNI 工作(同时不更改示例中的build.gradle )。 Then I built dylib using macOS built-in clang compiler and wrote proper (I guess) dlopen and dlsym code.然后我使用 macOS 内置的clang编译器构建了dylib并编写了正确的(我猜) dlopendlsym代码。 But when I build project I get NULL returned from dlopen call.但是当我构建项目时,我从dlopen调用返回 NULL。

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.我试图向用于编译使用动态库的二进制文件的build.gradle添加一些参数,但它没有帮助。

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?那么你能告诉我如何使用 Gradle 正确地让它工作吗? Remember that goal is to have just JNI-ready *.c wrapper and just dynamic linked libraries *.so & *.dylib & *.dll in my case.请记住,在我的例子中,目标是只有 JNI-ready *.c包装器和动态链接库*.so & *.dylib & *.dll

Update: I've decided not to use Gradle, I use VSCode tasks.json , it is like 20 times more intuitive.更新:我决定不使用 Gradle,我使用 VSCode tasks.json ,它的直观性提高了 20 倍。 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.我假设您在当前目录中有libsome.dylib Then you can load libwrapper.dylib inside JNI and it refer to this another lib: libsome.dylib然后你可以在JNI加载libwrapper.dylib并引用另一个库: 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这将相当于你的: 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.现在,您可以编译wrapper库。 In this sample it's HelloWorld .在这个示例中,它是HelloWorld

cc -shared -fpic recipeNo023_redux_HelloWorld.c -L. -lanother

Now, inside JNI , all you do is:现在,在JNI ,您要做的就是:

System.loadLibrary("HelloWorld");

Due to the fact HelloWorld was linked with shared library another it will have access to its symbols.由于HelloWorld与共享库链接, another它可以访问其符号。 So, you don't need to use dlopen .所以,你不需要使用dlopen

Try the sample (there is even Docker based file so you don't have to struggle with setting up environment).尝试示例(甚至还有基于 Docker 的文件,因此您不必为设置环境而烦恼)。

As @Oo.oO mentioned this recipe is exactly what I need.正如@Oo.oO 提到的, 这个食谱正是我所需要的。 It's dynamic load approach and it works on Mac and Linux.它是动态加载方法,适用于 Mac 和 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在 lib 的源代码中,您还应该定义

#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.稍后将在 Windows 案例中推进此答案。

Summary概括

macOS+ clang & Linux+ gcc macOS+ clang & Linux+ gcc

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM