【问题标题】:Bazel: Java app with JNI dependencyBazel:具有 JNI 依赖项的 Java 应用程序
【发布时间】:2017-09-16 16:37:15
【问题描述】:

我已经设法构建了我的 JNI 库(jar、jni 共享的 cc_library、包装的 cc_library),但我不知道如何构建使用它的 Java 应用程序。我的 BUILD 很简单:

java_binary(
    name = "OCFTestServer",
    srcs = glob(["src/main/java/**/*.java"]),
    deps = ["//:OpenOCF-JNI"])

这里OpenOCF-JNI 看起来像这样:

java_library(
    name = "OpenOCF-JNI",
    srcs = glob(["src/main/**/*.java"]),
    deps = ["libopenocf"],
    visibility = ["//visibility:public"])

libopenocf 是:

cc_library(
    name = "libopenocf",
    srcs = glob(["src/c/*.c"]) + glob(["src/c/*.h"])
    + ["@local_jdk//:jni_header",
       "@local_jdk//:jni_md_header-darwin"],
    ... etc ...

这些都成功构建。但是,构建不会导致构建依赖项,这是我所期望的(即构建 OCFTestServer 应该会导致构建 OpenOCF-JNI,这应该会导致构建 libopenocf-jni)。这不应该发生吗?

如果我使用单独的步骤构建它们,然后尝试运行应用程序(使用 bazel-bin 中的 OCFTestServer 包装器),我会得到 UnsatisfiedLinkError: no libopenocf-jni in java.library.path。但是通过阅读文档,我得到的印象是这一切都应该自动设置(即所需的 jni 库应该放在 java.library.path 中)。

我做错了什么?有人有构建和使用 JNI 库的示例吗?

【问题讨论】:

  • 我认为您的 cc_library 应该将其 src 与 hdrs 分开。不确定这是否相关。
  • @kd8azz: afaik hdrs 用于公共(即 api)标头。否则所有头文件都被视为源文件。
  • 我认为这里有答案:stackoverflow.com/questions/46160790/…
  • @mhlopko:该项目涉及构建库,而不是如何在应用程序中使用它们。
  • 啊抱歉,我没注意到是你:)

标签: java java-native-interface bazel


【解决方案1】:

我创建了一个简单的 repo:https://github.com/mhlopko/bazel-jni-example 来帮助您入门。

BUILD:

cc_library(
    name = "main-jni-lib",
    srcs = [
        "@local_jdk//:jni_header",
        "@local_jdk//:jni_md_header-linux",
        "Main.cc"
        ],
    hdrs = [ "Main.h" ],
    includes = [ "external/local_jdk/include", "external/local_jdk/include/linux" ],
)

cc_binary(
    name = "libmain-jni.so",
    deps = [ ":main-jni-lib" ],
    linkshared = 1,
)

java_binary(
    name = "Main",
    srcs = [ "Main.java" ],
    main_class = "Main",
    data = [ ":libmain-jni.so" ],
    jvm_flags = [ "-Djava.library.path=." ],
)

Main.java:

public class Main {
  static {
    System.loadLibrary("main-jni");
  }

  private native int foo();

  public static void main(String[] args) {
    System.out.println(new Main().foo());
  }
}

Main.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */

#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Main
 * Method:    foo
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_Main_foo(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Main.cc:

#include <jni.h>
#include <stdio.h>
#include "Main.h"

JNIEXPORT jint JNICALL Java_Main_foo(JNIEnv *, jobject) {
   return 42;
}

现在通过运行bazel run :Main,您应该会看到打印出的42,它来自Main.cc。该示例显然需要更多润色,因此它可以在非 linux 平台上运行,因此它可以与启动器脚本一起使用。您最终可能需要多次调用System.loadLibrary,就像bazel 在its windows loader 中所做的那样。

【讨论】:

  • 谢谢。问:为什么:libmain-jni.sodata 而不是deps
  • 或“runtime_deps”。
  • 使用数据或 deps 只是风格问题。效果是一样的,因为数据实际上是一个 cc 二进制文件,它被允许作为对 java_library 的依赖。
【解决方案2】:

我在试图弄清楚如何链接到 malmo 时偶然发现了你的问题,我刚刚开始工作。它链接到一个静态库,所以它可能对你有帮助,也可能没有。

第三方/构建:

java_import(
    name = "malmo",
    jars = ["MalmoJavaJar.jar"],
    deps = [":libMalmo"],
)

cc_library(
    name = "libMalmo",
    srcs = ["libMalmoJava.so"],
)

然后在我的实际目标中:

    "//third_party:malmo",

【讨论】:

    【解决方案3】:

    实际上,您不需要将 bazel local_jdk jni 和 jni_md 标头添加到您的包 cc_library 目标中。但是,您可以将它们复制到您的项目包中。

    https://github.com/bazelbuild/bazel/blob/c97c77333cab2b78dc8ac76834fb873389f520d7/src/main/native/BUILD#L4

    我已经提交PR到https://github.com/mhlopko/bazel-jni-example项目来修复jni相对路径问题。也许这可以帮助你。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-12
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      • 2014-06-18
      相关资源
      最近更新 更多