Files
javademos/jextract/scripts/run-all.sh
T
asmhatreandClaude Sonnet 5.5 ecceaa8b32 Add jextract module: SQLite bindings generated with jextract, FFM arenas, C strings, out-parameters, errors, upcalls, and a JMH comparison with JNI
jextract-25 early-access build (25-jextract+2-4) on JDK 25.0.5+8 against SQLite 3.45.1: 25 functions and 13 constants
generated into 4 committed files (the whole header would be 83 files), a small Db wrapper, the four arena kinds
used wrongly on purpose, ADDRESS vs C_POINTER zero-length vs unbounded segments and the SIGSEGV from getString
on a NULL pointer, sqlite3_open out-parameter, return codes to exceptions, a sqlite3_exec upcall and the VM exit
when it throws, the JDK 25 native-access warning and --illegal-native-access=deny, library loading failures,
and a JMH benchmark of FFM vs hand-written JNI vs plain Java (indicative only: shared 2-CPU machine).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b6Kg9RWSTJzyz6KP9X
2026-10-30 16:36:09 +00:00

82 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Regenerates every file in output/ except 10-jmh.txt (that one has its own script, about 4 minutes).
# JAVA_HOME=/path/to/jdk-25 JEXTRACT=/path/to/jextract ./scripts/run-all.sh
set -uo pipefail
unset JAVA_TOOL_OPTIONS # the JVM prints a "Picked up JAVA_TOOL_OPTIONS" banner in some sandboxes; it is noise in a transcript
HERE="$(cd "$(dirname "$0")/.." && pwd)"; cd "$HERE"
JAVA_HOME="${JAVA_HOME:?set JAVA_HOME to a JDK 25}"; export PATH="$JAVA_HOME/bin:$PATH"
JEXTRACT="${JEXTRACT:-$HERE/tools/jextract-25/bin/jextract}"
SQLITE_H="${SQLITE_H:-/usr/include/sqlite3.h}"
BUILD="${BUILD:-$HERE/build}"; OUT="$HERE/output"; mkdir -p "$OUT" "$BUILD/classes"
NA="--enable-native-access=ALL-UNNAMED"
run() { echo "\$ java $NA -cp build/classes demo.$1${2:+ $2}"; java $NA -cp "$BUILD/classes" demo."$1" ${2:-} 2>&1; }
# 01 - what was installed
{
echo "\$ jextract --version"; "$JEXTRACT" --version 2>&1
echo "\$ java -version"; java -version 2>&1
echo "\$ gcc --version | head -1"; gcc --version | head -1
echo "\$ dpkg-query -W libsqlite3-dev libsqlite3-0"; dpkg-query -W libsqlite3-dev libsqlite3-0
echo "\$ grep -E '#define SQLITE_(VERSION|VERSION_NUMBER) ' $SQLITE_H"; grep -E '#define SQLITE_(VERSION|VERSION_NUMBER) ' "$SQLITE_H"
} > "$OUT/01-tools.txt"
# 02 - generate bindings: the trimmed set that is committed, and the whole header for size comparison
{
echo "\$ scripts/gen.sh"
JEXTRACT="$JEXTRACT" SQLITE_H="$SQLITE_H" scripts/gen.sh 2>&1
echo "\$ find generated -name '*.java' | wc -l"; find generated -name '*.java' | wc -l
echo "\$ wc -l \$(find generated -name '*.java') | sort -n"; wc -l $(find generated -name '*.java') | sort -n
F="$(mktemp -d)"
echo "\$ jextract --output <tmp> -t demo.sqlite.full -l sqlite3 $SQLITE_H # no filters: the whole header"
"$JEXTRACT" --output "$F" -t demo.sqlite.full -l sqlite3 "$SQLITE_H" > "$F.log" 2>&1
echo "warnings: $(grep -c warning "$F.log")"; grep 'Skipping sqlite3 ' "$F.log"; grep 'Skipping sqlite3_stmt ' "$F.log"
echo "files: $(find "$F" -name '*.java' | wc -l)"
echo "lines: $(cat $(find "$F" -name '*.java') | wc -l)"
echo "bytes: $(cat $(find "$F" -name '*.java') | wc -c)"
echo "lines in the one big header class: $(wc -l < "$F/demo/sqlite/full/sqlite3_h.java")"
rm -rf "$F" "$F.log"
echo "\$ grep -o 'public static [A-Za-z.]* sqlite3_open[a-z_0-9\$]*(' generated/demo/sqlite/sqlite3_h.java | sort -u"
grep -o 'public static [A-Za-z.]* sqlite3_open[a-z_0-9$]*(' generated/demo/sqlite/sqlite3_h.java | sort -u
} > "$OUT/02-generate.txt"
# compile generated + hand-written sources
rm -rf "$BUILD/classes"; mkdir -p "$BUILD/classes"
javac -d "$BUILD/classes" $(find generated src -name '*.java') || exit 1
{ run Hello; echo; echo "\$ java $NA -Djextract.trace.downcalls=true -cp build/classes demo.Hello"; java $NA -Djextract.trace.downcalls=true -cp "$BUILD/classes" demo.Hello 2>&1; } > "$OUT/03-hello.txt"
{
echo "\$ java -cp build/classes demo.Hello # no flag at all, JDK 25 default"
java -cp "$BUILD/classes" demo.Hello 2>&1
echo; echo "\$ java --illegal-native-access=deny -cp build/classes demo.Hello"
java --illegal-native-access=deny -cp "$BUILD/classes" demo.Hello 2>&1 | head -6
} > "$OUT/04-native-access.txt"
run Tour > "$OUT/05-tour.txt"
run Arenas > "$OUT/06-arenas.txt"
{
run Trap; echo
echo "\$ java $NA -XX:ErrorFile=/tmp/jextract-hs_err.log -cp build/classes demo.NullText # first lines of the crash banner"
java $NA -XX:ErrorFile=/tmp/jextract-hs_err.log -cp "$BUILD/classes" demo.NullText 2>&1 | sed -n '1,9p'
echo "exit code: ${PIPESTATUS[0]}"
} > "$OUT/07-pointer-traps.txt"
run Errors > "$OUT/08-errors.txt"
{ run Callbacks; echo; echo "\$ java $NA -cp build/classes demo.Callbacks explode"; java $NA -cp "$BUILD/classes" demo.Callbacks explode 2>&1; echo "exit code: $?"; } > "$OUT/09-callbacks.txt"
# 11 - how the -l option decides what gets loaded
{
T="$(mktemp -d)"
n=0
for lib in sqlitez :libsqlite3.so.0; do
n=$((n+1))
echo "\$ jextract -t demo.sqlite -l $lib --include-function sqlite3_libversion --header-class-name sqlite3_h $SQLITE_H"
"$JEXTRACT" --output "$T/gen$n" -t demo.sqlite -l "$lib" --include-function sqlite3_libversion --header-class-name sqlite3_h "$SQLITE_H" 2>&1
grep -h 'libraryLookup' "$T/gen$n/demo/sqlite/sqlite3_h.java" | sed 's/^ *//'
mkdir -p "$T/c$n"; javac -d "$T/c$n" "$T/gen$n"/demo/sqlite/*.java src/extra/V.java
echo "\$ java $NA -cp <classes> V"
java $NA -cp "$T/c$n" V 2>&1 | head -5
echo
done
rm -rf "$T"
} > "$OUT/11-library-loading.txt"
echo done