1 <?xml version="1.0" encoding="utf-8"?> 2 3 # Using the `jextract` tool 4 5 `jextract` is a simple - but convenient - tool which generates a Java API from one or more native C headers. The tool can be obtained by building the [foreign-jextract](https://github.com/openjdk/panama-foreign) branch of Panama foreign repository. 6 7 Interacting with the `jextract` tool usually involves two steps: 8 9 1. Use the `jextract` tool to generate a java interface for some C header files 10 2. Write a Java program which invokes the wrapper API points generated by `jextract` 11 12 The `jextract` tool provides some basic options in order to control how the extraction process works; these are listed below: 13 14 * `-C <String>` - specify arguments to be passed to the underlying Clang parser 15 * `-I <String>` - specify include files path 16 * `-l <String>` - specify a library (name or full absolute path) which should be linked when the generated API is loaded 17 * `-d <String>` - specify where to place generated files 18 * `-t <String>` specify the target package for the generated classes 19 * --include-function <String> - name of function to include 20 * --include-macro <String> - name of constant macro to include 21 * --include-struct <String> - name of struct definition to include 22 * --include-typedef <String> - name of type definition to include 23 * --include-union <String> - name of union definition to include 24 * --include-var <String> - name of global variable to include 25 * `--source` - generate java sources instead of classfiles 26 27 The remainder of this documents shows some basic usage examples of the `jextract` tool. 28 29 ## Hello World 30 31 ### Hello World C Header (helloworld.h) 32 33 ```C 34 35 #ifndef helloworld_h 36 #define helloworld_h 37 38 extern void helloworld(void); 39 40 #endif /* helloworld_h */ 41 42 43 ``` 44 45 ### Hello World C Source (helloworld.c) 46 47 ```C 48 49 #include <stdio.h> 50 51 #include "helloworld.h" 52 53 void helloworld(void) { 54 printf("Hello World!\n"); 55 } 56 57 ``` 58 59 ### Building Hello World 60 61 ```sh 62 63 cc -shared -o libhelloworld.dylib helloworld.c 64 65 ``` 66 67 68 ### jextract a Jar file for helloworld.h 69 70 ```sh 71 72 jextract -t org.hello -lhelloworld helloworld.h 73 74 ``` 75 76 ### Java program that uses extracted helloworld interface 77 78 ```java 79 80 import static org.hello.helloworld_h.*; 81 82 public class HelloWorld { 83 public static void main(String[] args) { 84 helloworld(); 85 } 86 } 87 88 ``` 89 90 ### Running the Java code that invokes helloworld 91 92 ```sh 93 94 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign HelloWorld.java 95 96 ``` 97 98 ## Embedding Python interpreter in your Java program (Mac OS) 99 100 ### jextract Python.h 101 102 ```sh 103 104 jextract \ 105 -l python2.7 \ 106 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 107 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/python2.7/ \ 108 -t org.python \ 109 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/python2.7/Python.h 110 111 ``` 112 113 ### Java program that uses extracted Python interface 114 115 ```java 116 117 import jdk.incubator.foreign.ResourceScope; 118 import jdk.incubator.foreign.SegmentAllocator; 119 import static jdk.incubator.foreign.MemoryAddress.NULL; 120 // import jextracted python 'header' class 121 import static org.python.Python_h.*; 122 import org.python.*; 123 124 public class PythonMain { 125 public static void main(String[] args) { 126 String script = "print(sum([33, 55, 66])); print('Hello from Python!')\n"; 127 128 Py_Initialize(); 129 try (var scope = ResourceScope.newConfinedScope()) { 130 var allocator = SegmentAllocator.nativeAllocator(scope); 131 var str = allocator.allocateUtf8String(script); 132 PyRun_SimpleStringFlags(str, NULL); 133 Py_Finalize(); 134 } 135 } 136 } 137 138 ``` 139 140 ### Running the Java code that calls Python interpreter 141 142 ```sh 143 144 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ 145 -Djava.library.path=/System/Library/Frameworks/Python.framework/Versions/2.7/lib \ 146 PythonMain.java 147 148 ``` 149 150 ## Using readline library from Java code (Mac OS) 151 152 ### jextract readline.h 153 154 ```sh 155 156 jextract \ 157 -l readline -t org.unix \ 158 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 159 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/readline/readline.h 160 161 ``` 162 163 ### Java code that uses readline 164 165 ```java 166 167 import jdk.incubator.foreign.ResourceScope; 168 import jdk.incubator.foreign.SegmentAllocator; 169 import static org.unix.readline_h.*; 170 import org.unix.*; 171 172 public class Readline { 173 public static void main(String[] args) { 174 try (var scope = ResourceScope.newConfinedScope()) { 175 var allocator = SegmentAllocator.nativeAllocator(scope); 176 var url = allocator.allocateUtf8String("name? "); 177 178 // call "readline" API 179 var p = readline(url); 180 181 // print char* as is 182 System.out.println(p); 183 // convert char* ptr from readline as Java String & print it 184 System.out.println("Hello, " + p.getUtf8String(0)); 185 186 // pointer returned by readline has to be 'free'd 187 free(p); 188 } 189 } 190 } 191 192 ``` 193 194 ### Running the java code that uses readline 195 196 ``` 197 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ 198 -Djava.library.path=/usr/local/opt/readline/lib/ Readline.java 199 200 ``` 201 202 ## Using libcurl from Java (Mac OS) 203 204 ### jextract curl.h 205 206 ```sh 207 208 jextract -t org.unix -lcurl \ 209 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \ 210 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curl/ \ 211 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curl/curl.h 212 213 ``` 214 215 ### Java code that uses libcurl 216 217 ```java 218 219 import jdk.incubator.foreign.ResourceScope; 220 import jdk.incubator.foreign.SegmentAllocator; 221 import static jdk.incubator.foreign.MemoryAddress.NULL; 222 import static org.jextract.curl_h.*; 223 import org.jextract.*; 224 225 public class CurlMain { 226 public static void main(String[] args) { 227 var urlStr = args[0]; 228 curl_global_init(CURL_GLOBAL_DEFAULT()); 229 var curl = curl_easy_init(); 230 if(!curl.equals(NULL)) { 231 try (var scope = ResourceScope.newConfinedScope()) { 232 var allocator = SegmentAllocator.nativeAllocator(scope); 233 var url = allocator.allocateUtf8String(urlStr); 234 curl_easy_setopt(curl, CURLOPT_URL(), url.address()); 235 int res = curl_easy_perform(curl); 236 if (res != CURLE_OK()) { 237 String error = curl_easy_strerror(res).getUtf8String(0); 238 System.out.println("Curl error: " + error); 239 curl_easy_cleanup(curl); 240 } 241 } 242 } 243 curl_global_cleanup(); 244 } 245 } 246 247 ``` 248 249 ### Running the java code that uses libcurl 250 251 ```sh 252 253 # run this shell script by passing a URL as first argument 254 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ 255 -Djava.library.path=/usr/lib CurlMain.java $* 256 257 ``` 258 259 ## Using BLAS library 260 261 BLAS is a popular library that allows fast matrix and vector computation: [http://www.netlib.org/blas/](http://www.netlib.org/blas/). 262 263 ### Installing OpenBLAS (Mac OS) 264 265 On Mac, blas is available as part of the OpenBLAS library: [https://github.com/xianyi/OpenBLAS/wiki](https://github.com/xianyi/OpenBLAS/wiki) 266 267 OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version. 268 269 You can install openblas using HomeBrew 270 271 ```sh 272 273 brew install openblas 274 275 ``` 276 277 It installs include and lib directories under /usr/local/opt/openblas 278 279 ### jextracting cblas.h (MacOS) 280 281 The following command can be used to extract cblas.h on MacOs 282 283 ```sh 284 285 jextract -C "-D FORCE_OPENBLAS_COMPLEX_STRUCT" \ 286 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 287 -l openblas -t blas /usr/local/opt/openblas/include/cblas.h 288 289 ``` 290 291 ### Java sample code that uses cblas library 292 293 ```java 294 295 import jdk.incubator.foreign.MemoryAddress; 296 import jdk.incubator.foreign.SegmentAllocator; 297 import jdk.incubator.foreign.ResourceScope; 298 import blas.*; 299 import static blas.cblas_h.*; 300 301 public class TestBlas { 302 public static void main(String[] args) { 303 int Layout; 304 int transa; 305 306 double alpha, beta; 307 int m, n, lda, incx, incy, i; 308 309 Layout = CblasColMajor(); 310 transa = CblasNoTrans(); 311 312 m = 4; /* Size of Column ( the number of rows ) */ 313 n = 4; /* Size of Row ( the number of columns ) */ 314 lda = 4; /* Leading dimension of 5 * 4 matrix is 5 */ 315 incx = 1; 316 incy = 1; 317 alpha = 1; 318 beta = 0; 319 320 try (var scope = ResourceScope.newConfinedScope()) { 321 var allocator = SegmentAllocator.newNativeArena(scope); 322 var a = allocator.allocateArray(C_DOUBLE, new double[] { 323 1.0, 2.0, 3.0, 4.0, 324 1.0, 1.0, 1.0, 1.0, 325 3.0, 4.0, 5.0, 6.0, 326 5.0, 6.0, 7.0, 8.0 327 }); 328 var x = allocator.allocateArray(C_DOUBLE, new double[] { 329 1.0, 2.0, 1.0, 1.0 330 }); 331 var y = allocator.allocateArray(C_DOUBLE, n); 332 333 cblas_dgemv(Layout, transa, m, n, alpha, a, lda, x, incx, beta, y, incy); 334 /* Print y */ 335 for (i = 0; i < n; i++) { 336 System.out.print(String.format(" y%d = %f\n", i, y.getAtIndex(C_DOUBLE, i))); 337 } 338 } 339 } 340 } 341 342 ``` 343 344 ### Compiling and running the above BLAS sample 345 346 ```sh 347 348 jextract \ 349 -C "-D FORCE_OPENBLAS_COMPLEX_STRUCT" \ 350 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 351 -l openblas -t blas /usr/local/opt/openblas/include/cblas.h 352 353 ``` 354 355 ## Using LAPACK library (Mac OS) 356 357 On Mac OS, lapack is installed under /usr/local/opt/lapack directory. 358 359 ### jextracting lapacke.h 360 361 ```sh 362 363 jextract \ 364 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 365 -l lapacke -t lapack \ 366 /usr/local/opt/lapack/include/lapacke.h 367 368 ``` 369 370 ### Java sample code that uses LAPACK library 371 372 ```java 373 374 import jdk.incubator.foreign.MemorySegment; 375 import jdk.incubator.foreign.SegmentAllocator; 376 import jdk.incubator.foreign.ResourceScope; 377 import lapack.*; 378 import static lapack.lapacke_h.*; 379 380 public class TestLapack { 381 public static void main(String[] args) { 382 383 /* Locals */ 384 try (var scope = ResourceScope.newConfinedScope()) { 385 var allocator = SegmentAllocator.newNativeArena(scope); 386 var A = allocator.allocateArray(C_DOUBLE, new double[]{ 387 1, 2, 3, 4, 5, 1, 3, 5, 2, 4, 1, 4, 2, 5, 3 388 }); 389 var b = allocator.allocateArray(C_DOUBLE, new double[]{ 390 -10, 12, 14, 16, 18, -3, 14, 12, 16, 16 391 }); 392 int info, m, n, lda, ldb, nrhs; 393 394 /* Initialization */ 395 m = 5; 396 n = 3; 397 nrhs = 2; 398 lda = 5; 399 ldb = 5; 400 401 /* Print Entry Matrix */ 402 print_matrix_colmajor("Entry Matrix A", m, n, A, lda ); 403 /* Print Right Rand Side */ 404 print_matrix_colmajor("Right Hand Side b", n, nrhs, b, ldb ); 405 System.out.println(); 406 407 /* Executable statements */ 408 // printf( "LAPACKE_dgels (col-major, high-level) Example Program Results\n" ); 409 /* Solve least squares problem*/ 410 info = LAPACKE_dgels(LAPACK_COL_MAJOR(), (byte)'N', m, n, nrhs, A, lda, b, ldb); 411 412 /* Print Solution */ 413 print_matrix_colmajor("Solution", n, nrhs, b, ldb ); 414 System.out.println(); 415 System.exit(info); 416 } 417 } 418 419 static void print_matrix_colmajor(String msg, int m, int n, MemorySegment mat, int ldm) { 420 int i, j; 421 System.out.printf("\n %s\n", msg); 422 423 for( i = 0; i < m; i++ ) { 424 for( j = 0; j < n; j++ ) System.out.printf(" %6.2f", mat.getAtIndex(C_DOUBLE, i+j*ldm)); 425 System.out.printf( "\n" ); 426 } 427 } 428 } 429 430 ``` 431 432 ### Compiling and running the above LAPACK sample 433 434 ```sh 435 436 java --enable-native-access=ALL-UNNAMED \ 437 --add-modules jdk.incubator.foreign \ 438 -Djava.library.path=/usr/local/opt/lapack/lib \ 439 TestLapack.java 440 441 ``` 442 ## Using libproc library to list processes from Java (Mac OS) 443 444 ### jextract libproc.h 445 446 ```sh 447 448 jextract \ 449 -t org.unix \ 450 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 451 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libproc.h 452 453 ``` 454 455 ### Java program that uses libproc to list processes 456 457 ```java 458 459 import jdk.incubator.foreign.MemorySegment; 460 import jdk.incubator.foreign.ResourceScope; 461 import jdk.incubator.foreign.SegmentAllocator; 462 import org.unix.*; 463 import static jdk.incubator.foreign.MemoryAddress.NULL; 464 import static org.unix.libproc_h.*; 465 466 public class LibprocMain { 467 private static final int NAME_BUF_MAX = 256; 468 469 public static void main(String[] args) { 470 try (var scope = ResourceScope.newConfinedScope()) { 471 var allocator = SegmentAllocator.newNativeArena(scope); 472 // get the number of processes 473 int numPids = proc_listallpids(NULL, 0); 474 // allocate an array 475 var pids = allocator.allocateArray(C_INT, numPids); 476 // list all the pids into the native array 477 proc_listallpids(pids, numPids); 478 // convert native array to java array 479 int[] jpids = pids.toArray(C_INT); 480 // buffer for process name 481 var nameBuf = allocator.allocateArray(C_CHAR, NAME_BUF_MAX); 482 for (int i = 0; i < jpids.length; i++) { 483 int pid = jpids[i]; 484 // get the process name 485 proc_name(pid, nameBuf, NAME_BUF_MAX); 486 String procName = nameBuf.getUtf8String(0); 487 // print pid and process name 488 System.out.printf("%d %s\n", pid, procName); 489 } 490 } 491 } 492 } 493 494 ``` 495 496 ### Compiling and running the libproc sample 497 498 ```sh 499 500 java --enable-native-access=ALL-UNNAMED \ 501 --add-modules jdk.incubator.foreign \ 502 -Djava.library.path=/usr/lib LibprocMain.java 503 504 ``` 505 506 ## Using libgit2 from Java (Mac OS) 507 508 ### Getting and building libgit2 509 510 * Download libgit2 v1.0.0 source from https://github.com/libgit2/libgit2/releases 511 * Use cmake to build from libgit2 512 * Let ${LIBGIT2_HOME} be the directory where you expanded libgit2 sources. 513 * Let ${LIBGIT2_HOME}/build be the build directory where libgit2.dylib is built. 514 515 ### jextract git2.h 516 517 ```sh 518 519 jextract \ 520 -t com.github -lgit2 \ 521 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \ 522 -I ${LIBGIT2_HOME}/include/ \ 523 -I ${LIBGIT2_HOME}/include/git2 \ 524 ${LIBGIT2_HOME}/include/git2.h 525 526 ``` 527 528 ### Java program that uses libgit2 to clone github repo 529 530 ```java 531 532 import jdk.incubator.foreign.MemoryAddress; 533 import jdk.incubator.foreign.SegmentAllocator; 534 import jdk.incubator.foreign.ResourceScope; 535 import static com.github.git2_h.*; 536 import static jdk.incubator.foreign.MemoryAddress.NULL; 537 import com.github.*; 538 539 public class GitClone { 540 public static void main(String[] args) { 541 if (args.length != 2) { 542 System.err.println("java GitClone <url> <path>"); 543 System.exit(1); 544 } 545 git_libgit2_init(); 546 try (var scope = ResourceScope.newConfinedScope()) { 547 var allocator = SegmentAllocator.newNativeArena(scope); 548 var repo = allocator.allocate(C_POINTER); 549 var url = allocator.allocateUtf8String(args[0]); 550 var path = allocator.allocateUtf8String(args[1]); 551 System.out.println(git_clone(repo, url, path, NULL)); 552 } 553 git_libgit2_shutdown(); 554 } 555 } 556 557 ``` 558 559 ### Compiling and running the libgit2 sample 560 561 ```sh 562 563 # file run.sh 564 565 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ 566 -Djava.library.path=${LIBGIT2_HOME}/build/ \ 567 GitClone.java $* 568 ``` 569 570 ### Cloning a github repo using the above run.sh command 571 572 ```sh 573 574 sh run.sh https://github.com/libgit2/libgit2.git libgit2 575 576 ``` 577 578 ## Using sqlite3 library from Java (Mac OS) 579 580 581 ### jextract sqlite3.h 582 583 ```sh 584 585 jextract \ 586 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 587 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sqlite3.h \ 588 -t org.sqlite -lsqlite3 589 590 ``` 591 ### Java program that uses sqlite3 592 593 ```java 594 595 import jdk.incubator.foreign.MemoryAddress; 596 import jdk.incubator.foreign.MemorySegment; 597 import jdk.incubator.foreign.ResourceScope; 598 import jdk.incubator.foreign.SegmentAllocator; 599 import org.sqlite.*; 600 import static jdk.incubator.foreign.MemoryAddress.NULL; 601 import static org.sqlite.sqlite3_h.*; 602 603 public class SqliteMain { 604 public static void main(String[] args) throws Exception { 605 try (var scope = ResourceScope.newConfinedScope()) { 606 var allocator = SegmentAllocator.newNativeArena(scope); 607 // char** errMsgPtrPtr; 608 var errMsgPtrPtr = allocator.allocate(C_POINTER); 609 610 // sqlite3** dbPtrPtr; 611 var dbPtrPtr = allocator.allocate(C_POINTER); 612 613 int rc = sqlite3_open(allocator.allocateUtf8String("employee.db"), dbPtrPtr); 614 if (rc != 0) { 615 System.err.println("sqlite3_open failed: " + rc); 616 return; 617 } else { 618 System.out.println("employee db opened"); 619 } 620 621 // sqlite3* dbPtr; 622 var dbPtr = dbPtrPtr.get(C_POINTER, 0); 623 624 // create a new table 625 var sql = allocator.allocateUtf8String( 626 "CREATE TABLE EMPLOYEE (" + 627 " ID INT PRIMARY KEY NOT NULL," + 628 " NAME TEXT NOT NULL," + 629 " SALARY REAL NOT NULL )"); 630 631 rc = sqlite3_exec(dbPtr, sql, NULL, NULL, errMsgPtrPtr); 632 633 if (rc != 0) { 634 System.err.println("sqlite3_exec failed: " + rc); 635 System.err.println("SQL error: " + errMsgPtrPtr.get(C_POINTER, 0).getUtf8String(0)); 636 sqlite3_free(errMsgPtrPtr.get(C_POINTER, 0)); 637 } else { 638 System.out.println("employee table created"); 639 } 640 641 // insert two rows 642 sql = allocator.allocateUtf8String( 643 "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " + 644 "VALUES (134, 'Xyz', 200000.0); " + 645 "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " + 646 "VALUES (333, 'Abc', 100000.0);" 647 ); 648 rc = sqlite3_exec(dbPtr, sql, NULL, NULL, errMsgPtrPtr); 649 650 if (rc != 0) { 651 System.err.println("sqlite3_exec failed: " + rc); 652 System.err.println("SQL error: " + errMsgPtrPtr.get(C_POINTER, 0).getUtf8String(0)); 653 sqlite3_free(errMsgPtrPtr.get(C_POINTER, 0)); 654 } else { 655 System.out.println("rows inserted"); 656 } 657 658 int[] rowNum = new int[1]; 659 // callback to print rows from SELECT query 660 var callback = sqlite3_exec$callback.allocate((a, argc, argv, columnNames) -> { 661 System.out.println("Row num: " + rowNum[0]++); 662 System.out.println("numColumns = " + argc); 663 var argv_seg = MemorySegment.ofAddress(argv, C_POINTER.byteSize() * argc, scope); 664 var columnNames_seg = MemorySegment.ofAddress(columnNames, C_POINTER.byteSize() * argc, scope); 665 for (int i = 0; i < argc; i++) { 666 String name = columnNames_seg.getAtIndex(C_POINTER, i).getUtf8String(0); 667 String value = argv_seg.getAtIndex(C_POINTER, i).getUtf8String(0); 668 669 System.out.printf("%s = %s\n", name, value); 670 } 671 return 0; 672 }, scope); 673 674 // select query 675 sql = allocator.allocateUtf8String("SELECT * FROM EMPLOYEE"); 676 rc = sqlite3_exec(dbPtr, sql, callback, NULL, errMsgPtrPtr); 677 678 if (rc != 0) { 679 System.err.println("sqlite3_exec failed: " + rc); 680 System.err.println("SQL error: " + errMsgPtrPtr.get(C_POINTER, 0).getUtf8String(0)); 681 sqlite3_free(errMsgPtrPtr.get(C_POINTER, 0)); 682 } else { 683 System.out.println("done"); 684 } 685 686 sqlite3_close(dbPtr); 687 } 688 } 689 } 690 691 ``` 692 693 ### Compiling and running the sqlite3 sample 694 695 ```sh 696 697 java --enable-native-access=ALL-UNNAMED \ 698 --add-modules jdk.incubator.foreign \ 699 -Djava.library.path=/usr/lib SqliteMain.java 700 701 ``` 702 703 ## Using OpenGL library from Java (Mac OS) 704 705 ### jextract glut.h 706 707 ```sh 708 709 jextract -t opengl -lGL -l/System/Library/Frameworks/GLUT.framework/Versions/Current/GLUT \ 710 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \ 711 -C-F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks \ 712 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/GLUT.framework/Headers/glut.h 713 714 ``` 715 ### Java program that uses OpenGL 716 717 ```java 718 719 import jdk.incubator.foreign.ResourceScope; 720 import jdk.incubator.foreign.SegmentAllocator; 721 import opengl.*; 722 import static opengl.glut_h.*; 723 724 public class Teapot { 725 private float rot = 0; 726 727 Teapot(SegmentAllocator allocator) { 728 // Reset Background 729 glClearColor(0f, 0f, 0f, 0f); 730 // Setup Lighting 731 glShadeModel(GL_SMOOTH()); 732 var pos = allocator.allocateArray(C_FLOAT, new float[] {0.0f, 15.0f, -15.0f, 0}); 733 glLightfv(GL_LIGHT0(), GL_POSITION(), pos); 734 var spec = allocator.allocateArray(C_FLOAT, new float[] {1, 1, 1, 0}); 735 glLightfv(GL_LIGHT0(), GL_AMBIENT(), spec); 736 glLightfv(GL_LIGHT0(), GL_DIFFUSE(), spec); 737 glLightfv(GL_LIGHT0(), GL_SPECULAR(), spec); 738 var shini = allocator.allocate(C_FLOAT, 113); 739 glMaterialfv(GL_FRONT(), GL_SHININESS(), shini); 740 glEnable(GL_LIGHTING()); 741 glEnable(GL_LIGHT0()); 742 glEnable(GL_DEPTH_TEST()); 743 } 744 745 void display() { 746 glClear(GL_COLOR_BUFFER_BIT() | GL_DEPTH_BUFFER_BIT()); 747 glPushMatrix(); 748 glRotatef(-20f, 1f, 1f, 0f); 749 glRotatef(rot, 0f, 1f, 0f); 750 glutSolidTeapot(0.5d); 751 glPopMatrix(); 752 glutSwapBuffers(); 753 } 754 755 void onIdle() { 756 rot += 0.1; 757 glutPostRedisplay(); 758 } 759 760 public static void main(String[] args) { 761 try (var scope = ResourceScope.newConfinedScope()) { 762 var allocator = SegmentAllocator.newNativeArena(scope); 763 var argc = allocator.allocate(C_INT, 0); 764 glutInit(argc, argc); 765 glutInitDisplayMode(GLUT_DOUBLE() | GLUT_RGB() | GLUT_DEPTH()); 766 glutInitWindowSize(500, 500); 767 glutCreateWindow(allocator.allocateUtf8String("Hello Panama!")); 768 var teapot = new Teapot(allocator); 769 var displayStub = glutDisplayFunc$func.allocate(teapot::display, scope); 770 var idleStub = glutIdleFunc$func.allocate(teapot::onIdle, scope); 771 glutDisplayFunc(displayStub); 772 glutIdleFunc(idleStub); 773 glutMainLoop(); 774 } 775 } 776 } 777 778 ``` 779 780 ### Compiling and running the OpenGL sample 781 782 ```sh 783 784 java -XstartOnFirstThread --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ 785 -Djava.library.path=.:/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/ Teapot.java $* 786 787 ``` 788 789 ## Using tensorflow (Mac OS) 790 791 ### getting libtensorflow 792 793 * Download tensorflow library from 794 795 https://www.tensorflow.org/install/lang_c 796 797 * extract the downloaded tar in a directory called LIBTENSORFLOW_HOME 798 799 ### jextract c_api.h 800 801 ```sh 802 803 jextract --source \ 804 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \ 805 -t org.tensorflow \ 806 -I ${LIBTENSORFLOW_HOME}/include \ 807 -l ${LIBTENSORFLOW_HOME}/lib/libtensorflow.dylib \ 808 ${LIBTENSORFLOW_HOME}/include/tensorflow/c/c_api.h 809 810 javac --add-modules jdk.incubator.foreign org/tensorflow/*.java 811 812 ``` 813 814 ### Python program that creates and saves model 815 816 The following Python program should be run to create and save model 817 which will read and printed by a Java program. 818 819 Note: you need to install tensorflow package to run this python script. 820 821 ```python 822 823 import tensorflow as tf 824 from tensorflow.keras import models, layers 825 from tensorflow.keras.datasets import mnist 826 827 model = tf.keras.models.Sequential([ 828 tf.keras.layers.Flatten(input_shape=(28, 28)), 829 tf.keras.layers.Dense(128,activation='relu'), 830 tf.keras.layers.Dense(10, activation='softmax') 831 ]) 832 833 model.compile( 834 loss='sparse_categorical_crossentropy', 835 optimizer=tf.keras.optimizers.Adam(0.001), 836 metrics=['accuracy'], 837 ) 838 839 print(model.summary()) 840 841 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() 842 843 train_images = train_images/255.0 844 test_images = test_images/255.0 845 846 model.fit(train_images, train_labels, 847 epochs=4, batch_size=128, verbose=1) 848 849 test_loss, test_accuracy = model.evaluate(test_images, test_labels) 850 851 print(test_loss, test_accuracy) 852 853 model.save("saved_mnist_model") 854 855 ``` 856 857 ### Java program that uses Tensorflow C API 858 859 ```java 860 861 import jdk.incubator.foreign.*; 862 import static jdk.incubator.foreign.MemoryAddress.*; 863 import static org.tensorflow.c_api_h.*; 864 import org.tensorflow.*; 865 866 // simple program that loads saved model and prints basic info on operations in it 867 868 public class TensorflowLoadSavedModel { 869 public static void main(String... args) throws Exception { 870 System.out.println("TensorFlow C library version: " + TF_Version().getUtf8String(0)); 871 872 if (args.length == 0) { 873 System.err.println("java TensorflowLoadSavedModel <saved model dir>"); 874 System.exit(1); 875 } 876 877 try (var scope = ResourceScope.newConfinedScope()) { 878 var allocator = SegmentAllocator.newNativeArena(scope); 879 var graph = TF_NewGraph(); 880 var status = TF_NewStatus(); 881 var sessionOpts = TF_NewSessionOptions(); 882 883 var savedModelDir = allocator.allocateUtf8String(args[0]); 884 var tags = allocator.allocate(C_POINTER, allocator.allocateUtf8String("serve")); 885 var session = TF_LoadSessionFromSavedModel(sessionOpts, NULL, savedModelDir, tags, 1, graph, NULL, status); 886 887 if (TF_GetCode(status) != TF_OK()) { 888 System.err.printf("cannot load session from saved model: %s\n", 889 TF_Message(status).getUtf8String(0)); 890 } else { 891 System.err.println("load session from saved model works!"); 892 } 893 894 // print operations 895 var size = allocator.allocate(C_LONG_LONG); 896 var operation = NULL; 897 while (!(operation = TF_GraphNextOperation(graph, size)).equals(NULL)) { 898 System.out.printf("%s : %s\n", 899 TF_OperationName(operation).getUtf8String(0), 900 TF_OperationOpType(operation).getUtf8String(0)); 901 } 902 903 TF_DeleteGraph(graph); 904 TF_DeleteSession(session, status); 905 TF_DeleteSessionOptions(sessionOpts); 906 TF_DeleteStatus(status); 907 } 908 } 909 } 910 911 ``` 912 913 ### Compiling and running the Java Tensorflow sample 914 915 ```sh 916 917 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ 918 TensorflowLoadSavedModel.java saved_mnist_model 919 920 ``` 921 922 ## Using time.h (Mac OS) 923 924 ### jextract time.h 925 926 927 ```sh 928 929 jextract -t org.unix \ 930 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \ 931 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h 932 933 ``` 934 935 ### Java program that uses POSIX time library 936 937 ```java 938 939 import static org.unix.time_h.*; 940 import static jdk.incubator.foreign.CLinker.*; 941 import jdk.incubator.foreign.*; 942 import org.unix.*; 943 944 public class PanamaTime { 945 public static void main(String[] args) { 946 try (var scope = ResourceScope.newConfinedScope()) { 947 var allocator = SegmentAllocator.newNativeArena(scope); 948 var now = allocator.allocate(C_LONG, System.currentTimeMillis() / 1000); 949 MemorySegment time = tm.allocate(scope); 950 localtime_r(now, time); 951 System.err.printf("Time = %d:%d\n", tm.tm_hour$get(time), tm.tm_min$get(time)); 952 } 953 } 954 } 955 956 ``` 957 958 ### Compiling and running the time sample 959 960 961 ```sh 962 963 java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign PanamaTime.java 964 965 ``` 966 967 ## Using libclang library (Mac OS) 968 969 ### jextract Index.h 970 971 ```sh 972 973 # LIBCLANG_HOME is the directory where you've installed llvm 9.x or above 974 975 jextract --source -t org.llvm.clang -lclang \ 976 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \ 977 -I ${LIBCLANG_HOME}/include/ \ 978 -I ${LIBCLANG_HOME}/include/clang-c \ 979 ${LIBCLANG_HOME}/include/clang-c/Index.h 980 javac --add-modules jdk.incubator.foreign org/llvm/clang/*.java 981 982 ``` 983 984 ### Java program that uses libclang to print AST of a given C program 985 986 ```java 987 988 import jdk.incubator.foreign.*; 989 import static jdk.incubator.foreign.MemoryAddress.NULL; 990 import static org.llvm.clang.Index_h.*; 991 import org.llvm.clang.*; 992 993 public class ASTPrinter { 994 private static String asJavaString(MemorySegment clangStr) { 995 String str = clang_getCString(clangStr).getUtf8String(0); 996 clang_disposeString(clangStr); 997 return str; 998 } 999 1000 public static void main(String[] args) { 1001 if (args.length == 0) { 1002 System.err.println("java ASTPrinter <C source or header>"); 1003 System.exit(1); 1004 } 1005 1006 try (var scope = ResourceScope.newConfinedScope()) { 1007 // parse the C header/source passed from the command line 1008 var index = clang_createIndex(0, 0); 1009 var allocator = SegmentAllocator.newNativeArena(scope); 1010 var tu = clang_parseTranslationUnit(index, allocator.allocateUtf8String(args[0]), 1011 NULL, 0, NULL, 0, CXTranslationUnit_None()); 1012 // array trick to update within lambda 1013 var level = new int[1]; 1014 var visitor = new NativeSymbol[1]; 1015 1016 // clang Cursor visitor callback 1017 visitor[0] = CXCursorVisitor.allocate((cursor, parent, data) -> { 1018 var kind = clang_getCursorKind(cursor); 1019 var name = asJavaString(clang_getCursorSpelling(scope, cursor)); 1020 var kindName = asJavaString(clang_getCursorKindSpelling(scope, kind)); 1021 System.out.printf("%s %s %s", " ".repeat(level[0]), kindName, name); 1022 var type = clang_getCursorType(scope, cursor); 1023 if (CXType.kind$get(type) != CXType_Invalid()) { 1024 var typeName = asJavaString(clang_getTypeSpelling(scope, type)); 1025 System.out.printf(" <%s>", typeName); 1026 } 1027 System.out.println(); 1028 1029 // visit children 1030 level[0]++; 1031 clang_visitChildren(cursor, visitor[0], NULL); 1032 level[0]--; 1033 1034 return CXChildVisit_Continue(); 1035 }, scope); 1036 1037 // get the AST root and visit it 1038 var root = clang_getTranslationUnitCursor(scope, tu); 1039 clang_visitChildren(root, visitor[0], NULL); 1040 1041 clang_disposeTranslationUnit(tu); 1042 clang_disposeIndex(index); 1043 } 1044 } 1045 } 1046 1047 ``` 1048 1049 ### Compiling and running the libclang sample 1050 1051 ```sh 1052 1053 java --enable-native-access=ALL-UNNAMED \ 1054 -Djava.library.path=${LIBCLANG_HOME}/lib \ 1055 --add-modules jdk.incubator.foreign \ 1056 ASTPrinter.java $* 1057 1058 ```