4.2. ビルド

注記

JNI では C 言語のコンパイル・ビルドが必要となります。ビルドについては「付録:make」(付録C Makeツール)を参照ください。

以下の Makefile では javah や gcc 等のコマンドを使ったヘッダーの生成や、コンパイル・ビルドを「部分的」に自動化しています。「部分的」にという表現には、手動の要素が残されるからです。

まず C 言語のヘッダーの自動生成を行うには、「header_gen」という名称のターゲットを実行します。

$ pwd
/Users/komatsu/JniJnaRepo/HelloJNIPackage/jni
$ ls
makefile
$ make header_gen
javah -verbose -classpath ../target/classes com.book.jni.HelloJNI
[Creating file RegularFileObject[com_book_jni_HelloJNI.h]]
$ ls
com_book_jni_HelloJNI.h
makefile

これでヘッダーファイルの生成ができましたので、「com_book_jni_HelloJNI.c」を作成して、C言語のソースコードを記述します。ここが手動部分に該当します。

ソースコードが完成したら、cc/gcc/clangを使ったビルドの流れを解説します。ビルドには2つのステップがあります。

  1. 「com_book_jni_HelloJNI.c」からHelloJNI.oをビルド
  2. 「HelloJNI.o」からlibhello.dylibをビルド

前者はソースコードのコンパイル、後者はライブラリの生成・ビルドをします。このステップは「all」という名称のターゲットで実行できます。

$ make all
cc -c -I/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include/ -I /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include/darwin/ -c com_book_jni_HelloJNI.c -o HelloJNI.o
cc -dynamiclib -o libhello.dylib HelloJNI.o -framework JavaVM -framework OpenCL

これでネイティブライブラリ「libhello.dylib」が生成されます。生成されたファイルは以下のように確認できます。

$ ls
com_book_jni_HelloJNI.c libhello.dylib
HelloJNI.o              com_book_jni_HelloJNI.h makefile

最後にJNIクラスを実行します。

$ cd ../target/classes
$ java com.book.jni.HelloJNI
CL_DEVICE_NAME: Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
CL_DEVICE_NAME: HD Graphics 4000

このOpenCL実装がサポートデバイスを検索した結果を、標準出力し正常にプログラムが実行されたことが確認できました。

makefileのソースは以下のようになります。

makefile. 

# クラスパスを定義
CLASS_PATH = ../target/classes

# binディレクトリ内の.classファイルの仮想パスを定義
#vpath %.class $(CLASS_PATH)

all : libhello.dylib
        mv libhello.dylib $(CLASS_PATH)

# .dylib(Max OS X)を出力します。
# Windowsの場合は、hello.dll、Linuxの場合は libhello.so
# 出力: libhello.dylib
libhello.dylib : HelloJNI.o
        cc -dynamiclib -o $@ $< -framework JavaVM -framework OpenCL

# 検証環境ではjdk1.8.0_05を使用しています。インクルードパスはjdkバージョンと合わせます。
# 出力:HelloJNI.o
HelloJNI.o : com_book_jni_HelloJNI.c com_book_jni_HelloJNI.h
        cc -c -I/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include/ -I /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include/darwin/ -c $< -o $@

# ヘッダーファイルを自動生成します。
# 手動でヘッダーファイルを作る場合はこのステップは不要
# 出力:com_book_jni_HelloJNI.h
header_gen :
        javah -verbose -classpath $(CLASS_PATH) com.book.jni.HelloJNI

clean :
        rm com_book_jni_HelloJNI.h com_book_jni_HelloJNI.c HelloJNI.o $(CLASS_PATH)/libhello.dylib

4.2.1. javah

前の項目では、ヘッダーファイルを「header_gen」というmakeターゲットを呼び出して生成しました。makeターゲットはjavahというツールをバックグランドで実行しており、ヘッダーファイルを作成しているのですが、ここではjavahの使い方について解説します。

ネイティブ関数を持つJavaクラスからC形式のヘッダーファイルを生成するJDKツールです。Javaメソッドのシグネチャーをネイティブ関数プロトタイプに適合させます。

javahはCLIで呼び出し、「package_name_classname.h 」の形式を持つC言語ヘッダーファイルを生成します。

例えば「HelloJNI.class」から生成したクラスからヘッダーを生成するには以下の手順で行います。まずソースコードとなるHelloJNI.javaをコンパイルして、クラスファイルを生成します。

javac -d target/classes/ src/main/java/com/book/jni/HelloJNI.java

注記

この例はMavenで生成したプロジェクトのため「src/main/java」がソースコードのルートパスとなりますが、プロジェクトによっては「src」フォルダにパッケージがあるかもしれません。

次にクラスファイルが以下のように生成されたことを「ls」「cd」コマンド等で探索して確認します。

target
├── classes
│   ├── com
│   │   └── book
│   │       └── jni
│   │           └── HelloJNI.class

最後にクラスパスでjavahコマンドを発行します。

$ cd target/classes
$ javah -jni com.book.jni.HelloJNI

Makefile(makefile)を使うビルドでは、以下のように事前に設定しておいたクラスパスを使うと便利です。

$ javah -verbose -classpath $(CLASS_PATH) com.book.jni.HelloJNI

このjavahはヘッダーを生成するだけのことしかしません。そのため手動で書けるだけの知識があるのであれば、必須なステップではありません。ただし、javaソースコードの関数プロトタイプの個数が多くなるのであれば、自動生成を使うのは悪い考えではないでしょう。

4.2.2. JNIバイナリのビルド

Mac OS XでのJNIバイナリのビルドには、「jni.h」をインクルードするために以下のようなコマンドが発行されます。

gcc -c -I/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include/ -I /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include/darwin/ -c com_book_jni_HelloJNI.c -o HelloJNI.o

Linux

警告

Linuxでのコンパイル・ビルドは未検証です。

Linuxではコンパイルオプションは以下のように設定します。

-I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux

gccを使う場合は以下のようにすれば、ビルドできるはずです。

gcc -c -I$(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux -c com_book_jni_HelloJNI.c -o HelloJNI.o

Windows

警告

Windowsでのコンパイル・ビルドは未検証です。この項目ではMinGW32がインストールされていることを前提とします。

MinGW32をインストールする場合、Linuxと同様にコンパイルオプションを指定しますが、以下のように「linux」フォルダではなく、「win32」フォルダを指定します。

本書ではサポートしないVisual Studioですが、「追加のインクルードディレクトリ」で以下のように設定します。

"$(JAVA_HOME)\include";
"$(JAVA_HOME)\include\win32"

64bit Windowsの場合は、MinGW-w64をインストールをして、x64アーキテクチャに相当するフォルダを指定してください。

Copyright 2018-2019, by Masaki Komatsu