【问题标题】:Portable Makefile for JNI applicationsJNI 应用程序的可移植 Makefile
【发布时间】:2017-10-29 13:39:44
【问题描述】:

我有一个想要分发的带有 C JNI 后端的 Java 应用程序。分发 Java 部分很容易——这正是 jar 和 JVM 的用途。分发预编译的 C 代码并不容易,我想我必须要求用户自己构建本机库。

目前我使用ant 构建Java 代码和make 构建C 代码。问题是我在我的 Makefile 中硬编码了jni.h 和jni_md.h 的位置,这远非便携。我已经在万维网中搜索了尽可能多的 JNI 示例,并且所有这些示例都在某处具有硬编码路径。如果它是 JNI 以外的任何包,我可以很高兴地使用 pkg-config 之类的东西来获得正确的包含标志,但据我所知,JNI 不提供任何此类功能。

问题:我如何编写一个可移植 makefile 来查找 JNI 头文件并在任何*系统中编译我的 C 本机代码?

作为参考,以下是我考虑过的几个选项:

  • 我可以使用 CMake 的 FindJNI 函数,但我宁愿不使用,因为 C 代码不是那么复杂,而且 CMake 会有点矫枉过正(加上对用户的额外依赖)。

  • 我希望设置 JAVA_HOME 环境变量,但我不希望总是这样,因为(至少在 Ubuntu 中)安装 Java 时默认情况下没有设置它。

  • 我可以使用locate 或find 来手动查找文件并将包含标志拼接在一起,但这感觉相当hacky。

*我最感兴趣的是 Windows、OSX 和主流 Ubuntu 版本以及任何合理的现代 Java 版本。

【问题讨论】:

  • 如何在你的包中分发正确版本的jni.h?
  • @KinCheung 感谢您的评论。 jni.h 不是依赖于安装的具体Java版本吗?在我的系统中jni.h 在/usr/lib/jvm/java-8-openjdk-amd64 中,所以AFAIK 只是抓住jni.h 不一定适用于除OpenJDK 8 之外的任何Java 系统。不是这样吗?

标签: java c makefile java-native-interface


【解决方案1】:

如果它的可移植性足以让您依赖 GNU make(至少,这在大多数平台上都可用),您可以使用 JAVA_HOME 并尝试自己确定以防万一设置如下:

ifeq ($(JAVA_HOME),)
ifeq ($(OS),Windows_NT)
which = $(shell where $1)
else
which = $(shell which $1)
endif
JAVAC ?= $(realpath $(call which,javac))
JAVA_HOME = $(abspath $(dir $(JAVAC))..)
endif

ifneq ($(JAVA_HOME),)
JNI_INCLUDEDIR ?= $(JAVA_HOME)/include
endif

ifeq ($(JNI_INCLUDEDIR),)
$(error could not determine JNI include dir, try specifying either \
    JAVA_HOME or JNI_INCLUDEDIR)
endif

TARGETTRIPLET := $(shell $(CC) -dumpmachine)
ifeq ($(JNI_PLATFORM),)
ifeq ($(findstring mingw,$(TARGETTRIPLET)),mingw)
JNI_PLATFORM:= win32
else
ifeq ($(findstring linux,$(TARGETTRIPLET)),linux)
JNI_PLATFORM:= linux
# add more checks here
endif
endif
endif

JNI_PLATFORMINCLUDEDIR ?= $(JNI_INCLUDEDIR)/$(JNI_PLATFORM)

$(info JAVA_HOME: $(JAVA_HOME))
$(info JNI_INCLUDEDIR: $(JNI_INCLUDEDIR))
$(info JNI_PLATFORMINCLUDEDIR: $(JNI_PLATFORMINCLUDEDIR))

all:
    @:

.PHONY: all

示例输出:

> make
JAVA_HOME: /usr/lib/jvm/java-6-openjdk-amd64
JNI_INCLUDEDIR: /usr/lib/jvm/java-6-openjdk-amd64/include
JNI_PLATFORMINCLUDEDIR: /usr/lib/jvm/java-6-openjdk-amd64/include/linux

> make JAVA_HOME=/foo/bar
JAVA_HOME: /foo/bar
JNI_INCLUDEDIR: /foo/bar/include
JNI_PLATFORMINCLUDEDIR: /foo/bar/include/linux

> make JNI_INCLUDEDIR=/foo/bar
JAVA_HOME: /usr/lib/jvm/java-6-openjdk-amd64
JNI_INCLUDEDIR: /foo/bar
JNI_PLATFORMINCLUDEDIR: /foo/bar/linux

> make JNI_PLATFORM=amigaos
JAVA_HOME: /usr/lib/jvm/java-6-openjdk-amd64
JNI_INCLUDEDIR: /usr/lib/jvm/java-6-openjdk-amd64/include
JNI_PLATFORMINCLUDEDIR: /usr/lib/jvm/java-6-openjdk-amd64/include/amigaos

【讨论】:

  • 太棒了,谢谢@FelixPalmen。 jni_md.h 怎么样?这通常位于${JNI_INCLUDEDIR} 的依赖于操作系统的子文件夹中。有没有办法在${JNI_INCLUDEDIR} 中查找并包含子文件夹?
  • 这有点棘手,最好的方法是根据$(CC) -dumpmachine的输出来确定它......我将添加一个简单的例子
  • 看起来不错,它在我的 Ubuntu 16.04 中成功了。我会在 Windows 机器上尝试一下,如果它有效,我会将答案标记为正确。
  • 备案:我试过了,它确实按预期工作。已接受答案,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-05
  • 1970-01-01
  • 2011-03-14
  • 2012-10-10
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
相关资源
最近更新 更多