Using Panama “foreign” JDK

このエントリは2019年2月28日時点の以下の記事をベースにしています。
This entry is based on the following one as of February 28, 2019 (JST).
Using Panama “foreign” JDK
https://hg.openjdk.java.net/panama/dev/raw-file/c359a9e944de/doc/panama_foreign.html

Using Panama “foreign” JDK

panamaのforeignブランチのJDKを使うには2方法あります。

  1. panama リポジトリの”foreign”ブランチをローカルビルドする
    http://hg.openjdk.java.net/panama/dev/
  2. 事前ビルド済みのpanama “foreign” 早期アクセスバイナリをダウンロードする
    http://jdk.java.net/panama/

Javaで外部関数呼び出しを利用するには以下の3手順を踏みます。

  1. jextract を使ってCのヘッダーファイルに対するJavaインターフェースを生成する
  2. java.foreign APIを使ってCヘッダーインターフェースに対する実装を作成(バインド)する
  3. jextracted Javaインターフェースを使ってC関数を呼び出す

Windows notes

ほとんどのライブラリは間接的にVisual Studioランタイムライブラリに依存している(現時点ではjestractが問題なくヘッダーを抽出するにあたって必要)ため、Visual Studioのインストールが必要です。Windowsの例は Build Tools for Visual Studio 2017を用いてテストしました。

Build Tools for Visual Studio 2017
https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2017

多くの大きなシステムヘッダーが推移的に含まれているため、jextractにたくさんの追加メモリを割り当てるべきで、そうすればjextractはかなり快適に実行できるでしょう。Windowsのサポートはごく最近になって追加されたため、jextractのメモリ使用量はまだ最適化されていない関係上、メモリをたくさん割り当てるのは回避策です。 追加引数として -J-Xmx8G のように引数を渡せば、jextractにより多くのメモリを割り当てることができます。この例ではjextractに8GBのメモリを割り当てています。

コマンドはPowerShellでテストしています。

Embedding Python interpreter in your Java program (macOS)

jextract a Jar file for Python.h

jextract -l python2.7 \
  -L /System/Library/Frameworks/Python.framework/Versions/2.7/lib --record-library-path \
  --exclude-symbols .*_FromFormatV\|_.*\|PyOS_vsnprintf\|.*_VaParse.*\|.*_VaBuild.*\|PyBuffer_SizeFromFormat\|vasprintf\|vfprintf\|vprintf\|vsprintf \
  -t org.python \
  /usr/include/python2.7/Python.h \
  -o python.jar

Java program that uses extracted Python interface

// import java.foreign packages
import java.foreign.Libraries;
import java.foreign.Scope;
import java.foreign.memory.Pointer;

// import jextracted python 'header' classes
import static org.python.Python_h.*;
import static org.python.pythonrun_h.*;

public class PythonMain {
    public static void main(String[] args) {
        Py_Initialize();
        try (Scope s = Scope.newNativeScope()) {
            PyRun_SimpleStringFlags(s.allocateCString(
                "print(sum([33, 55, 66]));  print('Hello from Python!')n"),
                Pointer.nullPointer());
        }
        Py_Finalize();
    }
}

Running the Java code that calls Python interpreter

javac -cp python.jar PythonMain.java
java -cp python.jar:. PythonMain

jlinking Python Interpreter in your JDK (macOS)

Generating jmod using jextract

jextract -l python2.7 \
  -L /System/Library/Frameworks/Python.framework/Versions/2.7/lib \
  --exclude-symbols .*_FromFormatV\|_.*\|PyOS_vsnprintf\|.*_VaParse.*\|.*_VaBuild.*\|PyBuffer_SizeFromFormat\|vasprintf\|vfprintf\|vprintf\|vsprintf \
  -t org.python \
  /usr/include/python2.7/Python.h \
  -o org.python.jmod

Jlinking python module to create a JDK with Python in it

jdk.compiler と org.python モジュールを(jlinkで)生成したJDK(カスタムJRE)に追加します。

jlink --add-modules org.python,jdk.compiler --module-path . --output pythonjdk

Compile and run user code with “pythonjdk” jdk

環境変数 $PATH に $pythonjdk/bin が含まれている前提で、以下のコマンドを実行しています。

javac PythonMain.java
java PythonMain

Embedding Python interpreter in your Java program (Ubuntu 16.04)

jextract a Jar file for Python.h

jextract -l python2.7 \
  -L /usr/lib/python2.7/config-x86_64-linux-gnu --record-library-path \
  --exclude-symbols .*_FromFormatV\|_.*\|PyOS_vsnprintf\|.*_VaParse.*\|.*_VaBuild.*\|PyBuffer_SizeFromFormat\|vasprintf\|vfprintf\|vprintf\|vsprintf \
  -t org.python \
  /usr/include/python2.7/Python.h \
  -o python.jar

Compiling and Running Python Java example

macOSの章の手順に従ってください。

Embedding Python interpreter in your Java program (Windows)

jextract a Jar file for Python.h

ディレクトリ C:Python27 に python 2.7 がインストールされているものとします。

jextract -L "C:WindowsSystem32" -l python27 -o python.jar -t "org.python" --record-library-path C:Python27includePython.h

Compiling and Running Python Java example

javac -cp python.jar PythonMain.java
java -cp "python.jar;." PythonMain

Using sqlite3 library in your Java program (macOS)

jextract a jar file for sqlite3.h

jextract  /usr/include/sqlite3.h -t org.sqlite -lsqlite3 \
    -L /usr/lib --record-library-path         \
    --exclude-symbols sqlite3_vmprintf        \
    --exclude-symbols sqlite3_vsnprintf       \
    -o sqlite3.jar

Java sample that uses sqlite3 library

import java.lang.invoke.*;
import java.foreign.*;
import java.foreign.memory.*;
import org.sqlite.sqlite3.*;
import static org.sqlite.sqlite3_h.*;

public class SqliteMain {
    public static void main(String[] args) throws Exception {
        try ( Scope scope = Scope.newNativeScope()) {
            // char* errMsg;
            Pointer<Pointer<Byte>> errMsg = scope.allocate(NativeTypes.INT8.pointer());
            // sqlite3* db;
            Pointer<Pointer<sqlite3>> db = scope.allocate(LayoutType.ofStruct(sqlite3.class).pointer());
            int rc = sqlite3_open(scope.allocateCString("employee.db"), db);
            if (rc != 0) {
                System.err.println("sqlite3_open failed: " + rc);return;
            }
            // create a new table
            Pointer<Byte> sql = scope.allocateCString(
                                "CREATE TABLE EMPLOYEE ("  + 
                                "  ID INT PRIMARY KEY NOT NULL," +
                                "  NAME TEXT NOT NULL," +
                                "  SALARY REAL NOT NULL )" );
            // dummy ignoring callback
            Callback<FI1> callback = scope.allocateCallback(FI1.class, (a,b,c,d)->0);
            rc = sqlite3_exec(db.get(), sql, callback, Pointer.nullPointer(), errMsg);
            if (rc != 0) {
                System.err.println("sqlite3_exec failed: " + rc);
                System.err.println("SQL error: " + Pointer.toString(errMsg.get()));
                sqlite3_free(errMsg.get());
            }
            // insert two rows
            sql = scope.allocateCString(
                    "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " +
                    "VALUES (134, 'Xyz', 200000.0); " +
                    "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " +
                    "VALUES (333, 'Abc', 100000.0);"
                    );
            rc = sqlite3_exec(db.get(), sql, callback, Pointer.nullPointer(), errMsg);
            if (rc != 0) {
                System.err.println("sqlite3_exec failed: " + rc);
                System.err.println("SQL error: " + Pointer.toString(errMsg.get()));
                sqlite3_free(errMsg.get());
            }

            int[] rowNum = new int[1];
            // callback to print rows from SELECT query
            callback = scope.allocateCallback(FI1.class, (a, argc, argv, columnNames) -> {
                                System.out.println("Row num: " + rowNum[0]++);
                                System.out.println("numColumns = " + argc);
                                for (int i = 0; i < argc; i++) {
                                    String name = Pointer.toString(columnNames.offset(i).get());
                                    String value = Pointer.toString(argv.offset(i).get());
                                    System.out.printf("%s = %sn", name, value);
                                }
                                return 0;
                            });
                            
            // select query
            sql = scope.allocateCString("SELECT * FROM EMPLOYEE");
            rc = sqlite3_exec(db.get(), sql, callback, Pointer.nullPointer(), errMsg);
            if (rc != 0) {
                System.err.println("sqlite3_exec failed: " + rc);
                System.err.println("SQL error: " + Pointer.toString(errMsg.get()));
                sqlite3_free(errMsg.get());
            }
            sqlite3_close(db.get());
        }
    }
}

Compiling and Running sqlite Java example

javac -cp sqlite3.jar SqlMain.java
java -cp sqlite3.jar:. SqlMain

Using sqlite3 library in your Java program (Ubuntu 16.04)

Installing sqlite3

Ubuntu (16.04) にsqlite3ヘッダーファイルとライブラリをインストールするには、以下のコマンドを実行する必要があります。

sudo apt-get install libsqlite3-dev

これを実行すると、sqlite3のヘッダーファイル(インストール先は /usr/include )と共有ライブラリ(インストール先は /usr/lib/x86_64-linux-gnu )をインストールします。

jextract a jar file for sqlite3.h

以下のコマンドを実行して sqliteを抽出します。

jextract  /usr/include/sqlite3.h -t org.sqlite -lsqlite3 \
          -L /usr/lib/x86_64-linux-gnu --record-library-path \
           --exclude-symbols sqlite3_vmprintf \
           --exclude-symbols sqlite3_vsnprintf \
           -o sqlite3.jar

Compiling and Running sqlite Java example

macOS の手順を参照してください。上記の手順でライブラリを抽出すれば、サンプルプログラムがUbuntuでも動作するはずです。

Using BLAS library

BLAS とは高速な行列およびベクトル計算を可能にする有名なライブラリです。

BLAS (Basic Linear Algebra Subprograms)
http://www.netlib.org/blas/

Installing OpenBLAS (macOS)

Macの場合、BLASはOpenBLASライブラリの一部として利用できます。

OpenBLAS Wiki
https://github.com/xianyi/OpenBLAS/wiki

OpenBLASはGotoBLAS2の1.13 BSD版をベースにした最適化されたBLASライブラリです。

GotoBLAS2
https://www.tacc.utexas.edu/research-development/tacc-software/gotoblas2

openblasはHomeBrewを使ってインストールできます。

brew install openblas

/usr/local/opt/openblas配下の includelib ディレクトリにインストールされます。

Installing OpenBLAS (Ubuntu 16.04)

Ubuntuでは、BLASはatlasライブラリの一部として配布されます。 apt を使ってatlasをインストールできます。

Automatically Tuned Linear Algebra Software (ATLAS)
http://math-atlas.sourceforge.net/

sudo apt-get install libatlas-base-dev

このコマンドはincludeファイルを/usr/include/atlasにインストールするとともに、includeファイルに対応するライブラリを/usr/lib/atlas-devにインストールします。

jextracting cblas.h (macOS)

以下のコマンドを使ってmacOS上でcblas.hを抽出できます。

jextract -C "-D FORCE_OPENBLAS_COMPLEX_STRUCT" \
  -L /usr/local/opt/openblas/lib -I /usr/local/opt/openblas \
  -l openblas -t blas --record-library-path /usr/local/opt/openblas/include/cblas.h \
  -o cblas.jar

FORCE_OPENBLAS_COMPLEX_STRUCT というdefineが必要なのは、jextractがまだC99 の_Complex型に対応していないためです。その他のオプションは標準的なものです。

jextracting cblas.h (Ubuntu 16.04)

以下のコマンドを使ってUbuntuでcblas.hを抽出できます。

jextract -L /usr/lib/atlas-base -I /usr/include/atlas/ \
   -l cblas -t blas --record-library-path \
   /usr/include/atlas/cblas.h -o cblas.jar

Java sample code that uses cblas library

import blas.cblas;
import static blas.cblas_h.*;
import java.foreign.NativeTypes;
import java.foreign.Scope;
import java.foreign.memory.Array;
public class TestBlas {
   public static void main(String[] args) {
       @cblas.CBLAS_ORDER int Layout;
       @cblas.CBLAS_TRANSPOSE int transa;
       double alpha, beta;
       int m, n, lda, incx, incy, i;
       Layout = CblasColMajor;
       transa = CblasNoTrans;
       m = 4; /* Size of Column ( the number of rows ) */
       n = 4; /* Size of Row ( the number of columns ) */
       lda = 4; /* Leading dimension of 5 * 4 matrix is 5 */
       incx = 1;
       incy = 1;
       alpha = 1;
       beta = 0;
       try (Scope sc = Scope.newNativeScope()){
           Array<Double> a = sc.allocateArray(NativeTypes.DOUBLE, m * n);
           Array<Double> x = sc.allocateArray(NativeTypes.DOUBLE, n);
           Array<Double> y = sc.allocateArray(NativeTypes.DOUBLE, n);
           /* The elements of the first column */
           a.set(0, 1.0);
           a.set(1, 2.0);
           a.set(2, 3.0);
           a.set(3, 4.0);
           /* The elements of the second column */
           a.set(m, 1.0);
           a.set(m + 1, 1.0);
           a.set(m + 2, 1.0);
           a.set(m + 3, 1.0);
           /* The elements of the third column */
           a.set(m * 2, 3.0);
           a.set(m * 2 + 1, 4.0);
           a.set(m * 2 + 2, 5.0);
           a.set(m * 2 + 3, 6.0);
           /* The elements of the fourth column */
           a.set(m * 3, 5.0);
           a.set(m * 3 + 1, 6.0);
           a.set(m * 3 + 2, 7.0);
           a.set(m * 3 + 3, 8.0);
           /* The elemetns of x and y */
           x.set(0, 1.0);
           x.set(1, 2.0);
           x.set(2, 1.0);
           x.set(3, 1.0);
           y.set(0, 0.0);
           y.set(1, 0.0);
           y.set(2, 0.0);
           y.set(3, 0.0);
           cblas_dgemv(Layout, transa, m, n, alpha, a.elementPointer(), lda, x.elementPointer(), incx, beta,
                   y.elementPointer(), incy);
           /* Print y */
           for (i = 0; i < n; i++)
               System.out.print(String.format(" y%d = %f\n", i, y.get(i)));
       }
   }
}

Compiling and running the above cblas samples

javac -cp cblas.jar TestBlas.java
java -cp cblas.jar:. TestBlas

Using LAPACK library (Ubuntu)

Ubuntuでは、(atlasを使って) blasをインストールするために使った同じ手順で、blas上でビルドされた線形代数計算ライブラリのLAPACKのライブラリのヘッダーファイルとライブラリをインストールします。

jextracting clapack.h (Ubuntu 16.04)

以下のコマンドを使ってLAPACKのヘッダーを抽出できます。

jextract -L /usr/lib/atlas-base/atlas -I /usr/include/atlas/ \
   -l lapack -t lapack --record-library-path /usr/include/atlas/clapack.h -o clapack.jar

Java sample code that uses LAPACK library

import java.foreign.NativeTypes;
import java.foreign.Scope;
import java.foreign.memory.Array;
import static lapack.clapack_h.*;
import static lapack.cblas_h.*;
public class TestLapack {
    public static void main(String[] args) {
        /* Locals */
        try (Scope sc = Scope.newNativeScope()) {
            Array<Double> A = sc.allocateArray(NativeTypes.DOUBLE, new double[]{
                    1, 2, 3, 4, 5, 1, 3, 5, 2, 4, 1, 4, 2, 5, 3
            });
            Array<Double> b = sc.allocateArray(NativeTypes.DOUBLE, new double[]{
                    -10, 12, 14, 16, 18, -3, 14, 12, 16, 16
            });
            int info, m, n, lda, ldb, nrhs;
            /* Initialization */
            m = 5;
            n = 3;
            nrhs = 2;
            lda = 5;
            ldb = 5;
            /* Print Entry Matrix */
            print_matrix_colmajor("Entry Matrix A", m, n, A, lda );
            /* Print Right Rand Side */
            print_matrix_colmajor("Right Hand Side b", n, nrhs, b, ldb );
            System.out.println();
            /* Executable statements */
            //            printf( "LAPACKE_dgels (col-major, high-level) Example Program Results\n" );
            /* Solve least squares problem*/
            info = clapack_dgels(CblasColMajor, CblasNoTrans, m, n, nrhs, A.elementPointer(), lda, b.elementPointer(), ldb);
            /* Print Solution */
            print_matrix_colmajor("Solution", n, nrhs, b, ldb );
            System.out.println();
            System.exit(info);
        }
    }
    static void print_matrix_colmajor(String msg, int m, int n, Array<Double> mat, int ldm) {
        int i, j;
        System.out.printf("\n %s\n", msg);
        for( i = 0; i < m; i++ ) {
            for( j = 0; j < n; j++ ) System.out.printf(" %6.2f", mat.get(i+j*ldm));
            System.out.printf( "\n" );
        }
    }
}

Compiling and running the above LAPACK sample

javac -cp clapack.jar TestLapack.java
java -cp clapack.jar:. TestLapack

Using LAPACK library (macOS)

macOSでは、lapackは /usr/opt/lapackにインストールされます。

jextracting lapacke.h

jextract -L /usr/local/opt/lapack/lib -I /usr/local/opt/lapack/ \
  -l lapacke -t lapack --record-library-path /usr/local/opt/lapack/include/lapacke.h -o clapack.jar

Java sample code that uses LAPACK library

import java.foreign.NativeTypes;
import java.foreign.Scope;
import java.foreign.memory.Array;
import static lapack.lapacke_h.*;
public class TestLapack {
    public static void main(String[] args) {
        /* Locals */
        try (Scope sc = Scope.newNativeScope()) {
            Array<Double> A = sc.allocateArray(NativeTypes.DOUBLE, new double[]{
                    1, 2, 3, 4, 5, 1, 3, 5, 2, 4, 1, 4, 2, 5, 3
            });
            Array<Double> b = sc.allocateArray(NativeTypes.DOUBLE, new double[]{
                    -10, 12, 14, 16, 18, -3, 14, 12, 16, 16
            });
            int info, m, n, lda, ldb, nrhs;
            /* Initialization */
            m = 5;
            n = 3;
            nrhs = 2;
            lda = 5;
            ldb = 5;
            /* Print Entry Matrix */
            print_matrix_colmajor("Entry Matrix A", m, n, A, lda );
            /* Print Right Rand Side */
            print_matrix_colmajor("Right Hand Side b", n, nrhs, b, ldb );
            System.out.println();
            /* Executable statements */
            //            printf( "LAPACKE_dgels (col-major, high-level) Example Program Results\n" );
            /* Solve least squares problem*/
            info = LAPACKE_dgels(LAPACK_COL_MAJOR, (byte)'N', m, n, nrhs, A.elementPointer(), lda, b.elementPointer(), ldb);
            /* Print Solution */
            print_matrix_colmajor("Solution", n, nrhs, b, ldb );
            System.out.println();
            System.exit(info);
        }
    }
    static void print_matrix_colmajor(String msg, int m, int n, Array<Double> mat, int ldm) {
        int i, j;
        System.out.printf("\n %s\n", msg);
        for( i = 0; i < m; i++ ) {
            for( j = 0; j < n; j++ ) System.out.printf(" %6.2f", mat.get(i+j*ldm));
            System.out.printf( "\n" );
        }
    }
}

Compiling and running the above LAPACK sample

javac -cp clapack.jar TestLapack.java
java -cp clapack.jar:. TestLapack

Using libproc library to list processes from Java (macOS)

jextract a jar file for libproc.h

jextract -t org.unix -lproc -L /usr/lib — record-library-path -o libproc.jar /usr/include/libproc.h

Java program that uses libproc to list processes

import java.foreign.*;
import java.foreign.memory.*;
import static org.unix.libproc_h.*;
public class LibprocMain {
    private static final int NAME_BUF_MAX = 256;
    public static void main(String[] args) {
        // Scope for native allocations
        try (Scope s = Scope.newNativeScope()) {
            // get the number of processes
            int numPids = proc_listallpids(Pointer.nullPointer(), 0);
            // allocate an array
            Array<Integer> pids = s.allocateArray(NativeTypes.INT32, numPids);
            // list all the pids into the native array
            proc_listallpids(pids.elementPointer(), numPids);
            // convert native array to java array
            int[] jpids = pids.toArray(num -> new int[num]);
            // buffer for process name
            Pointer<Byte> nameBuf = s.allocate(NativeTypes.INT8, NAME_BUF_MAX);
            for (int i = 0; i < jpids.length; i++) {
                int pid = jpids[i];
                // get the process name
                proc_name(pid, nameBuf, NAME_BUF_MAX);
                String procName = Pointer.toString(nameBuf);
                // print pid and process name
                System.out.printf("%d %s\n", pid, procName);
            }
        }
    }
}

Running the Java code that uses libproc

javac -cp libproc.jar LibprocMain.java
java -cp libproc.jar:. LibprocMain

Using readline library from Java code (macOS)

jextract a jar file for readline.h

jextract -l readline -L /usr/local/opt/readline/lib/ --record-library-path \
    -t org.unix \
    /usr/include/readline/readline.h \
    --exclude-symbol readline_echoing_p -o readline.jar

Java code that uses readline

import java.foreign.*;
import java.foreign.memory.*;
import static org.unix.readline_h.*;
public class Readline {
    public static void main(String[] args) {
        // Scope for native allocations
        try (Scope s = Scope.newNativeScope()) {
            // allocate C memory initialized with Java string content
            var pstr = s.allocateCString("name? ");
            // call "readline" API
            var p = readline(pstr);
            // print char* as is
            System.out.println(p);
            // convert char* ptr from readline as Java String & print it
            System.out.println(Pointer.toString(p));
        }
    }
}

Running the java code that uses readline

javac -cp readline.jar Readline.java
java -cp readline.jar:. Readline

Using libcurl from Java (macOS)

jextract a jar for curl.h

jextract -t org.unix -L /usr/lib -lcurl --record-library-path /usr/include/curl/curl.h -o curl.jar

Java code that uses libcurl

import java.lang.invoke.*;
import java.foreign.*;
import java.foreign.memory.*;
import org.unix.curl.*;
import static org.unix.curl_h.*;
import static org.unix.easy_h.*;
public class CurlMain {
   public static void main(String[] args) {
       try (Scope s = Scope.newNativeScope()) { 
           curl_global_init(CURL_GLOBAL_DEFAULT);
           Pointer<Void> curl = curl_easy_init();
           if(!curl.isNull()) {
               Pointer<Byte> url = s.allocateCString(args[0]);
               curl_easy_setopt(curl, CURLOPT_URL, url);
               int res = curl_easy_perform(curl);
               if (res != CURLE_OK) {
                   curl_easy_cleanup(curl);
               }
           }
           curl_global_cleanup();
       }
    }
}

Running the java code that uses libcurl

javac -cp curl.jar CurlMain.java
java -cp curl.jar:. CurlMain <url>

Using unistd.h from Java code (Linux)

jextract a jar file for unistd.h

jextract /usr/include/unistd.h -t org.unix -o unistd.jar

Java code that calls getpid

import java.foreign.*;
import java.lang.invoke.*;
import org.unix.unistd;

public class Getpid {
    public static void main(String[] args) {
        // bind unistd interface
        var u = Libraries.bind(MethodHandles.lookup(), unistd.class);
        // call getpid from the unistd.h
        System.out.println(u.getpid());
        // check process id from Java API!
        System.out.println(ProcessHandle.current().pid());
    }
}

Running the Java code that uses getpid

javac -cp unistd.jar Getpid.java
java -cp unistd.jar:. Getpid

Using OpenGL graphic library (Ubuntu 16.04)

OpenGL は人気のある可搬性のあるグラフィックライブラリです。

OpenGL — The Industry Standard for High Performance Graphics
https://www.opengl.org/

Installing OpenGL (Ubuntu 16.04)

関連するOpenGLのヘッダーファイルとライブラリのインストールは、どのグラフィックカードが対象となるプラットフォームにインストールされているかに依存するため、少しトリッキーになることがあります。動作するようにするための変更はそれほど多くないのですが、以下の説明では、(NvidiaまたはAMDの)プロプライエタリなものではなく、(mesaなどの)OpenGLの標準バージョンを使用していることを前提としています。

OpenGLは常に多くの他のライブラリ、具体的にはGLUやglutと結びついています。以下のようにこれらのライブラリはすべて aptを使ってインストールできます。

sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev
インストールが成功すると、OpenGLのヘッダーファイルは /usr/include/GL に、ライブラリは/usr/lib/x86_64-linux-gnu/にそれぞれ配置されていることがわかります。

jextracting OpenGL (Ubuntu 16.04)

OpenGLライブラリの抽出にあたっては、以下のコマンドで十分です。

jextract -L /usr/lib/x86_64-linux-gnu  -l glut -l GLU -l GL --record-library-path -t opengl -o opengl.jar /usr/include/GL/glut.h

glutは別のライブラリ (GLU とGL) に依存しているため、jextractに追加のヘッダーを指定する必要はありません。

Java sample code that uses the OpenGL library

import java.foreign.NativeTypes;
import java.foreign.Scope;
import java.foreign.memory.Array;
import java.foreign.memory.Pointer;
import static opengl.gl_h.*;
import static opengl.freeglut_std_h.*;
public class Teapot {
    float rot = 0;
    Teapot(Scope sc) {
        // Misc Parameters
        Array<Float> pos = sc.allocateArray(NativeTypes.FLOAT, new float[] {0.0f, 15.0f, -15.0f, 0});
        Array<Float> spec = sc.allocateArray(NativeTypes.FLOAT, new float[] {1, 1, 1, 0});
        Array<Float> shini = sc.allocateArray(NativeTypes.FLOAT, new float[] {113});
        // Reset Background
        glClearColor(0, 0, 0, 0);
        // Setup Lighting
        glShadeModel(GL_SMOOTH);
        glLightfv(GL_LIGHT0, GL_POSITION, pos.elementPointer());
        glLightfv(GL_LIGHT0, GL_AMBIENT, spec.elementPointer());
        glLightfv(GL_LIGHT0, GL_DIFFUSE, spec.elementPointer());
        glLightfv(GL_LIGHT0, GL_SPECULAR, spec.elementPointer());
        glMaterialfv(GL_FRONT, GL_SHININESS, shini.elementPointer());
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_DEPTH_TEST);
    }
    void display() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glPushMatrix();
        glRotatef(-20, 1, 1, 0);
        glRotatef(rot, 0, 1, 0);
        glutSolidTeapot(0.5);
        glPopMatrix();
        glutSwapBuffers();
    }
    void onIdle() {
        rot += 0.1;
        glutPostRedisplay();
    }
    public static void main(String[] args) {
        try (Scope sc = Scope.newNativeScope()) {
            Pointer<Integer> argc = sc.allocate(NativeTypes.INT32);
            argc.set(0);
            glutInit(argc, Pointer.nullPointer());
            glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
            glutInitWindowSize(900, 900);
            glutCreateWindow(sc.allocateCString("Hello Panama!"));
            Teapot teapot = new Teapot(sc);
            glutDisplayFunc(sc.allocateCallback(teapot::display));
            glutIdleFunc(sc.allocateCallback(teapot::onIdle));
            glutMainLoop();
        }
    }
}

Running the Java code that uses OpenGL (Ubuntu 16.04)

javac -cp opengl.jar Teapot.java
java -cp opengl.jar:. Teapot

Using OpenGL graphic library (Windows)

Installing OpenGL

以下の場所からMSVC用のfreeglutパッケージをダウンロードします。

freeglut Windows Development Libraries
https://www.transmissionzero.co.uk/software/freeglut-devel/

freeglutのZipファイルを展開します。

jextracting OpenGL

展開したZipファイルのルートディレクトリに移動して、以下のコマンドを実行します。

$inc = "C:\Program Files (x86)\Windows Kits\10\Include\10.0.17134.0"
jextract -L C:\Windows\System32\ -L .\freeglut\bin\x64\ -l opengl32 -l freeglut -t opengl -o opengl.jar -m "$inc\um\gl=opengl" --record-library-path .\freeglut\include\GL\glut.h

$inc に割り当てたディレクトリはあくまでもサンプルであり、システムに依存します。Pathの最後のビルド番号 (この例では10.0.17134.0) が、親フォルダに現れているバージョン (この例ではC:Program Files (x86)Windows Kits10Includeなので 10) の最新版であることを確認してください。

多数の警告が出ますが、jarファイルが作業ディレクトリに生成されていれば、展開は成功しています。

Java sample code that uses the OpenGL library

これはUbuntuの章に記載したものと同じです。

Running the Java code that uses OpenGL

javac -cp .opengl.jar Teapot.java
java -cp "opengl.jar;." Teapot

Using TensorFlow C API (macOS)

“TensorFlow provides a C API that can be used to build bindings for other languages. The API is defined in c_api.h and designed for simplicity and uniformity rather than convenience.” (TensorFlowはC言語のAPIを提供しており、これを使って多言語のバインディングを作成できます。APIはc_api.hで定義され、利便性よりはむしろ簡潔性と統一性のために設計されています。)

Install TensorFlow for C
https://www.tensorflow.org/install/lang_c

Installing libtensorflow

上記URLに記載のセットアップ手順に従ってください。Macの場合、HomeBrewを使ってもibtensorflowをインストールできます。

brew install libtensorflow

Tensorflowはlibtensorflowを.soという拡張子の形(共有ライブラリ)で同梱しています。JavaはmacOSの場合、.dylibという拡張子を想定しているためうまく動作しません。これを回避するために、シンボリックリンクを作成します。

sudo ln -s /usr/local/lib/libtensorflow.so /usr/local/lib/libtensorflow.dylib

jextracting libtensorflow c_api.h

以下のコマンドを使ってc_api.hを抽出できます。

jextract -C -x -C c++  \
        -L /usr/local/lib -l tensorflow --record-library-path \
        -o tf.jar -t org.tensorflow.panama \
        /usr/local/include/tensorflow/c/c_api.h

TensorFlow C APIを抽出する際の注意点は、C++スタイルで引数なしで関数プロトタイプを宣言することです。例えば、 TF_Vision()TF_Version(void) のようにCスタイルではなく、不完全なC関数プロトタイプと見なされます。不完全な関数プロトタイプは可変長引数関数になるでしょう。それを避けるために、-C -x -C c++ を使って、clangの -x c++ オプションをjextractに渡す必要があります。

Java sample code that uses tensorflow library

import java.foreign.NativeTypes;
import java.foreign.Scope;
import java.foreign.memory.Array;
import java.foreign.memory.LayoutType;
import java.foreign.memory.Pointer;
import org.tensorflow.panama.c_api.TF_DataType;
import org.tensorflow.panama.c_api.TF_Graph;
import org.tensorflow.panama.c_api.TF_Operation;
import org.tensorflow.panama.c_api.TF_OperationDescription;
import org.tensorflow.panama.c_api.TF_Output;
import org.tensorflow.panama.c_api.TF_Session;
import org.tensorflow.panama.c_api.TF_SessionOptions;
import org.tensorflow.panama.c_api.TF_Status;
import org.tensorflow.panama.c_api.TF_Tensor;
import static org.tensorflow.panama.c_api_h.*;
public class TensorFlowExample {
    static Pointer<TF_Operation> PlaceHolder(Pointer<TF_Graph> graph, Pointer<TF_Status> status,
                                      @TF_DataType int dtype, String name) {
        try (var s = Scope.newNativeScope()) {
            Pointer<TF_OperationDescription> desc = TF_NewOperation(graph,
                    s.allocateCString("Placeholder"), s.allocateCString(name));
            TF_SetAttrType(desc, s.allocateCString("dtype"), TF_FLOAT);
            return TF_FinishOperation(desc, status);
        }
    }
    static Pointer<TF_Operation> ConstValue(Pointer<TF_Graph> graph, Pointer<TF_Status> status,
                                Pointer<TF_Tensor> tensor, String name) {
        try (var s = Scope.newNativeScope()) {
            Pointer<TF_OperationDescription> desc = TF_NewOperation(graph,
                    s.allocateCString("Const"), s.allocateCString(name));
            TF_SetAttrTensor(desc, s.allocateCString("value"), tensor, status);
            TF_SetAttrType(desc, s.allocateCString("dtype"), TF_TensorType(tensor));
            return TF_FinishOperation(desc, status);
        }
    }
    static Pointer<TF_Operation> Add(Pointer<TF_Graph> graph, Pointer<TF_Status> status,
                              Pointer<TF_Operation> one, Pointer<TF_Operation> two,
                              String name) {
        try (var s = Scope.newNativeScope()) {
            Pointer<TF_OperationDescription> desc = TF_NewOperation(graph,
                    s.allocateCString("AddN"), s.allocateCString(name));
            Array<TF_Output> add_inputs = s.allocateArray(
                    LayoutType.ofStruct(TF_Output.class),2);
            add_inputs.get(0).oper$set(one);
            add_inputs.get(0).index$set(0);
            add_inputs.get(1).oper$set(two);
            add_inputs.get(1).index$set(0);
            TF_AddInputList(desc, add_inputs.elementPointer(), 2);
            return TF_FinishOperation(desc, status);
        }
    }
    public static void main(String... args) {
        System.out.println("TensorFlow C library version: " + Pointer.toString(TF_Version()));
        Pointer<TF_Graph> graph = TF_NewGraph();
        Pointer<TF_SessionOptions> options = TF_NewSessionOptions();
        Pointer<TF_Status> status = TF_NewStatus();
        Pointer<TF_Session> session = TF_NewSession(graph, options, status);
        float in_val_one = 4.0f;
        float const_two = 2.0f;
        Pointer<TF_Tensor> tensor_in = TF_AllocateTensor(TF_FLOAT, Pointer.nullPointer(), 0, Float.BYTES);
        TF_TensorData(tensor_in).cast(NativeTypes.FLOAT).set(in_val_one);
        Pointer<TF_Tensor> tensor_const_two = TF_AllocateTensor(TF_FLOAT, Pointer.nullPointer(), 0, Float.BYTES);
        TF_TensorData(tensor_const_two).cast(NativeTypes.FLOAT).set(const_two);
        // Operations
        Pointer<TF_Operation> feed = PlaceHolder(graph, status, TF_FLOAT, "feed");
        Pointer<TF_Operation> two = ConstValue(graph, status, tensor_const_two, "const");
        Pointer<TF_Operation> add = Add(graph, status, feed, two, "add");

        try (var s = Scope.newNativeScope()) {
            var ltPtrTensor = LayoutType.ofStruct(TF_Tensor.class).pointer();
            // Session Inputs
            TF_Output input_operations = s.allocateStruct(TF_Output.class);
            input_operations.oper$set(feed);
            input_operations.index$set(0);
            Pointer<Pointer<TF_Tensor>> input_tensors = s.allocate(ltPtrTensor);
            input_tensors.set(tensor_in);
            // Session Outputs
            TF_Output output_operations = s.allocateStruct(TF_Output.class);
            output_operations.oper$set(add);
            output_operations.index$set(0);
            Pointer<Pointer<TF_Tensor>> output_tensors = s.allocate(ltPtrTensor);
            TF_SessionRun(session, Pointer.nullPointer(),
                // Inputs
                input_operations.ptr(), input_tensors, 1,
                // Outputs
                output_operations.ptr(), output_tensors, 1,
                // Target operations
                Pointer.nullPointer(), 0, Pointer.nullPointer(),
                status);
            System.out.println(String.format("Session Run Status: %d - %s",
                    TF_GetCode(status), Pointer.toString(TF_Message(status))));
            Pointer<TF_Tensor> tensor_out = output_tensors.get();
            System.out.println("Output Tensor Type: " + TF_TensorType(tensor_out));
            float outval = TF_TensorData(tensor_out).cast(NativeTypes.FLOAT).get();
            System.out.println("Output Tensor Value: " + outval);
            TF_CloseSession(session, status);
            TF_DeleteSession(session, status);
            TF_DeleteSessionOptions(options);
            TF_DeleteGraph(graph);
            TF_DeleteTensor(tensor_in);
            TF_DeleteTensor(tensor_out);
            TF_DeleteTensor(tensor_const_two);
            TF_DeleteStatus(status);
        }
    }
}

Compiling and running the above TensorFlow sample

javac -cp tf.jar TensorFlowExample.java 
java -cp tf.jar:. TensorFlowExample 

Using TensorFlow C API (Windows)

Installing libtensorflow

以下のURLからバイナリをダウンロードできます。ダウンロードしたらZipファイルを展開してください。

Install TensorFlow for C
https://www.tensorflow.org/install/lang_c

jextracting libtensorflow c_api.h

不正な関数プロトタイプに関するmacOSの命令で概説された問題はまだ存在します(TensorFlowのGitHubリポジトリで解決されましたが、この変更はバイナリディストリビューションには反映されていません)。しかしながら、Windowsではこれを回避するjextractコマンドがないため、 includecc_api.hを正しく抽出するには、誤った関数プロトタイプを手作業で修正するしかありません。

TF_Version() -> TF_Version(void)  
TF_NewGraph() -> TF_NewGraph(void)  
TF_NewSessionOptions() -> TF_NewSessionOptions(void)  
TF_NewStatus() -> TF_NewStatus(void)
TF_NewBuffer() -> TF_NewBuffer(void)
TF_NewImportGraphDefOptions() -> TF_NewImportGraphDefOptions(void)
TF_GetAllOpList() -> TF_GetAllOpList(void)

修正が完了したら、libtensorflowのルートディレクトリから以下のjextractコマンドを使うことができます。

jextract -L .\lib -l tensorflow -o tf.jar -t "org.tensorflow.panama" --record-library-path .\include\tensorflow\c\c_api.h

Java sample code that uses tensorflow library

macOSの章と同じです。

Compiling and running the above TensorFlow sample

javac -cp tf.jar TensorFlowExample.java
java -cp "tf.jar;." TensorFlowExample

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中